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
RevLine 
[229]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
[242]22        from mesh import Mesh
[229]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:   
[265]33            self.vertex_values = array(vertex_values).astype(Float)
[229]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
[275]56    def __len__(self):
57        return self.centroid_values.shape[0]
[389]58   
[229]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
[242]71
72        self.interpolate_from_vertices_to_edges()
73
74
75    def interpolate_from_vertices_to_edges(self):
[265]76        #Call correct module function
77        #(either from this module or C-extension)
78        interpolate_from_vertices_to_edges(self)
[229]79           
[242]80           
[461]81    def set_values(self, X, location='vertices', indexes = None):
[242]82        """Set values for quantity
[229]83
[242]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
[590]91        of elements. Otherwise it must be of dimension Nx3
[242]92
93        The values will be stored in elements following their
94        internal ordering.
95
[715]96        If values are described a function, it will be evaluated at
97        specified points
[546]98       
[715]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       
[242]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
[715]111        if location not in ['vertices', 'centroids', 'edges', 'unique vertices']:
[242]112            msg = 'Invalid location: %s' %location
113            raise msg
114
115        if X is None:
116            msg = 'Given values are None'
117            raise msg           
[700]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
[242]124       
125        if callable(X):
126            #Use function specific method
[590]127            self.set_function_values(X, location, indexes = indexes)       
[242]128        elif type(X) in [types.FloatType, types.IntType, types.LongType]:
129            if location == 'centroids':
[517]130                if (indexes ==  None):
131                    self.centroid_values[:] = X
132                else:
133                    #Brute force
[546]134                    for i in indexes:
[517]135                        self.centroid_values[i,:] = X
136                       
[242]137            elif location == 'edges':
[517]138                if (indexes ==  None):
139                    self.edge_values[:] = X
140                else:
141                    #Brute force
[546]142                    for i in indexes:
[517]143                        self.edge_values[i,:] = X
[715]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()
[242]164            else:
[517]165                if (indexes ==  None):
166                    self.vertex_values[:] = X
167                else:
168                    #Brute force
[546]169                    for i_vertex in indexes:
170                        self.vertex_values[i_vertex,:] = X     
[242]171
172        else:
173            #Use array specific method
[461]174            self.set_array_values(X, location, indexes = indexes)
[242]175
[715]176        if location == 'vertices' or location == 'unique vertices':
[242]177            #Intialise centroid and edge_values
178            self.interpolate()
[659]179           
180        if location == 'centroids':
181            #Extrapolate 1st order - to capture notion of area being specified
182            self.extrapolate_first_order()           
[531]183         
184    def get_values(self, location='vertices', indexes = None):
185        """get values for quantity
[242]186
[531]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"
[242]191
[531]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
[590]194        of elements. Otherwise it must be of dimension Nx3
[531]195
[590]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       
[531]202        """
203        from Numeric import take
204
[715]205        if location not in ['vertices', 'centroids', 'edges', 'unique vertices']:
[531]206            msg = 'Invalid location: %s' %location
207            raise msg         
208       
[700]209        import types, Numeric
210        assert type(indexes) in [types.ListType, types.NoneType,
211                                 Numeric.ArrayType],\
212                                 'Indices must be a list or None'
[531]213           
214        if location == 'centroids':
[715]215            if (indexes ==  None):
216                indexes = range(len(self))
[531]217            return take(self.centroid_values,indexes)                       
218        elif location == 'edges':
[715]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)
[531]242        else:
[715]243            if (indexes ==  None):
244                indexes = range(len(self))
[531]245            return take(self.vertex_values,indexes)
246
247
[590]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.
[593]253                  Permissible options are: vertices, centroid
[590]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
[591]281
[242]282           
[459]283    def set_array_values(self, values, location='vertices', indexes = None):
[242]284        """Set values for quantity
285
286        values: Numeric array
287        location: Where values are to be stored.
[715]288        Permissible options are: vertices, edges, centroid, unique vertices
289        Default is "vertices"
[461]290       
[715]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.
[461]294       
[242]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
[590]297        of elements.
[242]298
[590]299        Otherwise it must be of dimension Nx3
300
[242]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
[826]310        from Numeric import array, Float, Int, allclose
[242]311
312        values = array(values).astype(Float)
313
[459]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           
[242]319        N = self.centroid_values.shape[0]
320       
321        if location == 'centroids':
322            assert len(values.shape) == 1, 'Values array must be 1d'
[344]323
[459]324            if indexes == None:
325                msg = 'Number of values must match number of elements'
326                assert values.shape[0] == N, msg
[344]327           
[459]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                   
[242]337        elif location == 'edges':
338            assert len(values.shape) == 2, 'Values array must be 2d'
[344]339
340            msg = 'Number of values must match number of elements'
341            assert values.shape[0] == N, msg
342           
[242]343            msg = 'Array must be N x 3'           
344            assert values.shape[1] == 3, msg
345           
346            self.edge_values = values
[715]347           
348        elif location == 'unique vertices':
[826]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)
[242]353        else:
[344]354            if len(values.shape) == 1:
[715]355                self.set_vertex_values(values, indexes = indexes)
356                #if indexes == None:
[459]357                    #Values are being specified once for each unique vertex
[715]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
[517]364                       
[344]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
[459]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
[344]376            else:   
377                msg = 'Values array must be 1d or 2d'
378                raise msg
[459]379           
380
[773]381    # FIXME have a get_vertex_values as well, so the 'stage' quantity can be
[715]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
[324]392        """
[242]393
[324]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
[826]399        #print 'SHAPE A', A.shape
[324]400        assert len(A.shape) == 1
401
[715]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
[324]408        #Go through list of unique vertices
[715]409        for i_index,unique_vert_id in enumerate(vertex_list):
410            triangles = self.domain.vertexlist[unique_vert_id]
[324]411               
[715]412            if triangles is None: continue #In case there are unused points
[324]413
414            #Go through all triangle, vertex pairs
[715]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]
[324]418               
419        #Intialise centroid and edge_values
420        self.interpolate()
[459]421 
[274]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.
[283]427        FIXME: NOT DONE - do we need it?
[529]428        FIXME: this function isn't called by anything.
429               Maybe it should be removed..-DSG
[274]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
[288]455    #FIXME: Split up into geometric and numeric stuff.
456    #FIXME: Geometric (X,Y,V) should live in mesh.py
[292]457    #FIXME: STill remember to move XY to mesh
[274]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
[292]478        be aggregated as a concatenation of values at
[274]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
[291]501           
502        #Create connectivity
503                       
[274]504        if smooth == True:
[275]505
[529]506            V = self.domain.get_vertices()
[275]507            N = len(self.domain.vertexlist)
508            A = zeros(N, precision)
[291]509
[274]510            #Smoothing loop
[275]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
[297]516
517                if L is None: continue #In case there are unused points
518               
519                contributions = []
[275]520                for volume_id, vertex_id in L:
521                    v = self.vertex_values[volume_id, vertex_id]
522                    contributions.append(v)
[274]523
[275]524                A[k] = reduction(contributions)   
[274]525
526
527            if xy is True:
[281]528                X = self.domain.coordinates[:,0].astype(precision)
529                Y = self.domain.coordinates[:,1].astype(precision)
530               
[274]531                return X, Y, A, V
532            else:
533                return A, V
534        else:
535            #Don't smooth
536
[529]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           
[281]543            A = self.vertex_values.flat
[274]544
545            #Do vertex coordinates   
546            if xy is True:               
[275]547                C = self.domain.get_vertex_coordinates()
548
[282]549                X = C[:,0:6:2].copy()
550                Y = C[:,1:6:2].copy()               
[275]551
552                return X.flat, Y.flat, A, V           
[274]553            else:
554                return A, V
555
556           
[659]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
[274]568           
569
570
[242]571class Conserved_quantity(Quantity):
572    """Class conserved quantity adds to Quantity:
573
574    boundary values, storage and method for updating, and
[659]575    methods for (second order) extrapolation from centroid to vertices inluding
[242]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
[229]596    def update(self, timestep):
[272]597        #Call correct module function
598        #(either from this module or C-extension)
599        return update(self, timestep)
[229]600
601
602    def compute_gradients(self):
[260]603        #Call correct module function
604        #(either from this module or C-extension)
605        return compute_gradients(self)
606           
[229]607
608    def limit(self):
[245]609        #Call correct module function
610        #(either from this module or C-extension)
611        limit(self)
[229]612       
613       
614    def extrapolate_second_order(self):
[255]615        #Call correct module function
616        #(either from this module or C-extension)
617        extrapolate_second_order(self)
618
619
[272]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
[476]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   
[272]653    """
654   
655    from Numeric import sum, equal, ones, Float
656       
657    N = quantity.centroid_values.shape[0]
[458]658
659
[476]660    #Divide H by conserved quantity to obtain G (see docstring above)
[514]661
662
[458]663    for k in range(N):
664        x = quantity.centroid_values[k] 
665        if x == 0.0:
[514]666            #FIXME: Is this right
[458]667            quantity.semi_implicit_update[k] = 0.0           
668        else:
669            quantity.semi_implicit_update[k] /= x             
670           
[272]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
[265]676
[272]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
[265]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):       
[255]701    """Extrapolate conserved quantities from centroid to
702    vertices for each volume using
703    second order scheme.
704    """
[229]705       
[265]706    a, b = quantity.compute_gradients()
[229]707
[265]708    X = quantity.domain.get_vertex_coordinates()
709    qc = quantity.centroid_values
710    qv = quantity.vertex_values
[255]711   
712    #Check each triangle
[265]713    for k in range(quantity.domain.number_of_elements):
[255]714        #Centroid coordinates           
[305]715        x, y = quantity.domain.centroid_coordinates[k]
[229]716       
[255]717        #vertex coordinates
[260]718        x0, y0, x1, y1, x2, y2 = X[k,:]
[255]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)           
[229]724
[245]725
[260]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   
[305]736    centroid_coordinates = quantity.domain.centroid_coordinates
[260]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
[261]753            #Get data       
[260]754            q0 = centroid_values[k0]
755            q1 = centroid_values[k1]
756            q2 = centroid_values[k2]                   
757
[305]758            x0, y0 = centroid_coordinates[k0] #V0 centroid
759            x1, y1 = centroid_coordinates[k1] #V1 centroid
760            x2, y2 = centroid_coordinates[k2] #V2 centroid
[260]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
[305]779            x0, y0 = centroid_coordinates[k0] #V0 centroid
780            x1, y1 = centroid_coordinates[k1] #V1 centroid       
[260]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
[245]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
[259]865       
[262]866    from quantity_ext import limit, compute_gradients,\
[272]867    extrapolate_second_order, interpolate_from_vertices_to_edges, update
[265]868
Note: See TracBrowser for help on using the repository browser.