source: inundation/ga/storm_surge/pyvolution/general_mesh.py @ 1140

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

Added test for conservation of stage with various beds

File size: 8.4 KB
Line 
1   
2class General_mesh:
3    """Collection of triangular elements (purely geometric)
4
5    A triangular element is defined in terms of three vertex ids,
6    ordered counter clock-wise,
7    each corresponding to a given coordinate set.
8    Vertices from different elements can point to the same
9    coordinate set.
10
11    Coordinate sets are implemented as an N x 2 Numeric array containing
12    x and y coordinates.
13   
14
15    To instantiate:
16       Mesh(coordinates, triangles)
17
18    where
19
20      coordinates is either a list of 2-tuples or an Mx2 Numeric array of
21      floats representing all x, y coordinates in the mesh.
22
23      triangles is either a list of 3-tuples or an Nx3 Numeric array of
24      integers representing indices of all vertices in the mesh.
25      Each vertex is identified by its index i in [0, M-1].
26
27
28    Example:
29        a = [0.0, 0.0]
30        b = [0.0, 2.0]
31        c = [2.0,0.0]
32        e = [2.0, 2.0]
33       
34        points = [a, b, c, e]
35        triangles = [ [1,0,2], [1,2,3] ]   #bac, bce
36        mesh = Mesh(points, triangles)
37   
38        #creates two triangles: bac and bce
39
40    Other:
41   
42      In addition mesh computes an Nx6 array called vertex_coordinates.
43      This structure is derived from coordinates and contains for each
44      triangle the three x,y coordinates at the vertices.
45                               
46
47        This is a cut down version of mesh from pyvolution\mesh.py
48    """
49
50    def __init__(self, coordinates, triangles, origin = None):
51        """
52        Build triangles from x,y coordinates (sequence of 2-tuples or
53        Mx2 Numeric array of floats) and triangles (sequence of 3-tuples
54        or Nx3 Numeric array of non-negative integers).
55
56        origin is a 3-tuple consisting of UTM zone, easting and northing.
57        If specified coordinates are assumed to be relative to this origin.
58        """
59
60        from Numeric import array, zeros, Int, Float, maximum, sqrt, sum
61
62        self.triangles = array(triangles).astype(Int)
63        self.coordinates = array(coordinates)
64
65        self.origin = origin
66
67        #Input checks
68        msg = 'Triangles must an Nx2 Numeric array or a sequence of 2-tuples'
69        assert len(self.triangles.shape) == 2, msg
70
71        msg = 'Coordinates must an Mx2 Numeric array or a sequence of 2-tuples'
72        assert len(self.coordinates.shape) == 2, msg       
73
74        msg = 'Vertex indices reference non-existing coordinate sets'
75        assert max(max(self.triangles)) <= self.coordinates.shape[0], msg
76
77
78        #Register number of elements (N)
79        self.number_of_elements = N = self.triangles.shape[0]
80   
81        #Allocate space for geometric quantities       
82        self.normals = zeros((N, 6), Float)
83        self.areas = zeros(N, Float)
84        self.edgelengths = zeros((N, 3), Float)
85
86        #Get x,y coordinates for all triangles and store
87        self.vertex_coordinates = V = self.compute_vertex_coordinates()
88       
89        #Initialise each triangle
90        for i in range(N):
91            #if i % (N/10) == 0: print '(%d/%d)' %(i, N)
92           
93            x0 = V[i, 0]; y0 = V[i, 1]
94            x1 = V[i, 2]; y1 = V[i, 3]
95            x2 = V[i, 4]; y2 = V[i, 5]           
96
97            #Area
98            self.areas[i] = abs((x1*y0-x0*y1)+(x2*y1-x1*y2)+(x0*y2-x2*y0))/2
99
100            msg = 'Triangle (%f,%f), (%f,%f), (%f, %f)' %(x0,y0,x1,y1,x2,y2)
101            msg += ' is degenerate:  area == %f' %self.areas[i]
102            assert self.areas[i] > 0.0, msg
103       
104
105            #Normals
106            #The normal vectors
107            # - point outward from each edge
108            # - are orthogonal to the edge
109            # - have unit length
110            # - Are enumerated according to the opposite corner:
111            #   (First normal is associated with the edge opposite
112            #    the first vertex, etc)
113            # - Stored as six floats n0x,n0y,n1x,n1y,n2x,n2y per triangle
114
115            n0 = array([x2 - x1, y2 - y1])
116            l0 = sqrt(sum(n0**2))
117
118            n1 = array([x0 - x2, y0 - y2])
119            l1 = sqrt(sum(n1**2))
120
121            n2 = array([x1 - x0, y1 - y0])
122            l2 = sqrt(sum(n2**2))
123
124            #Normalise
125            n0 /= l0
126            n1 /= l1
127            n2 /= l2
128
129            #Compute and store
130            self.normals[i, :] = [n0[1], -n0[0],
131                                  n1[1], -n1[0],
132                                  n2[1], -n2[0]]
133           
134            #Edgelengths
135            self.edgelengths[i, :] = [l0, l1, l2]
136
137        #Build vertex list
138        self.build_vertexlist()       
139           
140    def __len__(self):
141        return self.number_of_elements
142
143    def __repr__(self):
144        return 'Mesh: %d triangles, %d elements'\
145               %(self.coordinates.shape[0], len(self))
146
147    def get_normals(self):
148        """Return all normal vectors.
149        Return normal vectors for all triangles as an Nx6 array
150        (ordered as x0, y0, x1, y1, x2, y2 for each triangle)       
151        """
152        return self.normals
153
154
155    def get_normal(self, i, j):
156        """Return normal vector j of the i'th triangle.
157        Return value is the numeric array slice [x, y]
158        """
159        return self.normals[i, 2*j:2*j+2]     
160   
161
162           
163    def get_vertex_coordinates(self):
164        """Return all vertex coordinates.
165        Return all vertex coordinates for all triangles as an Nx6 array
166        (ordered as x0, y0, x1, y1, x2, y2 for each triangle)       
167        """
168        return self.vertex_coordinates
169
170
171    def get_vertex_coordinate(self, i, j):
172        """Return coordinates for vertex j of the i'th triangle.
173        Return value is the numeric array slice [x, y]
174        """
175        return self.vertex_coordinates[i, 2*j:2*j+2]     
176
177
178    def compute_vertex_coordinates(self):
179        """Return vertex coordinates for all triangles as an Nx6 array
180        (ordered as x0, y0, x1, y1, x2, y2 for each triangle)
181        """
182
183        #FIXME: Perhaps they should be ordered as in obj files??
184        #See quantity.get_vertex_values
185       
186        from Numeric import zeros, Float
187
188        N = self.number_of_elements
189        vertex_coordinates = zeros((N, 6), Float) 
190
191        for i in range(N):
192            for j in range(3):
193                k = self.triangles[i,j]  #Index of vertex 0
194                v_k = self.coordinates[k]
195                vertex_coordinates[i, 2*j+0] = v_k[0]
196                vertex_coordinates[i, 2*j+1] = v_k[1]
197
198        return vertex_coordinates
199   
200    def get_vertices(self,  indexes=None):
201        """Get connectivity
202        indexes is the set of element ids of interest
203        """
204       
205        from Numeric import take
206       
207        if (indexes ==  None):
208            indexes = range(len(self))  #len(self)=number of elements
209           
210        return  take(self.triangles, indexes)
211
212    def get_unique_vertices(self,  indexes=None):
213        triangles = self.get_vertices(indexes=indexes)
214        unique_verts = {}
215        for triangle in triangles:
216           unique_verts[triangle[0]] = 0 
217           unique_verts[triangle[1]] = 0 
218           unique_verts[triangle[2]] = 0
219        return unique_verts.keys()   
220       
221    def build_vertexlist(self):
222        """Build vertexlist index by vertex ids and for each entry (point id)
223        build a list of (triangles, vertex_id) pairs that use the point
224        as vertex.
225
226        Preconditions:
227          self.coordinates and self.triangles are defined 
228       
229        Postcondition:
230          self.vertexlist is built
231        """
232
233        vertexlist = [None]*len(self.coordinates)
234        for i in range(self.number_of_elements):
235
236            a = self.triangles[i, 0]
237            b = self.triangles[i, 1]
238            c = self.triangles[i, 2]           
239
240            #Register the vertices v as lists of
241            #(triangle_id, vertex_id) tuples associated with them
242            #This is used for smoothing
243            for vertex_id, v in enumerate([a,b,c]):
244                if vertexlist[v] is None:
245                    vertexlist[v] = []
246
247                vertexlist[v].append( (i, vertex_id) )   
248
249        self.vertexlist = vertexlist
250   
251
252    def get_extent(self):
253        """Return min and max of all x and y coordinates
254        """
255
256
257        C = self.get_vertex_coordinates()
258        X = C[:,0:6:2].copy()
259        Y = C[:,1:6:2].copy()
260       
261        xmin = min(X.flat)
262        xmax = max(X.flat)
263        ymin = min(Y.flat)
264        ymax = max(Y.flat)               
265       
266        return xmin, xmax, ymin, ymax
267
268
269    def get_area(self):
270        """Return total area of mesh
271        """
272
273        return sum(self.areas)         
274       
Note: See TracBrowser for help on using the repository browser.