source: inundation/pyvolution/quantity.py @ 1740

Last change on this file since 1740 was 1697, checked in by steve, 19 years ago

Found problem with File_Boundary as used in validation test LWRU2. Have setup new BC Transmissive_Momentum_Set _Stage

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