source: inundation/ga/storm_surge/pyvolution/quantity.py @ 1266

Last change on this file since 1266 was 1011, checked in by ole, 20 years ago

Added test for conservation of stage with various beds

File size: 29.4 KB
Line 
1"""Class Quantity - Implements values at each triangular element
2
3To create:
4
5   Quantity(domain, vertex_values)
6
7   domain: Associated domain structure. Required.
8   
9   vertex_values: N x 3 array of values at each vertex for each element.
10                  Default None
11
12   If vertex_values are None Create array of zeros compatible with domain.
13   Otherwise check that it is compatible with dimenions of domain.
14   Otherwise raise an exception
15"""
16
17
18class Quantity:
19
20    def __init__(self, domain, vertex_values=None):
21
22        from mesh import Mesh
23        from Numeric import array, zeros, Float
24
25        msg = 'First argument in Quantity.__init__ '
26        msg += 'must be of class Mesh (or a subclass thereof)'
27        assert isinstance(domain, Mesh), msg
28
29        if vertex_values is None:
30            N = domain.number_of_elements           
31            self.vertex_values = zeros((N, 3), Float)
32        else:   
33            self.vertex_values = array(vertex_values).astype(Float)
34
35            N, V = self.vertex_values.shape
36            assert V == 3,\
37                   'Three vertex values per element must be specified'
38           
39
40            msg = 'Number of vertex values (%d) must be consistent with'\
41                  %N
42            msg += 'number of elements in specified domain (%d).'\
43                   %domain.number_of_elements
44           
45            assert N == domain.number_of_elements, msg
46
47        self.domain = domain   
48
49        #Allocate space for other quantities
50        self.centroid_values = zeros(N, Float)
51        self.edge_values = zeros((N, 3), Float)       
52
53        #Intialise centroid and edge_values
54        self.interpolate()
55
56    def __len__(self):
57        return self.centroid_values.shape[0]
58   
59    def interpolate(self):
60        """Compute interpolated values at edges and centroid
61        Pre-condition: vertex_values have been set
62        """
63
64        N = self.vertex_values.shape[0]       
65        for i in range(N):
66            v0 = self.vertex_values[i, 0]
67            v1 = self.vertex_values[i, 1]
68            v2 = self.vertex_values[i, 2]
69           
70            self.centroid_values[i] = (v0 + v1 + v2)/3
71
72        self.interpolate_from_vertices_to_edges()
73
74
75    def interpolate_from_vertices_to_edges(self):
76        #Call correct module function
77        #(either from this module or C-extension)
78        interpolate_from_vertices_to_edges(self)
79           
80           
81    def set_values(self, X, location='vertices', indexes = None):
82        """Set values for quantity
83
84        X: Compatible list, Numeric array (see below), constant or function
85        location: Where values are to be stored.
86                  Permissible options are: vertices, edges, centroid
87                  Default is "vertices"
88
89        In case of location == 'centroid' the dimension values must
90        be a list of a Numerical array of length N, N being the number
91        of elements. Otherwise it must be of dimension Nx3
92
93        The values will be stored in elements following their
94        internal ordering.
95
96        If values are described a function, it will be evaluated at
97        specified points
98       
99        If indexex is not 'unique vertices' Indexes is the set of element ids
100        that the operation applies to.
101        If indexex is 'unique vertices' Indexes is the set of vertex ids
102        that the operation applies to.
103       
104       
105        If selected location is vertices, values for centroid and edges
106        will be assigned interpolated values.
107        In any other case, only values for the specified locations
108        will be assigned and the others will be left undefined.
109        """
110
111        if location not in ['vertices', 'centroids', 'edges', 'unique vertices']:
112            msg = 'Invalid location: %s' %location
113            raise msg
114
115        if X is None:
116            msg = 'Given values are None'
117            raise msg           
118
119        import types, Numeric
120        assert type(indexes) in [types.ListType, types.NoneType,
121                                 Numeric.ArrayType],\
122                                 'Indices must be a list or None'
123
124       
125        if callable(X):
126            #Use function specific method
127            self.set_function_values(X, location, indexes = indexes)       
128        elif type(X) in [types.FloatType, types.IntType, types.LongType]:
129            if location == 'centroids':
130                if (indexes ==  None):
131                    self.centroid_values[:] = X
132                else:
133                    #Brute force
134                    for i in indexes:
135                        self.centroid_values[i,:] = X
136                       
137            elif location == 'edges':
138                if (indexes ==  None):
139                    self.edge_values[:] = X
140                else:
141                    #Brute force
142                    for i in indexes:
143                        self.edge_values[i,:] = X
144                       
145            elif location == 'unique vertices':
146                if (indexes ==  None):
147                    self.edge_values[:] = X
148                else:
149
150                    #Go through list of unique vertices
151                    for unique_vert_id in indexes:
152                        triangles = self.domain.vertexlist[unique_vert_id]
153                       
154                        #In case there are unused points
155                        if triangles is None: continue 
156
157                        #Go through all triangle, vertex pairs
158                        #and set corresponding vertex value
159                        for triangle_id, vertex_id in triangles:
160                            self.vertex_values[triangle_id, vertex_id] = X
161               
162                        #Intialise centroid and edge_values
163                        self.interpolate()
164            else:
165                if (indexes ==  None):
166                    self.vertex_values[:] = X
167                else:
168                    #Brute force
169                    for i_vertex in indexes:
170                        self.vertex_values[i_vertex,:] = X     
171
172        else:
173            #Use array specific method
174            self.set_array_values(X, location, indexes = indexes)
175
176        if location == 'vertices' or location == 'unique vertices':
177            #Intialise centroid and edge_values
178            self.interpolate()
179           
180        if location == 'centroids':
181            #Extrapolate 1st order - to capture notion of area being specified
182            self.extrapolate_first_order()           
183         
184    def get_values(self, location='vertices', indexes = None):
185        """get values for quantity
186
187        return X, Compatible list, Numeric array (see below)
188        location: Where values are to be stored.
189                  Permissible options are: vertices, edges, centroid
190                  Default is "vertices"
191
192        In case of location == 'centroid' the dimension values must
193        be a list of a Numerical array of length N, N being the number
194        of elements. Otherwise it must be of dimension Nx3
195
196        The returned values with be a list the length of indexes
197        (N if indexes = None).  Each value will be a list of the three
198        vertex values for this quantity.
199       
200        Indexes is the set of element ids that the operation applies to.
201       
202        """
203        from Numeric import take
204
205        if location not in ['vertices', 'centroids', 'edges', 'unique vertices']:
206            msg = 'Invalid location: %s' %location
207            raise msg         
208       
209        import types, Numeric
210        assert type(indexes) in [types.ListType, types.NoneType,
211                                 Numeric.ArrayType],\
212                                 'Indices must be a list or None'
213           
214        if location == 'centroids':
215            if (indexes ==  None):
216                indexes = range(len(self))
217            return take(self.centroid_values,indexes)                       
218        elif location == 'edges':
219            if (indexes ==  None):
220                indexes = range(len(self))
221            return take(self.edge_values,indexes)                     
222        elif location == 'unique vertices':
223            if (indexes ==  None):
224                indexes=range(self.domain.coordinates.shape[0])
225            vert_values = []
226            #Go through list of unique vertices
227            for unique_vert_id in indexes:
228                triangles = self.domain.vertexlist[unique_vert_id]
229                       
230                #In case there are unused points
231                if triangles is None: 
232                    msg = 'Unique vertex not associated with triangles' 
233                    raise msg
234
235                # Go through all triangle, vertex pairs
236                # Average the values
237                sum = 0
238                for triangle_id, vertex_id in triangles:
239                    sum += self.vertex_values[triangle_id, vertex_id]
240                vert_values.append(sum/len(triangles))
241            return Numeric.array(vert_values)
242        else:
243            if (indexes ==  None):
244                indexes = range(len(self))
245            return take(self.vertex_values,indexes)
246
247
248    def set_function_values(self, f, location='vertices', indexes = None):
249        """Set values for quantity using specified function
250
251        f: x, y -> z Function where x, y and z are arrays
252        location: Where values are to be stored.
253                  Permissible options are: vertices, centroid
254                  Default is "vertices"
255        """
256        from Numeric import take
257
258        if (indexes ==  None):
259            indexes = range(len(self))
260            is_subset = False
261        else:
262            is_subset = True
263        if location == 'centroids':
264            P = take(self.domain.centroid_coordinates,indexes)
265            if is_subset:
266                self.set_values(f(P[:,0], P[:,1]), location, indexes = indexes)
267            else:
268                self.set_values(f(P[:,0], P[:,1]), location)
269        elif location == 'vertices':
270            P = self.domain.vertex_coordinates
271            if is_subset:
272                #Brute force
273                for e in indexes:
274                    for i in range(3):
275                        self.vertex_values[e,i] = f(P[e,2*i], P[e,2*i+1])
276            else:
277                for i in range(3):
278                    self.vertex_values[:,i] = f(P[:,2*i], P[:,2*i+1])
279        else:
280            raise 'Not implemented: %s' %location
281
282           
283    def set_array_values(self, values, location='vertices', indexes = None):
284        """Set values for quantity
285
286        values: Numeric array
287        location: Where values are to be stored.
288        Permissible options are: vertices, edges, centroid, unique vertices
289        Default is "vertices"
290       
291        indexes - if this action is carried out on a subset of
292        elements or unique vertices
293        The element/unique vertex indexes are specified here.
294       
295        In case of location == 'centroid' the dimension values must
296        be a list of a Numerical array of length N, N being the number
297        of elements.
298
299        Otherwise it must be of dimension Nx3
300
301        The values will be stored in elements following their
302        internal ordering.
303
304        If selected location is vertices, values for centroid and edges
305        will be assigned interpolated values.
306        In any other case, only values for the specified locations
307        will be assigned and the others will be left undefined.
308        """
309
310        from Numeric import array, Float, Int, allclose
311
312        values = array(values).astype(Float)
313
314        if (indexes <> None):
315            indexes = array(indexes).astype(Int)
316            msg = 'Number of values must match number of indexes'
317            assert values.shape[0] == indexes.shape[0], msg
318           
319        N = self.centroid_values.shape[0]
320       
321        if location == 'centroids':
322            assert len(values.shape) == 1, 'Values array must be 1d'
323
324            if indexes == None:
325                msg = 'Number of values must match number of elements'
326                assert values.shape[0] == N, msg
327           
328                self.centroid_values = values
329            else:
330                msg = 'Number of values must match number of indexes'
331                assert values.shape[0] == indexes.shape[0], msg
332               
333                #Brute force
334                for i in range(len(indexes)):
335                    self.centroid_values[indexes[i]] = values[i]
336                   
337        elif location == 'edges':
338            assert len(values.shape) == 2, 'Values array must be 2d'
339
340            msg = 'Number of values must match number of elements'
341            assert values.shape[0] == N, msg
342           
343            msg = 'Array must be N x 3'           
344            assert values.shape[1] == 3, msg
345           
346            self.edge_values = values
347           
348        elif location == 'unique vertices':
349            assert len(values.shape) == 1 or allclose(values.shape[1:], 1),\
350                   'Values array must be 1d'
351
352            self.set_vertex_values(values.flat, indexes = indexes)
353        else:
354            if len(values.shape) == 1:
355                self.set_vertex_values(values, indexes = indexes)
356                #if indexes == None:
357                    #Values are being specified once for each unique vertex
358                #    msg = 'Number of values must match number of vertices'
359                #    assert values.shape[0] == self.domain.coordinates.shape[0], msg
360                 #   self.set_vertex_values(values)
361                #else:
362                #    for element_index, value in map(None, indexes, values):
363                #        self.vertex_values[element_index, :] = value
364                       
365            elif len(values.shape) == 2:
366                #Vertex values are given as a triplet for each triangle
367               
368                msg = 'Array must be N x 3'           
369                assert values.shape[1] == 3, msg
370               
371                if indexes == None:
372                    self.vertex_values = values
373                else:
374                    for element_index, value in map(None, indexes, values): 
375                        self.vertex_values[element_index] = value
376            else:   
377                msg = 'Values array must be 1d or 2d'
378                raise msg
379           
380
381    # FIXME have a get_vertex_values as well, so the 'stage' quantity can be
382    # set, based on the elevation
383    def set_vertex_values(self, A, indexes = None):
384        """Set vertex values for all unique vertices based on input array A
385        which has one entry per unique vertex, i.e.
386        one value for each row in array self.domain.coordinates or
387        one value for each row in vertexlist.
388
389        indexes is the list of vertex_id's that will be set.
390
391        Note: Functions not allowed
392        """
393
394        from Numeric import array, Float
395
396        #Assert that A can be converted to a Numeric array of appropriate dim
397        A = array(A, Float)
398
399        #print 'SHAPE A', A.shape
400        assert len(A.shape) == 1
401
402        if indexes == None:
403            assert A.shape[0] == self.domain.coordinates.shape[0]
404            vertex_list = range(A.shape[0])
405        else:
406            assert A.shape[0] == len(indexes)
407            vertex_list = indexes
408        #Go through list of unique vertices
409        for i_index,unique_vert_id in enumerate(vertex_list):
410            triangles = self.domain.vertexlist[unique_vert_id]
411               
412            if triangles is None: continue #In case there are unused points
413
414            #Go through all triangle, vertex pairs
415            #touching vertex unique_vert_id and set corresponding vertex value
416            for triangle_id, vertex_id in triangles:
417                self.vertex_values[triangle_id, vertex_id] = A[i_index]
418               
419        #Intialise centroid and edge_values
420        self.interpolate()
421 
422    def smooth_vertex_values(self, value_array='field_values',
423                             precision = None):
424        """ Smooths field_values or conserved_quantities data.
425        TODO: be able to smooth individual fields
426        NOTE:  This function does not have a test.
427        FIXME: NOT DONE - do we need it?
428        FIXME: this function isn't called by anything.
429               Maybe it should be removed..-DSG
430        """
431
432        from Numeric import concatenate, zeros, Float, Int, array, reshape
433
434       
435        A,V = self.get_vertex_values(xy=False,
436                                     value_array=value_array,
437                                     smooth = True,
438                                     precision = precision)
439
440        #Set some field values
441        for volume in self:
442            for i,v in enumerate(volume.vertices):
443                if value_array == 'field_values':
444                    volume.set_field_values('vertex', i, A[v,:])
445                elif value_array == 'conserved_quantities':
446                    volume.set_conserved_quantities('vertex', i, A[v,:])
447
448        if value_array == 'field_values':
449            self.precompute()
450        elif value_array == 'conserved_quantities':
451            Volume.interpolate_conserved_quantities()
452       
453   
454    #Method for outputting model results
455    #FIXME: Split up into geometric and numeric stuff.
456    #FIXME: Geometric (X,Y,V) should live in mesh.py
457    #FIXME: STill remember to move XY to mesh
458    def get_vertex_values(self,
459                          xy=True, 
460                          smooth = None,
461                          precision = None,
462                          reduction = None):
463        """Return vertex values like an OBJ format
464
465        The vertex values are returned as one sequence in the 1D float array A.
466        If requested the coordinates will be returned in 1D arrays X and Y.
467       
468        The connectivity is represented as an integer array, V, of dimension
469        M x 3, where M is the number of volumes. Each row has three indices
470        into the X, Y, A arrays defining the triangle.
471
472        if smooth is True, vertex values corresponding to one common
473        coordinate set will be smoothed according to the given
474        reduction operator. In this case vertex coordinates will be
475        de-duplicated.
476       
477        If no smoothings is required, vertex coordinates and values will
478        be aggregated as a concatenation of values at
479        vertices 0, vertices 1 and vertices 2
480
481       
482        Calling convention
483        if xy is True:
484           X,Y,A,V = get_vertex_values
485        else:
486           A,V = get_vertex_values         
487           
488        """
489
490        from Numeric import concatenate, zeros, Float, Int, array, reshape
491
492
493        if smooth is None:
494            smooth = self.domain.smooth
495
496        if precision is None:
497            precision = Float
498
499        if reduction is None:
500            reduction = self.domain.reduction
501           
502        #Create connectivity
503                       
504        if smooth == True:
505
506            V = self.domain.get_vertices()
507            N = len(self.domain.vertexlist)
508            A = zeros(N, precision)
509
510            #Smoothing loop
511            for k in range(N):
512                L = self.domain.vertexlist[k]
513               
514                #Go through all triangle, vertex pairs
515                #contributing to vertex k and register vertex value
516
517                if L is None: continue #In case there are unused points
518               
519                contributions = []
520                for volume_id, vertex_id in L:
521                    v = self.vertex_values[volume_id, vertex_id]
522                    contributions.append(v)
523
524                A[k] = reduction(contributions)   
525
526
527            if xy is True:
528                X = self.domain.coordinates[:,0].astype(precision)
529                Y = self.domain.coordinates[:,1].astype(precision)
530               
531                return X, Y, A, V
532            else:
533                return A, V
534        else:
535            #Don't smooth
536
537            # Create a V like [[0 1 2], [3 4 5]....[3*m-2 3*m-1 3*m]]
538            # These vert_id's will relate to the verts created bellow
539            m = len(self.domain)  #Number of volumes
540            M = 3*m        #Total number of unique vertices
541            V = reshape(array(range(M)).astype(Int), (m,3))
542           
543            A = self.vertex_values.flat
544
545            #Do vertex coordinates   
546            if xy is True:               
547                C = self.domain.get_vertex_coordinates()
548
549                X = C[:,0:6:2].copy()
550                Y = C[:,1:6:2].copy()               
551
552                return X.flat, Y.flat, A, V           
553            else:
554                return A, V
555
556           
557    def extrapolate_first_order(self):
558        """Extrapolate conserved quantities from centroid to
559        vertices for each volume using
560        first order scheme.
561        """
562       
563        qc = self.centroid_values
564        qv = self.vertex_values
565
566        for i in range(3):
567            qv[:,i] = qc
568           
569
570    def get_integral(self):
571        """Compute the integral of quantity across entire domain
572        """
573        integral = 0
574        for k in range(self.domain.number_of_elements):
575            area = self.domain.areas[k]
576            qc = self.centroid_values[k]     
577            integral += qc*area
578           
579        return integral   
580       
581
582class Conserved_quantity(Quantity):
583    """Class conserved quantity adds to Quantity:
584
585    boundary values, storage and method for updating, and
586    methods for (second order) extrapolation from centroid to vertices inluding
587    gradients and limiters
588    """
589
590    def __init__(self, domain, vertex_values=None):
591        Quantity.__init__(self, domain, vertex_values)
592       
593        from Numeric import zeros, Float
594
595        #Allocate space for boundary values
596        L = len(domain.boundary)
597        self.boundary_values = zeros(L, Float)
598
599        #Allocate space for updates of conserved quantities by
600        #flux calculations and forcing functions
601
602        N = domain.number_of_elements
603        self.explicit_update = zeros(N, Float )
604        self.semi_implicit_update = zeros(N, Float )
605               
606
607    def update(self, timestep):
608        #Call correct module function
609        #(either from this module or C-extension)
610        return update(self, timestep)
611
612
613    def compute_gradients(self):
614        #Call correct module function
615        #(either from this module or C-extension)
616        return compute_gradients(self)
617           
618
619    def limit(self):
620        #Call correct module function
621        #(either from this module or C-extension)
622        limit(self)
623       
624       
625    def extrapolate_second_order(self):
626        #Call correct module function
627        #(either from this module or C-extension)
628        extrapolate_second_order(self)
629
630
631def update(quantity, timestep):   
632    """Update centroid values based on values stored in
633    explicit_update and semi_implicit_update as well as given timestep
634
635    Function implementing forcing terms must take on argument
636    which is the domain and they must update either explicit
637    or implicit updates, e,g,:
638
639    def gravity(domain):
640        ....
641        domain.quantities['xmomentum'].explicit_update = ...
642        domain.quantities['ymomentum'].explicit_update = ...
643
644       
645
646    Explicit terms must have the form
647   
648        G(q, t)
649   
650    and explicit scheme is
651   
652       q^{(n+1}) = q^{(n)} + delta_t G(q^{n}, n delta_t)
653
654
655    Semi implicit forcing terms are assumed to have the form
656   
657       G(q, t) = H(q, t) q
658   
659    and the semi implicit scheme will then be
660   
661      q^{(n+1}) = q^{(n)} + delta_t H(q^{n}, n delta_t) q^{(n+1})
662
663   
664    """
665   
666    from Numeric import sum, equal, ones, Float
667       
668    N = quantity.centroid_values.shape[0]
669
670
671    #Divide H by conserved quantity to obtain G (see docstring above)
672
673
674    for k in range(N):
675        x = quantity.centroid_values[k] 
676        if x == 0.0:
677            #FIXME: Is this right
678            quantity.semi_implicit_update[k] = 0.0           
679        else:
680            quantity.semi_implicit_update[k] /= x             
681           
682    #Explicit updates
683    quantity.centroid_values += timestep*quantity.explicit_update
684           
685    #Semi implicit updates
686    denominator = ones(N, Float)-timestep*quantity.semi_implicit_update
687
688    if sum(equal(denominator, 0.0)) > 0.0:
689        msg = 'Zero division in semi implicit update. Call Stephen :-)'
690        raise msg
691    else:
692        #Update conserved_quantities from semi implicit updates
693        quantity.centroid_values /= denominator
694
695
696def interpolate_from_vertices_to_edges(quantity):
697    """Compute edge values from vertex values using linear interpolation
698    """
699
700    for k in range(quantity.vertex_values.shape[0]):
701        q0 = quantity.vertex_values[k, 0]
702        q1 = quantity.vertex_values[k, 1]
703        q2 = quantity.vertex_values[k, 2]
704           
705        quantity.edge_values[k, 0] = 0.5*(q1+q2)
706        quantity.edge_values[k, 1] = 0.5*(q0+q2) 
707        quantity.edge_values[k, 2] = 0.5*(q0+q1)
708
709
710
711def extrapolate_second_order(quantity):       
712    """Extrapolate conserved quantities from centroid to
713    vertices for each volume using
714    second order scheme.
715    """
716       
717    a, b = quantity.compute_gradients()
718
719    X = quantity.domain.get_vertex_coordinates()
720    qc = quantity.centroid_values
721    qv = quantity.vertex_values
722   
723    #Check each triangle
724    for k in range(quantity.domain.number_of_elements):
725        #Centroid coordinates           
726        x, y = quantity.domain.centroid_coordinates[k]
727       
728        #vertex coordinates
729        x0, y0, x1, y1, x2, y2 = X[k,:]
730       
731        #Extrapolate
732        qv[k,0] = qc[k] + a[k]*(x0-x) + b[k]*(y0-y)
733        qv[k,1] = qc[k] + a[k]*(x1-x) + b[k]*(y1-y)
734        qv[k,2] = qc[k] + a[k]*(x2-x) + b[k]*(y2-y)           
735
736
737def compute_gradients(quantity):                   
738    """Compute gradients of triangle surfaces defined by centroids of
739    neighbouring volumes.
740    If one edge is on the boundary, use own centroid as neighbour centroid.
741    If two or more are on the boundary, fall back to first order scheme.
742    """
743
744    from Numeric import zeros, Float
745    from util import gradient
746   
747    centroid_coordinates = quantity.domain.centroid_coordinates
748    surrogate_neighbours = quantity.domain.surrogate_neighbours   
749    centroid_values = quantity.centroid_values   
750    number_of_boundaries = quantity.domain.number_of_boundaries     
751   
752    N = centroid_values.shape[0]
753
754    a = zeros(N, Float)
755    b = zeros(N, Float)
756   
757    for k in range(N):
758        if number_of_boundaries[k] < 2:
759            #Two or three true neighbours
760
761            #Get indices of neighbours (or self when used as surrogate)     
762            k0, k1, k2 = surrogate_neighbours[k,:]
763
764            #Get data       
765            q0 = centroid_values[k0]
766            q1 = centroid_values[k1]
767            q2 = centroid_values[k2]                   
768
769            x0, y0 = centroid_coordinates[k0] #V0 centroid
770            x1, y1 = centroid_coordinates[k1] #V1 centroid
771            x2, y2 = centroid_coordinates[k2] #V2 centroid
772
773            #Gradient
774            a[k], b[k] = gradient(x0, y0, x1, y1, x2, y2, q0, q1, q2)
775       
776        elif number_of_boundaries[k] == 2:
777            #One true neighbour
778
779            #Get index of the one neighbour
780            for k0 in surrogate_neighbours[k,:]:
781                if k0 != k: break
782            assert k0 != k
783
784            k1 = k  #self
785
786            #Get data
787            q0 = centroid_values[k0]
788            q1 = centroid_values[k1]
789
790            x0, y0 = centroid_coordinates[k0] #V0 centroid
791            x1, y1 = centroid_coordinates[k1] #V1 centroid       
792
793            #Gradient
794            det = x0*y1 - x1*y0
795            if det != 0.0:
796                a[k] = (y1*q0 - y0*q1)/det
797                b[k] = (x0*q1 - x1*q0)/det
798
799        else:
800            #No true neighbours -       
801            #Fall back to first order scheme
802            pass
803       
804   
805    return a, b
806       
807                   
808
809def limit(quantity):       
810    """Limit slopes for each volume to eliminate artificial variance
811    introduced by e.g. second order extrapolator
812   
813    This is an unsophisticated limiter as it does not take into
814    account dependencies among quantities.
815   
816    precondition:
817    vertex values are estimated from gradient
818    postcondition:
819    vertex values are updated
820    """
821
822    from Numeric import zeros, Float
823
824    N = quantity.domain.number_of_elements
825   
826    beta_w = quantity.domain.beta_w
827       
828    qc = quantity.centroid_values
829    qv = quantity.vertex_values
830       
831    #Find min and max of this and neighbour's centroid values
832    qmax = zeros(qc.shape, Float)
833    qmin = zeros(qc.shape, Float)       
834   
835    for k in range(N):
836        qmax[k] = qmin[k] = qc[k]
837        for i in range(3):
838            n = quantity.domain.neighbours[k,i]
839            if n >= 0:
840                qn = qc[n] #Neighbour's centroid value
841               
842                qmin[k] = min(qmin[k], qn)
843                qmax[k] = max(qmax[k], qn)
844     
845   
846    #Diffences between centroids and maxima/minima
847    dqmax = qmax - qc
848    dqmin = qmin - qc
849       
850    #Deltas between vertex and centroid values
851    dq = zeros(qv.shape, Float)
852    for i in range(3):
853        dq[:,i] = qv[:,i] - qc
854
855    #Phi limiter   
856    for k in range(N):
857       
858        #Find the gradient limiter (phi) across vertices
859        phi = 1.0
860        for i in range(3):
861            r = 1.0
862            if (dq[k,i] > 0): r = dqmax[k]/dq[k,i]
863            if (dq[k,i] < 0): r = dqmin[k]/dq[k,i]
864           
865            phi = min( min(r*beta_w, 1), phi )   
866
867        #Then update using phi limiter
868        for i in range(3):           
869            qv[k,i] = qc[k] + phi*dq[k,i]
870
871
872
873import compile
874if compile.can_use_C_extension('quantity_ext.c'):
875    #Replace python version with c implementations
876       
877    from quantity_ext import limit, compute_gradients,\
878    extrapolate_second_order, interpolate_from_vertices_to_edges, update
879
Note: See TracBrowser for help on using the repository browser.