source: inundation/ga/storm_surge/pyvolution/advection.py @ 1697

Last change on this file since 1697 was 1639, checked in by steve, 20 years ago
File size: 11.1 KB
RevLine 
[1363]1import sys
2from os import sep
3sys.path.append('..'+sep+'pyvolution')
4
[195]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
[773]16There is only one conserved quantity, the stage u
[195]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
[1363]23Geoscience Australia, 2004
[195]24"""
25
[1556]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
[195]37from domain import *
38Generic_domain = Domain #Rename
39
40class Domain(Generic_domain):
41
[1556]42    def __init__(self, coordinates, vertices, boundary = None,
43                 tagged_elements = None, geo_reference = None,
44                 use_inscribed_circle=False, velocity = None):
[195]45
[1556]46        conserved_quantities = ['stage']
47        other_quantities = []
[195]48        Generic_domain.__init__(self, coordinates, vertices, boundary,
[1556]49                                conserved_quantities, other_quantities,
50                                tagged_elements, geo_reference,
51                                use_inscribed_circle)
[195]52
[1556]53
[195]54        if velocity is None:
55            self.velocity = [1,0]
56        else:
57            self.velocity = velocity
58
[1363]59        #Only first is implemented for advection
60        self.default_order = self.order = 1
[195]61
[271]62        #Realtime visualisation
[1556]63        self.visualiser = None
64        self.visualise  = False
[1363]65        self.visualise_color_stage = False
66        self.visualise_timer = True
67        self.visualise_range_z = None
[1556]68
[271]69        self.smooth = True
[195]70
[1556]71    def initialise_visualiser(self,scale_z=1.0,rect=None):
[1575]72        #Realtime visualisation
[1556]73        if self.visualiser is None:
[1564]74            from realtime_visualisation_new import Visualiser
[1556]75            self.visualiser = Visualiser(self,scale_z,rect)
76        self.visualise = True
[271]77
[1363]78
[195]79    def check_integrity(self):
80        Generic_domain.check_integrity(self)
81
[773]82        msg = 'Conserved quantity must be "stage"'
83        assert self.conserved_quantities[0] == 'stage', msg
[195]84
[1363]85
[195]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.
[1363]89
[195]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        """
[1363]95
[195]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
[1363]106
[195]107        max_speed = abs(normal_velocity)
[1363]108        return flux, max_speed
[195]109
[1556]110    def compute_fluxes(self):
[1363]111
[1575]112        try:
113            import weave
114            self.weave_available = True
115        except:
116            self.weave_available = False
[1556]117
[1575]118        if self.weave_available:
119            self.compute_fluxes_weave()
120        else:
121            self.compute_fluxes_python()
122
123
124
[1556]125    def compute_fluxes_python(self):
[195]126        """Compute all fluxes and the timestep suitable for all volumes
127        in domain.
[1363]128
[195]129        Compute total flux for each conserved quantity using "flux_function"
[1363]130
[195]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
[1363]142        domain.timestep is set to the largest step satisfying all volumes.
[195]143        """
144
145        import sys
146        from Numeric import zeros, Float
147        from config import max_timestep
148
149        N = self.number_of_elements
[1363]150
[195]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
[1363]158
[195]159        timestep = max_timestep #FIXME: Get rid of this
160
161        #Shortcuts
[773]162        Stage = self.quantities['stage']
[195]163
164        #Arrays
[773]165        stage = Stage.edge_values
[195]166
[773]167        stage_bdry = Stage.boundary_values
[1363]168
[195]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
[773]178                ql = stage[k, i]
[195]179
180                #Quantities at neighbour on nearest face
[1363]181                n = neighbours[k,i]
[195]182                if n < 0:
183                    m = -n-1 #Convert neg flag to index
[773]184                    qr = stage_bdry[m]
[1363]185                else:
[195]186                    m = neighbour_edges[k,i]
[773]187                    qr = stage[n, m]
[195]188
[1363]189
190                #Outward pointing normal vector
[195]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]
[1363]196
[195]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]
[773]206            Stage.explicit_update[k] = flux[0]
[1363]207
[195]208            timestep = min(timestep, optimal_timestep)
209
[1363]210        self.timestep = timestep
[195]211
[1556]212    def compute_fluxes_weave(self):
213        """Compute all fluxes and the timestep suitable for all volumes
214        in domain.
[271]215
[1556]216        Compute total flux for each conserved quantity using "flux_function"
[1363]217
[1556]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
[1639]328        logger.debug('Trying to weave advection.compute_fluxes')
[1556]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
[271]338    def evolve(self, yieldstep = None, finaltime = None):
339        """Specialisation of basic evolve method from parent class
340        """
[1363]341
[271]342        #Initialise real time viz if requested
343        if self.visualise is True and self.time == 0.0:
[1556]344            #import realtime_visualisation_new as visualise
345            self.initialise_visualiser()
[1639]346            #self.visualiser.update_quantity('stage')
[1556]347            self.visualiser.update_timer()
[271]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:
[1639]353                #self.visualiser.update_quantity('stage')
[1556]354                self.visualiser.update_timer()
[271]355
[1363]356            #Pass control on to outer loop for more specific actions
[271]357            yield(t)
Note: See TracBrowser for help on using the repository browser.