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

Last change on this file since 773 was 773, checked in by ole, 19 years ago

Changed quantity name 'level' to 'stage'

File size: 29.0 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
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, 'Values array must be 1d'
350            self.set_vertex_values(values, indexes = indexes)
351        else:
352            if len(values.shape) == 1:
353                self.set_vertex_values(values, indexes = indexes)
354                #if indexes == None:
355                    #Values are being specified once for each unique vertex
356                #    msg = 'Number of values must match number of vertices'
357                #    assert values.shape[0] == self.domain.coordinates.shape[0], msg
358                 #   self.set_vertex_values(values)
359                #else:
360                #    for element_index, value in map(None, indexes, values):
361                #        self.vertex_values[element_index, :] = value
362                       
363            elif len(values.shape) == 2:
364                #Vertex values are given as a triplet for each triangle
365               
366                msg = 'Array must be N x 3'           
367                assert values.shape[1] == 3, msg
368               
369                if indexes == None:
370                    self.vertex_values = values
371                else:
372                    for element_index, value in map(None, indexes, values): 
373                        self.vertex_values[element_index] = value
374            else:   
375                msg = 'Values array must be 1d or 2d'
376                raise msg
377           
378
379    # FIXME have a get_vertex_values as well, so the 'stage' quantity can be
380    # set, based on the elevation
381    def set_vertex_values(self, A, indexes = None):
382        """Set vertex values for all unique vertices based on input array A
383        which has one entry per unique vertex, i.e.
384        one value for each row in array self.domain.coordinates or
385        one value for each row in vertexlist.
386
387        indexes is the list of vertex_id's that will be set.
388
389        Note: Functions not allowed
390        """
391
392        from Numeric import array, Float
393
394        #Assert that A can be converted to a Numeric array of appropriate dim
395        A = array(A, Float)
396
397        assert len(A.shape) == 1
398
399        if indexes == None:
400            assert A.shape[0] == self.domain.coordinates.shape[0]
401            vertex_list = range(A.shape[0])
402        else:
403            assert A.shape[0] == len(indexes)
404            vertex_list = indexes
405        #Go through list of unique vertices
406        for i_index,unique_vert_id in enumerate(vertex_list):
407            triangles = self.domain.vertexlist[unique_vert_id]
408               
409            if triangles is None: continue #In case there are unused points
410
411            #Go through all triangle, vertex pairs
412            #touching vertex unique_vert_id and set corresponding vertex value
413            for triangle_id, vertex_id in triangles:
414                self.vertex_values[triangle_id, vertex_id] = A[i_index]
415               
416        #Intialise centroid and edge_values
417        self.interpolate()
418 
419    def smooth_vertex_values(self, value_array='field_values',
420                             precision = None):
421        """ Smooths field_values or conserved_quantities data.
422        TODO: be able to smooth individual fields
423        NOTE:  This function does not have a test.
424        FIXME: NOT DONE - do we need it?
425        FIXME: this function isn't called by anything.
426               Maybe it should be removed..-DSG
427        """
428
429        from Numeric import concatenate, zeros, Float, Int, array, reshape
430
431       
432        A,V = self.get_vertex_values(xy=False,
433                                     value_array=value_array,
434                                     smooth = True,
435                                     precision = precision)
436
437        #Set some field values
438        for volume in self:
439            for i,v in enumerate(volume.vertices):
440                if value_array == 'field_values':
441                    volume.set_field_values('vertex', i, A[v,:])
442                elif value_array == 'conserved_quantities':
443                    volume.set_conserved_quantities('vertex', i, A[v,:])
444
445        if value_array == 'field_values':
446            self.precompute()
447        elif value_array == 'conserved_quantities':
448            Volume.interpolate_conserved_quantities()
449       
450   
451    #Method for outputting model results
452    #FIXME: Split up into geometric and numeric stuff.
453    #FIXME: Geometric (X,Y,V) should live in mesh.py
454    #FIXME: STill remember to move XY to mesh
455    def get_vertex_values(self,
456                          xy=True, 
457                          smooth = None,
458                          precision = None,
459                          reduction = None):
460        """Return vertex values like an OBJ format
461
462        The vertex values are returned as one sequence in the 1D float array A.
463        If requested the coordinates will be returned in 1D arrays X and Y.
464       
465        The connectivity is represented as an integer array, V, of dimension
466        M x 3, where M is the number of volumes. Each row has three indices
467        into the X, Y, A arrays defining the triangle.
468
469        if smooth is True, vertex values corresponding to one common
470        coordinate set will be smoothed according to the given
471        reduction operator. In this case vertex coordinates will be
472        de-duplicated.
473       
474        If no smoothings is required, vertex coordinates and values will
475        be aggregated as a concatenation of values at
476        vertices 0, vertices 1 and vertices 2
477
478       
479        Calling convention
480        if xy is True:
481           X,Y,A,V = get_vertex_values
482        else:
483           A,V = get_vertex_values         
484           
485        """
486
487        from Numeric import concatenate, zeros, Float, Int, array, reshape
488
489
490        if smooth is None:
491            smooth = self.domain.smooth
492
493        if precision is None:
494            precision = Float
495
496        if reduction is None:
497            reduction = self.domain.reduction
498           
499        #Create connectivity
500                       
501        if smooth == True:
502
503            V = self.domain.get_vertices()
504            N = len(self.domain.vertexlist)
505            A = zeros(N, precision)
506
507            #Smoothing loop
508            for k in range(N):
509                L = self.domain.vertexlist[k]
510               
511                #Go through all triangle, vertex pairs
512                #contributing to vertex k and register vertex value
513
514                if L is None: continue #In case there are unused points
515               
516                contributions = []
517                for volume_id, vertex_id in L:
518                    v = self.vertex_values[volume_id, vertex_id]
519                    contributions.append(v)
520
521                A[k] = reduction(contributions)   
522
523
524            if xy is True:
525                X = self.domain.coordinates[:,0].astype(precision)
526                Y = self.domain.coordinates[:,1].astype(precision)
527               
528                return X, Y, A, V
529            else:
530                return A, V
531        else:
532            #Don't smooth
533
534            # Create a V like [[0 1 2], [3 4 5]....[3*m-2 3*m-1 3*m]]
535            # These vert_id's will relate to the verts created bellow
536            m = len(self.domain)  #Number of volumes
537            M = 3*m        #Total number of unique vertices
538            V = reshape(array(range(M)).astype(Int), (m,3))
539           
540            A = self.vertex_values.flat
541
542            #Do vertex coordinates   
543            if xy is True:               
544                C = self.domain.get_vertex_coordinates()
545
546                X = C[:,0:6:2].copy()
547                Y = C[:,1:6:2].copy()               
548
549                return X.flat, Y.flat, A, V           
550            else:
551                return A, V
552
553           
554    def extrapolate_first_order(self):
555        """Extrapolate conserved quantities from centroid to
556        vertices for each volume using
557        first order scheme.
558        """
559       
560        qc = self.centroid_values
561        qv = self.vertex_values
562
563        for i in range(3):
564            qv[:,i] = qc
565           
566
567
568class Conserved_quantity(Quantity):
569    """Class conserved quantity adds to Quantity:
570
571    boundary values, storage and method for updating, and
572    methods for (second order) extrapolation from centroid to vertices inluding
573    gradients and limiters
574    """
575
576    def __init__(self, domain, vertex_values=None):
577        Quantity.__init__(self, domain, vertex_values)
578       
579        from Numeric import zeros, Float
580
581        #Allocate space for boundary values
582        L = len(domain.boundary)
583        self.boundary_values = zeros(L, Float)
584
585        #Allocate space for updates of conserved quantities by
586        #flux calculations and forcing functions
587
588        N = domain.number_of_elements
589        self.explicit_update = zeros(N, Float )
590        self.semi_implicit_update = zeros(N, Float )
591               
592
593    def update(self, timestep):
594        #Call correct module function
595        #(either from this module or C-extension)
596        return update(self, timestep)
597
598
599    def compute_gradients(self):
600        #Call correct module function
601        #(either from this module or C-extension)
602        return compute_gradients(self)
603           
604
605    def limit(self):
606        #Call correct module function
607        #(either from this module or C-extension)
608        limit(self)
609       
610       
611    def extrapolate_second_order(self):
612        #Call correct module function
613        #(either from this module or C-extension)
614        extrapolate_second_order(self)
615
616
617def update(quantity, timestep):   
618    """Update centroid values based on values stored in
619    explicit_update and semi_implicit_update as well as given timestep
620
621    Function implementing forcing terms must take on argument
622    which is the domain and they must update either explicit
623    or implicit updates, e,g,:
624
625    def gravity(domain):
626        ....
627        domain.quantities['xmomentum'].explicit_update = ...
628        domain.quantities['ymomentum'].explicit_update = ...
629
630       
631
632    Explicit terms must have the form
633   
634        G(q, t)
635   
636    and explicit scheme is
637   
638       q^{(n+1}) = q^{(n)} + delta_t G(q^{n}, n delta_t)
639
640
641    Semi implicit forcing terms are assumed to have the form
642   
643       G(q, t) = H(q, t) q
644   
645    and the semi implicit scheme will then be
646   
647      q^{(n+1}) = q^{(n)} + delta_t H(q^{n}, n delta_t) q^{(n+1})
648
649   
650    """
651   
652    from Numeric import sum, equal, ones, Float
653       
654    N = quantity.centroid_values.shape[0]
655
656
657    #Divide H by conserved quantity to obtain G (see docstring above)
658
659
660    for k in range(N):
661        x = quantity.centroid_values[k] 
662        if x == 0.0:
663            #FIXME: Is this right
664            quantity.semi_implicit_update[k] = 0.0           
665        else:
666            quantity.semi_implicit_update[k] /= x             
667           
668    #Explicit updates
669    quantity.centroid_values += timestep*quantity.explicit_update
670           
671    #Semi implicit updates
672    denominator = ones(N, Float)-timestep*quantity.semi_implicit_update
673
674    if sum(equal(denominator, 0.0)) > 0.0:
675        msg = 'Zero division in semi implicit update. Call Stephen :-)'
676        raise msg
677    else:
678        #Update conserved_quantities from semi implicit updates
679        quantity.centroid_values /= denominator
680
681
682def interpolate_from_vertices_to_edges(quantity):
683    """Compute edge values from vertex values using linear interpolation
684    """
685
686    for k in range(quantity.vertex_values.shape[0]):
687        q0 = quantity.vertex_values[k, 0]
688        q1 = quantity.vertex_values[k, 1]
689        q2 = quantity.vertex_values[k, 2]
690           
691        quantity.edge_values[k, 0] = 0.5*(q1+q2)
692        quantity.edge_values[k, 1] = 0.5*(q0+q2) 
693        quantity.edge_values[k, 2] = 0.5*(q0+q1)
694
695
696
697def extrapolate_second_order(quantity):       
698    """Extrapolate conserved quantities from centroid to
699    vertices for each volume using
700    second order scheme.
701    """
702       
703    a, b = quantity.compute_gradients()
704
705    X = quantity.domain.get_vertex_coordinates()
706    qc = quantity.centroid_values
707    qv = quantity.vertex_values
708   
709    #Check each triangle
710    for k in range(quantity.domain.number_of_elements):
711        #Centroid coordinates           
712        x, y = quantity.domain.centroid_coordinates[k]
713       
714        #vertex coordinates
715        x0, y0, x1, y1, x2, y2 = X[k,:]
716       
717        #Extrapolate
718        qv[k,0] = qc[k] + a[k]*(x0-x) + b[k]*(y0-y)
719        qv[k,1] = qc[k] + a[k]*(x1-x) + b[k]*(y1-y)
720        qv[k,2] = qc[k] + a[k]*(x2-x) + b[k]*(y2-y)           
721
722
723def compute_gradients(quantity):                   
724    """Compute gradients of triangle surfaces defined by centroids of
725    neighbouring volumes.
726    If one edge is on the boundary, use own centroid as neighbour centroid.
727    If two or more are on the boundary, fall back to first order scheme.
728    """
729
730    from Numeric import zeros, Float
731    from util import gradient
732   
733    centroid_coordinates = quantity.domain.centroid_coordinates
734    surrogate_neighbours = quantity.domain.surrogate_neighbours   
735    centroid_values = quantity.centroid_values   
736    number_of_boundaries = quantity.domain.number_of_boundaries     
737   
738    N = centroid_values.shape[0]
739
740    a = zeros(N, Float)
741    b = zeros(N, Float)
742   
743    for k in range(N):
744        if number_of_boundaries[k] < 2:
745            #Two or three true neighbours
746
747            #Get indices of neighbours (or self when used as surrogate)     
748            k0, k1, k2 = surrogate_neighbours[k,:]
749
750            #Get data       
751            q0 = centroid_values[k0]
752            q1 = centroid_values[k1]
753            q2 = centroid_values[k2]                   
754
755            x0, y0 = centroid_coordinates[k0] #V0 centroid
756            x1, y1 = centroid_coordinates[k1] #V1 centroid
757            x2, y2 = centroid_coordinates[k2] #V2 centroid
758
759            #Gradient
760            a[k], b[k] = gradient(x0, y0, x1, y1, x2, y2, q0, q1, q2)
761       
762        elif number_of_boundaries[k] == 2:
763            #One true neighbour
764
765            #Get index of the one neighbour
766            for k0 in surrogate_neighbours[k,:]:
767                if k0 != k: break
768            assert k0 != k
769
770            k1 = k  #self
771
772            #Get data
773            q0 = centroid_values[k0]
774            q1 = centroid_values[k1]
775
776            x0, y0 = centroid_coordinates[k0] #V0 centroid
777            x1, y1 = centroid_coordinates[k1] #V1 centroid       
778
779            #Gradient
780            det = x0*y1 - x1*y0
781            if det != 0.0:
782                a[k] = (y1*q0 - y0*q1)/det
783                b[k] = (x0*q1 - x1*q0)/det
784
785        else:
786            #No true neighbours -       
787            #Fall back to first order scheme
788            pass
789       
790   
791    return a, b
792       
793                   
794
795def limit(quantity):       
796    """Limit slopes for each volume to eliminate artificial variance
797    introduced by e.g. second order extrapolator
798   
799    This is an unsophisticated limiter as it does not take into
800    account dependencies among quantities.
801   
802    precondition:
803    vertex values are estimated from gradient
804    postcondition:
805    vertex values are updated
806    """
807
808    from Numeric import zeros, Float
809
810    N = quantity.domain.number_of_elements
811   
812    beta = quantity.domain.beta
813       
814    qc = quantity.centroid_values
815    qv = quantity.vertex_values
816       
817    #Find min and max of this and neighbour's centroid values
818    qmax = zeros(qc.shape, Float)
819    qmin = zeros(qc.shape, Float)       
820   
821    for k in range(N):
822        qmax[k] = qmin[k] = qc[k]
823        for i in range(3):
824            n = quantity.domain.neighbours[k,i]
825            if n >= 0:
826                qn = qc[n] #Neighbour's centroid value
827               
828                qmin[k] = min(qmin[k], qn)
829                qmax[k] = max(qmax[k], qn)
830     
831   
832    #Diffences between centroids and maxima/minima
833    dqmax = qmax - qc
834    dqmin = qmin - qc
835       
836    #Deltas between vertex and centroid values
837    dq = zeros(qv.shape, Float)
838    for i in range(3):
839        dq[:,i] = qv[:,i] - qc
840
841    #Phi limiter   
842    for k in range(N):
843       
844        #Find the gradient limiter (phi) across vertices
845        phi = 1.0
846        for i in range(3):
847            r = 1.0
848            if (dq[k,i] > 0): r = dqmax[k]/dq[k,i]
849            if (dq[k,i] < 0): r = dqmin[k]/dq[k,i]
850           
851            phi = min( min(r*beta, 1), phi )   
852
853        #Then update using phi limiter
854        for i in range(3):           
855            qv[k,i] = qc[k] + phi*dq[k,i]
856
857
858
859import compile
860if compile.can_use_C_extension('quantity_ext.c'):
861    #Replace python version with c implementations
862       
863    from quantity_ext import limit, compute_gradients,\
864    extrapolate_second_order, interpolate_from_vertices_to_edges, update
865
Note: See TracBrowser for help on using the repository browser.