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

Last change on this file since 900 was 826, checked in by ole, 20 years ago

Played with DEM's and NetCDF

File size: 29.1 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
571class Conserved_quantity(Quantity):
572    """Class conserved quantity adds to Quantity:
573
574    boundary values, storage and method for updating, and
575    methods for (second order) extrapolation from centroid to vertices inluding
576    gradients and limiters
577    """
578
579    def __init__(self, domain, vertex_values=None):
580        Quantity.__init__(self, domain, vertex_values)
581       
582        from Numeric import zeros, Float
583
584        #Allocate space for boundary values
585        L = len(domain.boundary)
586        self.boundary_values = zeros(L, Float)
587
588        #Allocate space for updates of conserved quantities by
589        #flux calculations and forcing functions
590
591        N = domain.number_of_elements
592        self.explicit_update = zeros(N, Float )
593        self.semi_implicit_update = zeros(N, Float )
594               
595
596    def update(self, timestep):
597        #Call correct module function
598        #(either from this module or C-extension)
599        return update(self, timestep)
600
601
602    def compute_gradients(self):
603        #Call correct module function
604        #(either from this module or C-extension)
605        return compute_gradients(self)
606           
607
608    def limit(self):
609        #Call correct module function
610        #(either from this module or C-extension)
611        limit(self)
612       
613       
614    def extrapolate_second_order(self):
615        #Call correct module function
616        #(either from this module or C-extension)
617        extrapolate_second_order(self)
618
619
620def update(quantity, timestep):   
621    """Update centroid values based on values stored in
622    explicit_update and semi_implicit_update as well as given timestep
623
624    Function implementing forcing terms must take on argument
625    which is the domain and they must update either explicit
626    or implicit updates, e,g,:
627
628    def gravity(domain):
629        ....
630        domain.quantities['xmomentum'].explicit_update = ...
631        domain.quantities['ymomentum'].explicit_update = ...
632
633       
634
635    Explicit terms must have the form
636   
637        G(q, t)
638   
639    and explicit scheme is
640   
641       q^{(n+1}) = q^{(n)} + delta_t G(q^{n}, n delta_t)
642
643
644    Semi implicit forcing terms are assumed to have the form
645   
646       G(q, t) = H(q, t) q
647   
648    and the semi implicit scheme will then be
649   
650      q^{(n+1}) = q^{(n)} + delta_t H(q^{n}, n delta_t) q^{(n+1})
651
652   
653    """
654   
655    from Numeric import sum, equal, ones, Float
656       
657    N = quantity.centroid_values.shape[0]
658
659
660    #Divide H by conserved quantity to obtain G (see docstring above)
661
662
663    for k in range(N):
664        x = quantity.centroid_values[k] 
665        if x == 0.0:
666            #FIXME: Is this right
667            quantity.semi_implicit_update[k] = 0.0           
668        else:
669            quantity.semi_implicit_update[k] /= x             
670           
671    #Explicit updates
672    quantity.centroid_values += timestep*quantity.explicit_update
673           
674    #Semi implicit updates
675    denominator = ones(N, Float)-timestep*quantity.semi_implicit_update
676
677    if sum(equal(denominator, 0.0)) > 0.0:
678        msg = 'Zero division in semi implicit update. Call Stephen :-)'
679        raise msg
680    else:
681        #Update conserved_quantities from semi implicit updates
682        quantity.centroid_values /= denominator
683
684
685def interpolate_from_vertices_to_edges(quantity):
686    """Compute edge values from vertex values using linear interpolation
687    """
688
689    for k in range(quantity.vertex_values.shape[0]):
690        q0 = quantity.vertex_values[k, 0]
691        q1 = quantity.vertex_values[k, 1]
692        q2 = quantity.vertex_values[k, 2]
693           
694        quantity.edge_values[k, 0] = 0.5*(q1+q2)
695        quantity.edge_values[k, 1] = 0.5*(q0+q2) 
696        quantity.edge_values[k, 2] = 0.5*(q0+q1)
697
698
699
700def extrapolate_second_order(quantity):       
701    """Extrapolate conserved quantities from centroid to
702    vertices for each volume using
703    second order scheme.
704    """
705       
706    a, b = quantity.compute_gradients()
707
708    X = quantity.domain.get_vertex_coordinates()
709    qc = quantity.centroid_values
710    qv = quantity.vertex_values
711   
712    #Check each triangle
713    for k in range(quantity.domain.number_of_elements):
714        #Centroid coordinates           
715        x, y = quantity.domain.centroid_coordinates[k]
716       
717        #vertex coordinates
718        x0, y0, x1, y1, x2, y2 = X[k,:]
719       
720        #Extrapolate
721        qv[k,0] = qc[k] + a[k]*(x0-x) + b[k]*(y0-y)
722        qv[k,1] = qc[k] + a[k]*(x1-x) + b[k]*(y1-y)
723        qv[k,2] = qc[k] + a[k]*(x2-x) + b[k]*(y2-y)           
724
725
726def compute_gradients(quantity):                   
727    """Compute gradients of triangle surfaces defined by centroids of
728    neighbouring volumes.
729    If one edge is on the boundary, use own centroid as neighbour centroid.
730    If two or more are on the boundary, fall back to first order scheme.
731    """
732
733    from Numeric import zeros, Float
734    from util import gradient
735   
736    centroid_coordinates = quantity.domain.centroid_coordinates
737    surrogate_neighbours = quantity.domain.surrogate_neighbours   
738    centroid_values = quantity.centroid_values   
739    number_of_boundaries = quantity.domain.number_of_boundaries     
740   
741    N = centroid_values.shape[0]
742
743    a = zeros(N, Float)
744    b = zeros(N, Float)
745   
746    for k in range(N):
747        if number_of_boundaries[k] < 2:
748            #Two or three true neighbours
749
750            #Get indices of neighbours (or self when used as surrogate)     
751            k0, k1, k2 = surrogate_neighbours[k,:]
752
753            #Get data       
754            q0 = centroid_values[k0]
755            q1 = centroid_values[k1]
756            q2 = centroid_values[k2]                   
757
758            x0, y0 = centroid_coordinates[k0] #V0 centroid
759            x1, y1 = centroid_coordinates[k1] #V1 centroid
760            x2, y2 = centroid_coordinates[k2] #V2 centroid
761
762            #Gradient
763            a[k], b[k] = gradient(x0, y0, x1, y1, x2, y2, q0, q1, q2)
764       
765        elif number_of_boundaries[k] == 2:
766            #One true neighbour
767
768            #Get index of the one neighbour
769            for k0 in surrogate_neighbours[k,:]:
770                if k0 != k: break
771            assert k0 != k
772
773            k1 = k  #self
774
775            #Get data
776            q0 = centroid_values[k0]
777            q1 = centroid_values[k1]
778
779            x0, y0 = centroid_coordinates[k0] #V0 centroid
780            x1, y1 = centroid_coordinates[k1] #V1 centroid       
781
782            #Gradient
783            det = x0*y1 - x1*y0
784            if det != 0.0:
785                a[k] = (y1*q0 - y0*q1)/det
786                b[k] = (x0*q1 - x1*q0)/det
787
788        else:
789            #No true neighbours -       
790            #Fall back to first order scheme
791            pass
792       
793   
794    return a, b
795       
796                   
797
798def limit(quantity):       
799    """Limit slopes for each volume to eliminate artificial variance
800    introduced by e.g. second order extrapolator
801   
802    This is an unsophisticated limiter as it does not take into
803    account dependencies among quantities.
804   
805    precondition:
806    vertex values are estimated from gradient
807    postcondition:
808    vertex values are updated
809    """
810
811    from Numeric import zeros, Float
812
813    N = quantity.domain.number_of_elements
814   
815    beta = quantity.domain.beta
816       
817    qc = quantity.centroid_values
818    qv = quantity.vertex_values
819       
820    #Find min and max of this and neighbour's centroid values
821    qmax = zeros(qc.shape, Float)
822    qmin = zeros(qc.shape, Float)       
823   
824    for k in range(N):
825        qmax[k] = qmin[k] = qc[k]
826        for i in range(3):
827            n = quantity.domain.neighbours[k,i]
828            if n >= 0:
829                qn = qc[n] #Neighbour's centroid value
830               
831                qmin[k] = min(qmin[k], qn)
832                qmax[k] = max(qmax[k], qn)
833     
834   
835    #Diffences between centroids and maxima/minima
836    dqmax = qmax - qc
837    dqmin = qmin - qc
838       
839    #Deltas between vertex and centroid values
840    dq = zeros(qv.shape, Float)
841    for i in range(3):
842        dq[:,i] = qv[:,i] - qc
843
844    #Phi limiter   
845    for k in range(N):
846       
847        #Find the gradient limiter (phi) across vertices
848        phi = 1.0
849        for i in range(3):
850            r = 1.0
851            if (dq[k,i] > 0): r = dqmax[k]/dq[k,i]
852            if (dq[k,i] < 0): r = dqmin[k]/dq[k,i]
853           
854            phi = min( min(r*beta, 1), phi )   
855
856        #Then update using phi limiter
857        for i in range(3):           
858            qv[k,i] = qc[k] + phi*dq[k,i]
859
860
861
862import compile
863if compile.can_use_C_extension('quantity_ext.c'):
864    #Replace python version with c implementations
865       
866    from quantity_ext import limit, compute_gradients,\
867    extrapolate_second_order, interpolate_from_vertices_to_edges, update
868
Note: See TracBrowser for help on using the repository browser.