source: inundation/pyvolution/advection.py @ 1812

Last change on this file since 1812 was 1639, checked in by steve, 19 years ago
File size: 11.1 KB
Line 
1import sys
2from os import sep
3sys.path.append('..'+sep+'pyvolution')
4
5"""Class Domain -
62D triangular domains for finite-volume computations of
7the advection equation.
8
9This module contains a specialisation of class Domain from module domain.py
10consisting of methods specific to the advection equantion
11
12The equation is
13
14  u_t + (v_1 u)_x + (v_2 u)_y = 0
15
16There is only one conserved quantity, the stage u
17
18The advection equation is a very simple specialisation of the generic
19domain and may serve as an instructive example or a test of other
20components such as visualisation.
21
22Ole Nielsen, Stephen Roberts, Duncan Gray, Christopher Zoppou
23Geoscience Australia, 2004
24"""
25
26
27import logging, logging.config
28logger = logging.getLogger('advection')
29logger.setLevel(logging.WARNING)
30
31try:
32    logging.config.fileConfig('log.ini')
33except:
34    pass
35
36
37from domain import *
38Generic_domain = Domain #Rename
39
40class Domain(Generic_domain):
41
42    def __init__(self, coordinates, vertices, boundary = None,
43                 tagged_elements = None, geo_reference = None,
44                 use_inscribed_circle=False, velocity = None):
45
46        conserved_quantities = ['stage']
47        other_quantities = []
48        Generic_domain.__init__(self, coordinates, vertices, boundary,
49                                conserved_quantities, other_quantities,
50                                tagged_elements, geo_reference,
51                                use_inscribed_circle)
52
53
54        if velocity is None:
55            self.velocity = [1,0]
56        else:
57            self.velocity = velocity
58
59        #Only first is implemented for advection
60        self.default_order = self.order = 1
61
62        #Realtime visualisation
63        self.visualiser = None
64        self.visualise  = False
65        self.visualise_color_stage = False
66        self.visualise_timer = True
67        self.visualise_range_z = None
68
69        self.smooth = True
70
71    def initialise_visualiser(self,scale_z=1.0,rect=None):
72        #Realtime visualisation
73        if self.visualiser is None:
74            from realtime_visualisation_new import Visualiser
75            self.visualiser = Visualiser(self,scale_z,rect)
76        self.visualise = True
77
78
79    def check_integrity(self):
80        Generic_domain.check_integrity(self)
81
82        msg = 'Conserved quantity must be "stage"'
83        assert self.conserved_quantities[0] == 'stage', msg
84
85
86    def flux_function(self, normal, ql, qr, zl=None, zr=None):
87        """Compute outward flux as inner product between velocity
88        vector v=(v_1, v_2) and normal vector n.
89
90        if <n,v> > 0 flux direction is outward bound and its magnitude is
91        determined by the quantity inside volume: ql.
92        Otherwise it is inbound and magnitude is determined by the
93        quantity outside the volume: qr.
94        """
95
96        v1 = self.velocity[0]
97        v2 = self.velocity[1]
98
99
100        normal_velocity = v1*normal[0] + v2*normal[1]
101
102        if normal_velocity < 0:
103            flux = qr * normal_velocity
104        else:
105            flux = ql * normal_velocity
106
107        max_speed = abs(normal_velocity)
108        return flux, max_speed
109
110    def compute_fluxes(self):
111
112        try:
113            import weave
114            self.weave_available = True
115        except:
116            self.weave_available = False
117
118        if self.weave_available:
119            self.compute_fluxes_weave()
120        else:
121            self.compute_fluxes_python()
122
123
124
125    def compute_fluxes_python(self):
126        """Compute all fluxes and the timestep suitable for all volumes
127        in domain.
128
129        Compute total flux for each conserved quantity using "flux_function"
130
131        Fluxes across each edge are scaled by edgelengths and summed up
132        Resulting flux is then scaled by area and stored in
133        domain.explicit_update
134
135        The maximal allowable speed computed by the flux_function
136        for each volume
137        is converted to a timestep that must not be exceeded. The minimum of
138        those is computed as the next overall timestep.
139
140        Post conditions:
141        domain.explicit_update is reset to computed flux values
142        domain.timestep is set to the largest step satisfying all volumes.
143        """
144
145        import sys
146        from Numeric import zeros, Float
147        from config import max_timestep
148
149        N = self.number_of_elements
150
151        neighbours = self.neighbours
152        neighbour_edges = self.neighbour_edges
153        normals = self.normals
154
155        areas = self.areas
156        radii = self.radii
157        edgelengths = self.edgelengths
158
159        timestep = max_timestep #FIXME: Get rid of this
160
161        #Shortcuts
162        Stage = self.quantities['stage']
163
164        #Arrays
165        stage = Stage.edge_values
166
167        stage_bdry = Stage.boundary_values
168
169        flux = zeros(1, Float) #Work array for summing up fluxes
170
171        #Loop
172        for k in range(N):
173            optimal_timestep = float(sys.maxint)
174
175            flux[:] = 0.  #Reset work array
176            for i in range(3):
177                #Quantities inside volume facing neighbour i
178                ql = stage[k, i]
179
180                #Quantities at neighbour on nearest face
181                n = neighbours[k,i]
182                if n < 0:
183                    m = -n-1 #Convert neg flag to index
184                    qr = stage_bdry[m]
185                else:
186                    m = neighbour_edges[k,i]
187                    qr = stage[n, m]
188
189
190                #Outward pointing normal vector
191                normal = normals[k, 2*i:2*i+2]
192
193                #Flux computation using provided function
194                edgeflux, max_speed = self.flux_function(normal, ql, qr)
195                flux -= edgeflux * edgelengths[k,i]
196
197                #Update optimal_timestep
198                try:
199                    optimal_timestep = min(optimal_timestep, radii[k]/max_speed)
200                except ZeroDivisionError:
201                    pass
202
203            #Normalise by area and store for when all conserved
204            #quantities get updated
205            flux /= areas[k]
206            Stage.explicit_update[k] = flux[0]
207
208            timestep = min(timestep, optimal_timestep)
209
210        self.timestep = timestep
211
212    def compute_fluxes_weave(self):
213        """Compute all fluxes and the timestep suitable for all volumes
214        in domain.
215
216        Compute total flux for each conserved quantity using "flux_function"
217
218        Fluxes across each edge are scaled by edgelengths and summed up
219        Resulting flux is then scaled by area and stored in
220        domain.explicit_update
221
222        The maximal allowable speed computed by the flux_function
223        for each volume
224        is converted to a timestep that must not be exceeded. The minimum of
225        those is computed as the next overall timestep.
226
227        Post conditions:
228        domain.explicit_update is reset to computed flux values
229        domain.timestep is set to the largest step satisfying all volumes.
230        """
231
232        import sys
233        from Numeric import zeros, Float
234        from config import max_timestep
235
236        import weave
237        from weave import converters
238
239        N = self.number_of_elements
240
241
242        timestep    = zeros( 1, Float);
243        timestep[0] = float(max_timestep) #FIXME: Get rid of this
244
245        #Shortcuts
246        Stage = self.quantities['stage']
247
248        #Arrays
249        neighbours      = self.neighbours
250        neighbour_edges = self.neighbour_edges
251        normals         = self.normals
252        areas           = self.areas
253        radii           = self.radii
254        edgelengths     = self.edgelengths
255
256        stage_edge      = Stage.edge_values
257        stage_bdry      = Stage.boundary_values
258        stage_update    = Stage.explicit_update
259
260        huge_timestep = float(sys.maxint)
261
262        v1 = self.velocity[0]
263        v2 = self.velocity[1]
264
265        code = """
266        //Loop
267
268        double qr,ql;
269        int m,n;
270        double normal[2];
271        double normal_velocity;
272        double flux, edgeflux;
273        double max_speed;
274        double optimal_timestep;
275        for (int k=0; k<N; k++){
276
277            optimal_timestep = huge_timestep;
278            flux = 0.0;  //Reset work array
279            for (int i=0; i<3; i++){
280                //Quantities inside volume facing neighbour i
281                ql = stage_edge(k, i);
282
283                //Quantities at neighbour on nearest face
284                n = neighbours(k,i);
285                if (n < 0) {
286                    m = -n-1; //Convert neg flag to index
287                    qr = stage_bdry(m);
288                } else {
289                    m = neighbour_edges(k,i);
290                    qr = stage_edge(n, m);
291                }
292
293
294                //Outward pointing normal vector
295                for (int j=0; j<2; j++){
296                    normal[j] = normals(k, 2*i+j);
297                }
298
299
300                //Flux computation using provided function
301                normal_velocity = v1*normal[0] + v2*normal[1];
302
303                if (normal_velocity < 0) {
304                    edgeflux = qr * normal_velocity;
305                } else {
306                    edgeflux = ql * normal_velocity;
307                }
308
309                max_speed = fabs(normal_velocity);
310                flux = flux - edgeflux * edgelengths(k,i);
311
312                //Update optimal_timestep
313                if (max_speed != 0.0) {
314                    optimal_timestep = (optimal_timestep>radii(k)/max_speed) ? radii(k)/max_speed : optimal_timestep;
315                }
316
317            }
318
319            //Normalise by area and store for when all conserved
320            //quantities get updated
321            stage_update(k) = flux/areas(k);
322
323            timestep(0) = (timestep(0)>optimal_timestep) ? optimal_timestep : timestep(0);
324
325        }
326        """
327
328        logger.debug('Trying to weave advection.compute_fluxes')
329        weave.inline(code, ['stage_edge','stage_bdry','stage_update',
330                             'neighbours','neighbour_edges','normals',
331                             'areas','radii','edgelengths','huge_timestep',
332                             'timestep','v1','v2','N'],
333                             type_converters = converters.blitz, compiler='gcc');
334
335        self.timestep = timestep[0]
336
337
338    def evolve(self, yieldstep = None, finaltime = None):
339        """Specialisation of basic evolve method from parent class
340        """
341
342        #Initialise real time viz if requested
343        if self.visualise is True and self.time == 0.0:
344            #import realtime_visualisation_new as visualise
345            self.initialise_visualiser()
346            #self.visualiser.update_quantity('stage')
347            self.visualiser.update_timer()
348
349        #Call basic machinery from parent class
350        for t in Generic_domain.evolve(self, yieldstep, finaltime):
351            #Real time viz
352            if self.visualise is True:
353                #self.visualiser.update_quantity('stage')
354                self.visualiser.update_timer()
355
356            #Pass control on to outer loop for more specific actions
357            yield(t)
Note: See TracBrowser for help on using the repository browser.