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

Last change on this file since 1507 was 1387, checked in by steve, 20 years ago

Need to look at uint test for inscribed circle

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