source: inundation/pyvolution/general_mesh.py @ 2852

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

Domain from meshfile, set storage of unique vertex values etc

File size: 10.1 KB
Line 
1
2from coordinate_transforms.geo_reference import Geo_reference
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    #FIXME: It would be a good idea to use geospatial data as an alternative
53    #input
54    def __init__(self, coordinates, triangles,
55                 geo_reference=None):
56        """
57        Build triangles from x,y coordinates (sequence of 2-tuples or
58        Mx2 Numeric array of floats) and triangles (sequence of 3-tuples
59        or Nx3 Numeric array of non-negative integers).
60
61        origin is a 3-tuple consisting of UTM zone, easting and northing.
62        If specified coordinates are assumed to be relative to this origin.
63        """
64
65        from Numeric import array, zeros, Int, Float, sqrt, sum
66
67        self.triangles = array(triangles,Int)
68        self.coordinates = array(coordinates,Float)
69       
70
71        # FIXME: this stores a geo_reference, but when coords are returned
72        # This geo_ref is not taken into account!
73        if geo_reference is None:
74            self.geo_reference = Geo_reference() #Use defaults
75        else:
76            self.geo_reference = geo_reference
77
78        #Input checks
79        msg = 'Triangles must an Nx3 Numeric array or a sequence of 3-tuples'
80        assert len(self.triangles.shape) == 2, msg
81
82        msg = 'Coordinates must an Mx2 Numeric array or a sequence of 2-tuples'
83        assert len(self.coordinates.shape) == 2, msg
84
85        msg = 'Vertex indices reference non-existing coordinate sets'
86        assert max(max(self.triangles)) <= self.coordinates.shape[0], msg
87
88
89        #Register number of elements (N)
90        self.number_of_elements = N = self.triangles.shape[0]
91
92        #FIXME: Maybe move to statistics?
93
94        xy_extent = [ min(self.coordinates[:,0]), min(self.coordinates[:,1]) ,
95                      max(self.coordinates[:,0]), max(self.coordinates[:,1]) ]
96
97        self.xy_extent = array(xy_extent, Float)
98
99
100        #Allocate space for geometric quantities
101        self.normals = zeros((N, 6), Float)
102        self.areas = zeros(N, Float)
103        self.edgelengths = zeros((N, 3), Float)
104
105        #Get x,y coordinates for all triangles and store
106        self.vertex_coordinates = V = self.compute_vertex_coordinates()
107
108
109        #Initialise each triangle
110        for i in range(N):
111            #if i % (N/10) == 0: print '(%d/%d)' %(i, N)
112
113            x0 = V[i, 0]; y0 = V[i, 1]
114            x1 = V[i, 2]; y1 = V[i, 3]
115            x2 = V[i, 4]; y2 = V[i, 5]
116
117            #Area
118            self.areas[i] = abs((x1*y0-x0*y1)+(x2*y1-x1*y2)+(x0*y2-x2*y0))/2
119
120            msg = 'Triangle (%f,%f), (%f,%f), (%f, %f)' %(x0,y0,x1,y1,x2,y2)
121            msg += ' is degenerate:  area == %f' %self.areas[i]
122            assert self.areas[i] > 0.0, msg
123
124
125            #Normals
126            #The normal vectors
127            # - point outward from each edge
128            # - are orthogonal to the edge
129            # - have unit length
130            # - Are enumerated according to the opposite corner:
131            #   (First normal is associated with the edge opposite
132            #    the first vertex, etc)
133            # - Stored as six floats n0x,n0y,n1x,n1y,n2x,n2y per triangle
134
135            n0 = array([x2 - x1, y2 - y1])
136            l0 = sqrt(sum(n0**2))
137
138            n1 = array([x0 - x2, y0 - y2])
139            l1 = sqrt(sum(n1**2))
140
141            n2 = array([x1 - x0, y1 - y0])
142            l2 = sqrt(sum(n2**2))
143
144            #Normalise
145            n0 /= l0
146            n1 /= l1
147            n2 /= l2
148
149            #Compute and store
150            self.normals[i, :] = [n0[1], -n0[0],
151                                  n1[1], -n1[0],
152                                  n2[1], -n2[0]]
153
154            #Edgelengths
155            self.edgelengths[i, :] = [l0, l1, l2]
156
157       
158        #Build vertex list
159        self.build_vertexlist()
160
161           
162
163    def __len__(self):
164        return self.number_of_elements
165
166    def __repr__(self):
167        return 'Mesh: %d triangles, %d elements'\
168               %(self.coordinates.shape[0], len(self))
169
170    def get_normals(self):
171        """Return all normal vectors.
172        Return normal vectors for all triangles as an Nx6 array
173        (ordered as x0, y0, x1, y1, x2, y2 for each triangle)
174        """
175        return self.normals
176
177
178    def get_normal(self, i, j):
179        """Return normal vector j of the i'th triangle.
180        Return value is the numeric array slice [x, y]
181        """
182        return self.normals[i, 2*j:2*j+2]
183
184
185
186    def get_vertex_coordinates(self, obj = False):
187        """Return all vertex coordinates.
188        Return all vertex coordinates for all triangles as an Nx6 array
189        (ordered as x0, y0, x1, y1, x2, y2 for each triangle)
190
191        if obj is True, the x/y pairs are returned in a 3*N x 2 array.
192        FIXME, we might make that the default.
193        FIXME Maybe use keyword: continuous = False for this condition?
194        FIXME - Maybe use something referring to unique vertices?
195
196       
197        """
198
199        if obj is True:
200            from Numeric import concatenate, reshape
201            V = self.vertex_coordinates
202            #return concatenate( (V[:,0:2], V[:,2:4], V[:,4:6]), axis=0)
203
204            N = V.shape[0]
205            return reshape(V, (3*N, 2))
206        else:   
207            return self.vertex_coordinates
208
209
210    def get_vertex_coordinate(self, i, j):
211        """Return coordinates for vertex j of the i'th triangle.
212        Return value is the numeric array slice [x, y]
213        """
214        return self.vertex_coordinates[i, 2*j:2*j+2]
215
216
217    def compute_vertex_coordinates(self):
218        """Return vertex coordinates for all triangles as an Nx6 array
219        (ordered as x0, y0, x1, y1, x2, y2 for each triangle)
220        """
221
222        #FIXME (Ole): Perhaps they should be ordered as in obj files??
223        #See quantity.get_vertex_values
224        #FIXME (Ole) - oh yes they should
225
226        from Numeric import zeros, Float
227
228        N = self.number_of_elements
229        vertex_coordinates = zeros((N, 6), Float)
230
231        for i in range(N):
232            for j in range(3):
233                k = self.triangles[i,j]  #Index of vertex 0
234                v_k = self.coordinates[k]
235                vertex_coordinates[i, 2*j+0] = v_k[0]
236                vertex_coordinates[i, 2*j+1] = v_k[1]
237
238        return vertex_coordinates
239
240    def get_vertices(self, indices=None):
241        """Get connectivity
242        indices is the set of element ids of interest
243        """
244
245        from Numeric import take
246
247        if (indices ==  None):
248            indices = range(len(self))  #len(self)=number of elements
249
250        return  take(self.triangles, indices)
251
252    #FIXME - merge these two (get_vertices and get_triangles)
253    def get_triangles(self, obj = False):
254        """Get connetivity
255        Return triangles (triplets of indices into point coordinates)
256       
257        If obj is True return structure commensurate with replicated
258        points, allowing for discontinuities
259        (FIXME: Need good name for this concept)
260        """
261
262        if obj is True:
263            from Numeric import array, reshape, Int       
264            m = len(self)  #Number of triangles
265            M = 3*m        #Total number of unique vertices
266            T = reshape(array(range(M)).astype(Int), (m,3))
267        else:
268            T = self.triangles
269
270        return T     
271
272   
273
274    def get_unique_vertices(self,  indices=None):
275        triangles = self.get_vertices(indices=indices)
276        unique_verts = {}
277        for triangle in triangles:
278           unique_verts[triangle[0]] = 0
279           unique_verts[triangle[1]] = 0
280           unique_verts[triangle[2]] = 0
281        return unique_verts.keys()
282
283    def build_vertexlist(self):
284        """Build vertexlist index by vertex ids and for each entry (point id)
285        build a list of (triangles, vertex_id) pairs that use the point
286        as vertex.
287
288        Preconditions:
289          self.coordinates and self.triangles are defined
290
291        Postcondition:
292          self.vertexlist is built
293        """
294
295        vertexlist = [None]*len(self.coordinates)
296        for i in range(self.number_of_elements):
297
298            a = self.triangles[i, 0]
299            b = self.triangles[i, 1]
300            c = self.triangles[i, 2]
301
302            #Register the vertices v as lists of
303            #(triangle_id, vertex_id) tuples associated with them
304            #This is used for averaging multiple vertex values.
305            for vertex_id, v in enumerate([a,b,c]):
306                if vertexlist[v] is None:
307                    vertexlist[v] = []
308
309                vertexlist[v].append( (i, vertex_id) )
310
311        self.vertexlist = vertexlist
312
313
314    def get_extent(self):
315        """Return min and max of all x and y coordinates
316        """
317
318
319        C = self.get_vertex_coordinates()
320        X = C[:,0:6:2].copy()
321        Y = C[:,1:6:2].copy()
322
323        xmin = min(X.flat)
324        xmax = max(X.flat)
325        ymin = min(Y.flat)
326        ymax = max(Y.flat)
327
328        return xmin, xmax, ymin, ymax
329
330
331    def get_area(self):
332        """Return total area of mesh
333        """
334
335        return sum(self.areas)
336
337       
Note: See TracBrowser for help on using the repository browser.