Hierarchic data structure for unstructured 3D triangular mesh - CiteSeerX

0 downloads 0 Views 219KB Size Report
implementations of this hierarchic data structure and studies have shown that the ... implementation is for a general unstructured triangular mesh, it can be adapted for a ..... xyz. The coordinates of the nodal position. public list. pEdge.
Hierarchic data structure for unstructured 3D triangular mesh: An object-oriented implementation by Y. Sua & A. Senthil Kumarb aAdvanced

Computing Programme, Institute of High Performance Computing

bDepartment

of Mechanical Engineering, National University of Singapore

URL: http://docs.ihpc.a-star.edu.sg This work is licensed under a Attribution-NonCommercial-ShareAlike 2.5 License For more information, please visit the Creative Commons site at http://creativommons.org/licenses/by-nc-sa/2.5/

1

Hierarchic data structure for unstructured 3D triangular mesh: An objectoriented implementation

Y. Sua,∗, A. Senthil Kumarb aInstitute

of High Performance Computing, 1 Science Park Road 01-01, The Capricorn, Singapore Science Park II, Singapore 117528, Singapore. [email protected]

bDepartment

of Mechanical Engineering, National University of Singapore, Singapore 119260, Singapore. [email protected]

Abstract This paper describes a hierarchic data structure to represent an unstructured triangular mesh. This data structure is suited for fast upward and downward retrieval of adjacency information, and hence, is useful for mesh modification algorithms like refinement, coarsening and smoothing. A full implementation of a hierarchic mesh data structure is presented with design consideration prioritized for fast retrieval of adjacency information. The implementation is based on an object-oriented programming (OOP) paradigm using C++ and it utilizes the Standard Template Library (STL) in the design of class containers for adjacency information. The data structure can be easily adapted to cater for volume meshes or to support other element type.

Keywords: Mesh data structure; Unstructured mesh; Triangular mesh; Finite Element, Object-oriented programming

2

1. Introduction

An important consideration in the design of a mesh data structure is how it can efficiently provide information required by the processes that utilizes it. For most automatic adaptive procedures, like mesh refinement, mesh coarsening and mesh smoothing, retrieval of adjacency information is an important component. The adjacency relationship of a mesh refers to how the topological entities are connected to each other, namely, the nodes, edges and faces, in the case of a triangular mesh. Classical mesh data structure consists of representations for element and node entities, with each element storing the pointers to its component nodes. However, this data structure quickly becomes computationally expensive when adjacency queries are required. This makes it inadequate even for moderately complex mesh generation applications [1]. An alternative form of representation consists of topological entities as well as the adjacency relations. The basics of this mesh representation is given by Remacle et al. [2] and Shephard [3]. Topology provides an unambiguous and shape independent abstraction of the mesh. For a triangular mesh, they are the nodes, edges and faces. Each topological entity of dimension n is bounded by a set of topological entities of dimension n-1, and it is also associated with a set of topological entities of dimension n+1. For example, an edge is a 1-D entity with 2 nodes (0-D entities) bounding it and it is in turn associated with a set of faces (2-D entities). Such two-way adjacencies form the basic structure of a hierarchic topological relationship. While not all adjacency relations are stored implicitly, they can be derived at the expense of some

3

computational cost. As the order of the required adjacency information increases, the computational cost increases in a non-linear manner. Beall et al. [4] has presented three implementations of this hierarchic data structure and studies have shown that the hierarchic representation does not necessarily add a significant amount of extra storage. Garimella [1] has also made a detailed study on the requirements of storage compactness and computational efficiency on 10 different hierarchic mesh data structure. The storage compactness was estimated based on assumptions on the number of expected adjacency entities and the computational efficiency was calculated based on the cost of invoking an operator and the frequency of calling such an operator. Waltz [5] has also presented a derived data structure whereby topological information are inferred from rudimentary topological entities. While there are discussions on the issue of mesh data structures, there have been very few publications that focuses on the implementation of data structures. This paper presents a full implementation of a hierarchic mesh data structure in C++ with the source code provided. It details the design of a full data structure where every topological entity is represented. Design considerations are formulated with fast retrieval of adjacency relations as the foremost criteria. The adjacency relations are stored to the second-order both for upward and downward queries. Even though the implementation is for a general unstructured triangular mesh, it can be adapted for a 3D mesh using the same programming paradigm. The data structure contains topology, geometric and adjacency information, and arbitrary data can be easily added to cater for application specific algorithms.

4

The paper is organized as follows. Section 2 focuses on the common operations required in adaptive mesh applications. Section 3 discusses the design consideration for the mesh data structure. Section 4 provides a detailed explanation of the implementation and description of the intrinsic behaviour of the creation and deletion of topological entities. Section 5 includes a discussion of the algorithm, its possible expansion and issues relating to memory storage, and Section 6 concludes the paper.

2. Requirements of mesh modification algorithms

Mesh modification algorithms alter the mesh by either changing the geometric properties of the topological entities (e.g. smoothing) or by changing the adjacency relations between the topological entities (e.g. edge swapping). This section focuses on the various requirements of mesh modification procedures, which will form the design considerations for the mesh data structure. Three typical mesh modification algorithms will be studied, namely mesh smoothing [6-8], mesh refinement [9-11] and mesh coarsening [12-14].

2.1 Mesh smoothing

The process of improving the shape of existing elements in a mesh is known as smoothing. Essentially, smoothing improves the quality of a mesh by relocating the internal nodes to a “better” position. The Laplacian smoothing method is the most common mesh smoothing algorithm employed by mesh generation codes. The aim of

5

the smoothing operation is to relocate every movable node of the mesh so that the elements are as close to an equilateral as possible. It does so by relocating every movable node to a position, which is the average of the coordinates of the nodes adjacent to it. The formula [7] is as follows:

(1)

where pi is the new coordinate position vector of i (the node being considered), qi is the new coordinate position vector, Adj(i) is the set of neighbouring nodes directly connected to i by an element edge, Vvar is the set of movable nodes and Vfix is the set of fixed nodes. It is evident that only the geometric locations of the nodes are altered while the topological relations amongst the entities are not modified. The required topological query is the neighbouring nodes which are connected to the node in consideration. This is essentially a two-part operation which involves (a)

querying the edges that are connected to the node, which is an upward adjacency query, and

(b)

querying the opposite node which shares each of these edges, which is a downward adjacency query.

6

2.2 Mesh refinement

Adaptive finite element techniques require that the mesh be modified in a way such that the size of the elements satisfies the condition dictated by the error indicator. This requirement of solution-adaptivity can be achieved by locally refining the mesh. Mesh refinement algorithms can be classified into three main categories: •

Template based refinement



Edge bisection



Node insertion

To ensure validity of the mesh, refinement procedures must be performed such that the adjacent faces are conforming. The required topological query is, therefore, the adjacent face (or faces, in the case of a non-manifold surface mesh) which is connected to the face being split. This is essentially a two-part operation which involves (c)

querying the edges that are associated with the face, which is a downward adjacency query, and

(d)

querying the opposite face which shares each of these edges, which is an upward adjacency query.

Additional operations involved updating the adjacency relations of all the associated topological entities in the process of deletion and creation of new faces.

2.3 Mesh coarsening

7

Typical mesh coarsening algorithms are based on the incremental deletion of the nodes via an edge collapsing procedure. The advantage of this approach is that it will always result in a valid coarse mesh, even though not all unwanted nodes are removed at times [12]. This is due to the constraint of maintaining reasonable aspect ratio of the resulting elements. The edge collapsing procedure removes a node by shrinking one of its edges to zero length, as shown in Fig. 1. Node 6 is to be deleted by collapsing edge 64. This is done by moving node 6 along edge 64 until it coincides with node 4. As a result, the edge 64 and the elements represented by Δ456 and Δ346 are removed. 1

1

5

5 4

4

8

8

6

7

2

3

7

2

3

Fig. 1. Node deletion based on edge collapsing.

To support the edge collapsing process, the following topological queries are required: (a)

Querying the nodes that are associated with the edge to be collapsed, which is a downward adjacency query.

(b)

Querying the faces, which are associated with this edge, which is an upward adjacency query.

(c)

Querying the faces which are associated with node 6, which is also an upward adjacency query, but of a second-order. This query is needed to

8

calculate the expected change in element shape quality in the edge collapsing process. Additional operations involved updating the adjacency relations of all the associated topological entities in the process of deletion of faces and merging of nodes.

3. Design of mesh data structure

An obvious requirement of an efficient mesh data structure is that it must be able to represent all topological entities, either explicitly or implicitly. In the case of the latter, topological entities that are not stored directly must be derived in a computationally efficient manner. The trade-off is between the storage space required and the time to access various adjacency information. A comparison was made by Beall et al [4] on the size of a data structure based on the classic element-node connectivity (implicit representation) to the hierarchic representation (explicit representation). The study revealed that the hierarchic data structure does not necessarily take significantly more storage space than a classical data structure, as shown in Fig 2(a). Besides representing topological entities, the data structure must also be capable of storing, or retrieving geometric properties of the topological entities. These geometric properties are usually related to the dimension of the topological entity in consideration. Some geometric information of interest are the coordinates of a node, the length of an edge and the area of a face. In addition, the data structure must be able to incorporate arbitrary data with each topological entity to ensure flexibility.

9

The data structure must also provide an efficient retrieval of adjacency relationships without having to traverse the whole data structure. For adaptive procedures, first-order adjacency relations are usually sufficient. Higher-order adjacency relations can be derived from the first-order relations, though at the expense of computational time. However, storing all orders of relations can be memory intensive. In this implementation, the first and second-order relations are stored for fast retrieval of upward and downward adjacency relationships. The connectivity relations are illustrated in Fig. 2(b).

Face

Face 3

3

6

6

2

2 Node

3

Edge 6 Node

Fig. 2. Mesh representation showing (a) classical data structure and (b) hierarchical data structure with two levels of adjacency relationships.

Based on the considerations above, it is natural to implement the data structure in the object-oriented computing paradigm. By implementing the data structure as classes, the following design considerations can be addressed: (a)

Topological entities are efficiently represented as objects of classes.

(b)

Geometric properties can be stored as class member variables (e.g. coordinates of nodes) or they can be calculated based on the class member functions (e.g. normal vector of a face).

10

(c)

Adjacency relationships can be stored as class member variables and topological queries can be made through class member variables or even overloading operators.

(d)

The data structure can be easily modified to incorporate arbitrary data, like scalar or vector field.

(e)

Class constructors ensure that intrinsic values of the topological entities are handled automatically and transparently. This minimizes the book-keeping required in the process of object creation and deletion.

(f)

Class destructors ensure that corresponding lower-order topological entities are deleted when they are no longer associated with any higher-order entities. This allows the data base to be always free of “orphaned” entities, thus preventing memory leakages.

4. Implementation of data structure

The choice of programming language used in the implementation of the mesh data structure is C++. This section describes in detail the components of the mesh objects, namely, the member variables and functions, the constructors and destructors, and the overloaded operators. Note that the storage for adjacency relations are implemented using the list container in the C++ Standard Template Library [15]. A container class describes an object that holds other objects. They are building blocks used to create object-oriented programs and they make the internals of a program much easier to

11

construct. A feature of storing upward adjacencies is that the size is a variable. Utilizing STL in the implementation of adjacency storage makes the program easy to manage. The choice of the STL containers is an important consideration in the design of the data structure. In this work, we prefer the sequence containers (vector, deque and list)

over the associative containers (map, multimap, set and multiset). Even though

it is possible to design the adjacency containers using associative containers, the over head tends to be higher in terms of memory storage. For example, an estimated perelement additional heap space required for the set container is 12 bytes while that for the list container is 8 bytes. The reason for the difference in overhead is that the associative containers need additional memory to store the associated keys for each element. The sequence containers are very similar in their basic functionality, that is, they all hold linear sequences, but different in the cost of their operations. A vector container has very low overhead and allows rapid random access to its elements. However, it is expensive to insert or remove an element in the middle of the sequence, and it is also expensive when allocation needs to be made for additional storage. On the other hand, a deque

allows random access that is almost as efficient as a vector container, but it

performs significantly faster when new storage needs to be allocated. The list container can perform insertion or removal of an element at any position in the sequence but operations entailing moving and changing the positions of its elements tend to be costly. Table 1 shows the estimated operation complexity of the STL containers. For the purpose of the mesh data structure, the choice for adjacency storage is the list

container. The reason is because it allows for very efficient insertion (which

12

happens when a new finite element entity is created) and removal (which happens when a mesh class destructor is invoked) of adjacency relationships while incurring reasonable overhead. The key operations encountered during the mesh creation and modification processes are insertion, removal and searching of adjacency relationships. Insertion can take place at the end of the sequence, so the operation complexity is almost comparable for all 3 containers. However, it is important that provision is made for efficient removal of adjacency relationships from any position in the sequence, of which the list container out performs the rest. The efficiency for search operations is also similar for these containers. Based on the considerations above, the list container is selected for the purpose of mesh adjacency storage.

Table 1. Complexity analysis of STL containers. Container

Overhead

Iterators

Insert

Erase

Find

list

8

Bidirectional

12

Random

vector

0

Random

set

12

Bidirectional

amortized constant amortized constant at begin or end; else N amortized constant at end; else N log N

N

deque

amortized constant amortized constant at begin or end; else N/2 amortized constant at end; else N log N

multiset

12

Bidirectional

log N

d log (N+d)

log N

map

16

Bidirectional

log N

log N

log N

multimap

16

Bidirectional

log N

d log (N+d)

log N

4.1

Node Class API

Member parameters The Node class data members are summarized in Table 2.

13

N

N log N

Table 2. Data members of Node class. Scope

Type

Name

Description

public

int

id

The id of the node.

public

double[3]

xyz

The coordinates of the nodal position.

public

list

pEdge

public

list

pFace

private

static int

total_count

The list of Edge pointers to all the edges that are connected to this node. The list of Face pointers that are connected to this node. Total number of existing nodes.

private

static int

max_id

The maximum node id.

private

static list

NodeList

The list of Node pointers to all the existing nodes.

Constructor The following takes place when the class constructor Node N( double pos[3] ) is called: •

A node with xyz = pos is created.



The id of the node is set to be max_id + 1.



The max_id and total_count is incremented by 1.



The pointer to N is added to NodeList.

Destructor The following takes place when the class destructor ~Node() is called: •

If there are still edges attached to N, i.e. pEdge is not empty or pEdge.size() = 0, then the destructor will issue a warning before deletion.



If there are still faces attached to N, i.e. pFace is not empty or pFace.size() = 0, then the destructor will issue a warning before deletion.



The value of total_count is decremented by 1.



The pointer to N is removed from NodeList.

14

Member Functions The following describes the various member functions in Node class: •

void addEdge( Edge* ptr_edge ). N.addEdge( ptr_edge ) will add ptr_edge (pointer to an edge) to the list of connected edges pEdge if the

pointer to that edge does not already exist in the list. •

void addFace( Face* ptr_face ). N.addFace( ptr_face ) will add ptr_face (the pointer to a face) to the list of connected faces pFace if the

pointer to that face does not already exist in the list. •

static int getNodeCount(). N.getNodeCount() returns the total

number of nodes in existence. •

static int getMaxId(). N.getMaxId() returns the maximum id of all the

nodes. •

static void setMaxId( int iVal ). N.setMaxId( iVal ) sets the

maximum node id to iVal. •

static list getNodeList(). N.getNodeList() returns the

list of nodes in existence.

4.2

Edge Class API

Member parameters The Edge class data members are summarized in Table 3.

Table 3. Data members of Edge class.

15

Scope

Type

Name

Description

public

int

id

The id of the edge.

public

Node*[2]

pNode

Pointers to the nodes of the edge.

public

list

pFace

private

static int

total_count

The list of Face pointers that are connected to this edge. Total number of existing edges.

private

static int

max_id

The maximum edge id.

private

static list

EdgeList

The list of Edge pointers to all the existing edges.

Constructor The following takes place when the class constructor Edge E( Node* pN1, Node* pN2 ) is called:



An edge with pNode[0] = pN1 and pNode[1] = pN2 is created.



The id is set to be max_id + 1.



The max_id and total_count is incremented by 1.



The pointer to E is added to EdgeList.



The member function pNode[0]->addEdge( &E ) is called so that the pointer to E is added to the list pNode[0]->pEdge (i.e. list of pointers to class object Edge).



The member function pNode[1]->addEdge( &E ) is called so that the pointer to E is added to the list pNode[1]->pEdge (i.e. list of pointers to class object Edge).

Destructor The following takes place when the class destructor ~Edge() is called: •

The value of total_count is decremented by 1.



The pointer to E is removed from EdgeList.

16



The pointer to E is removed from the list pNode[0]->pEdge (i.e. list of pointers to class object Edge). If the list pNode[0]->pEdge is empty after the removal, then the node pointed to by pNode[0] is deleted. This is to prevent the occurrence of orphan nodes which will result in undesirable memory leakage.



The pointer to E is removed from the list pNode[1]->pEdge (i.e. list of pointers to class object Edge). If the list pNode[1]->pEdge is empty after the removal, then the node pointed to by pNode[1] is deleted. This is to prevent the occurrence of orphan nodes which will result in undesirable memory leakage.

Overloaded Operator The “==” operator is overloaded in the Edge class. The operation E1 == E2 returns TRUE if edges E1 and E2 has the same Node pair.

Member Functions The following describes the various member functions in Edge class: •

static int getEdgeCount(). E.getEdgeCount() returns the total

number of edges in existence. •

static int getMaxId(). E.getMaxId() returns the maximum id of all the

edges. •

static list getEdgeList(). E.getEdgeList() returns the list

of edges in existence. •

double length(). E.length() returns the length of the edge.

17

4.3

Face Class API

Member parameters The Face class data members are summarized in Table 4. Table 4. Data members of Face class. Scope

Type

Name

Description

public

int

id

The id of the face.

public

Node*[3]

pNode

public

Edge*[3]

pEdge

Array of Node pointers to the nodes of the face. Array of Edge pointers to the edges of the face.

private

static int

total_count

Total number of existing faces.

private

static int

max_id

The maximum face id.

private

static list

FaceList

The list of Face pointers to all the existing faces.

Constructor Node[2]

Edge[2]

Edge[1] F

Node[0]

Edge[0]

Node[1]

Fig. 3. Topology of a Linear Triangular Element.

The topology of a linear triangular element is based on the convention shown in Fig. 3. The following takes place when the class constructor Face F( Node* pN1, Node* pN2, Node* pN3 ) is called:

18



A face with pNode[0] = pN1, pNode[1] = pN2 and pNode[2] = pN3 is created.



The id is set to be max_id + 1.



The max_id and total_count is incremented by 1.



The pointer to F is added to FaceList.



The member function pNode[0]->addFace( &F ) is called so that the pointer to F is added to the list pNode[0]->pFace (i.e. list of pointers to class object Face).



The member function pNode[1]->addFace( &F ) is called so that the pointer to F is added to the list pNode[1]->pFace (i.e. list of pointers to class object Face).



The member function pNode[2]->addFace( &F ) is called so that the pointer to F is added to the list pNode[2]->pFace (i.e. list of pointers to class object Face).



Check if an edge E already exists between nodes N1 and N2 by calling pE = fem_get_nodes_common_edge( pN1, pN2 ). If the edge already exists,

i.e. pE != NULL, then add the pointer to F to the list pE->pFace (i.e. list of pointers to class object Face) and set pEdge[0] = pE. If edge does not already exists, then create a class object Edge E1( pN1, pN2 ) and add the pointer to F to the list pE1->pFace and set pEdge[0] = pE1.



Check if an edge E already exist between nodes N2 and N3 by calling pE = fem_get_nodes_common_edge( pN2, pN3 )*. If the edge already exists,

19

i.e. pE != NULL, then add the pointer to F to the list pE->pFace (i.e. list of pointers to class object Face) and set pEdge[1] = pE. If edge does not already exists, then create a class object Edge E2( pN2, pN3 ) and add the pointer to F to the list pE2->pFace and set pEdge[1] = pE2.



Check if an edge E already exist between nodes N3 and N1 by calling pE = fem_get_nodes_common_edge( pN3, pN1 )*. If the edge already exists,

i.e. pE != NULL, then add the pointer to F to the list pE->pFace (i.e. list of pointers to class object Face) and set pEdge[2] = pE. If edge does not already exists, then create a class object Edge E3( pN3, pN1 ) and add the pointer to F to the list pE3->pFace and set pEdge[2] = pE3.

Destructor The following takes place when the class destructor ~Face() is called: •

The value of total_count is decremented by 1.



The pointer to F is removed from FaceList.



The pointer to F is removed from the list pEdge[0]->pFace (i.e. list of pointers to class object Face). If the list pEdge[0]->pFace is empty after the removal, then the edge pointed to by pEdge[0] is deleted. This is to prevent the occurrence of orphan edges which will result in undesirable memory leakage.



The pointer to F is removed from the list pEdge[1]->pFace (i.e. list of pointers to class object Face). If the list pEdge[1]->pFace is empty after the removal, then the edge pointed to by pEdge[1] is deleted. This is to prevent the occurrence of orphan edges which will result in undesirable memory leakage.

20



The pointer to F is removed from the list pEdge[1]->pFace (i.e. list of pointers to class object Face). If the list pEdge[1]->pFace is empty after the removal, then the edge pointed to by pEdge[1] is deleted. This is to prevent the occurrence of orphan edges which will result in undesirable memory leakage.

Member Functions The following describes the various member functions in Face class: •

static int getFaceCount(). F.getFaceCount() returns the total

number of faces in existence. •

static int getMaxId(). F.getMaxId() returns the maximum id of all

the faces. •

static list getFaceList(). F.getFaceList() returns the

list of faces in existence. •

void normal( double nVector[3] ). F.normal( nVector[3] )

calculates the normal vector nVector based on the right-hand convention.

5. Discussion

To evaluate the computational efficiency of the data structure, an infinite triangular mesh with all equilateral faces is assumed. The operation count OC is approximated based on the method proposed by Garimella [1] and is used to quantify the computational requirement for a topological query. As the adjacency relations of the

21

topological entities are stored to the second-order in this implementation, the running time for the following topological queries is constant: •

faces associated with a node (OC = 6),



edges associated with a node (OC = 6),



faces associated with an edge (OC = 2),



nodes forming an edge (OC = 2),



edges forming a face (OC = 3), and



nodes forming a face (OC = 3)

Other derived topological queries are •

neighbours of a node, which has a linear running time proportional to the number of edges associated with the node (OC = 24),



faces surrounding a face, which has a running time proportional to the number of nodes multiplied by the number of faces associated with each node (OC = 147), and



edges on the boundary of the mesh, which has a linear running time proportional to the total number of edges (OC = 4n where n is the total number of edges in the database)

As can be seen, by using a second-order two-way hierarchical representation, most of the required topological queries can be performed very efficiently. While the memory storage requirement for a triangular mesh is relatively low, the storage requirement scales up rapidly in a similar implementation for a tetrahedral mesh. Fig. 4 shows a comparison of the need to store the adjacency relations for a triangular and tetrahedral mesh.

22

Region 4

2

5

6

Face 3 6

Face 2 3

Edge 2

3

23

5

4

Edge

6

35

2

14

Node

Node

Triangular Mesh

Tetrahedral Mesh

3

Fig. 4. Mesh representations of triangular and tetrahedral mesh.

Apparently, to store to the third-order adjacency is not a feasible approach in this case. A good alternative that balances between storage requirement and computational efficiency is shown in Fig. 5 [1]. The implementation in this paper can be easily adapted to represent this tetrahedral mesh data structure. The addition requirement is to represent a higher order topological entity, namely the region, and to design relevant member functions to perform the necessary topological queries.

23

Region 4 5 Face 3 Edge 2

14 Node

Fig. 5. Alternative mesh representation of tetrahedral mesh.

To verify the robustness of the implementation, a few mesh modification algorithms are developed based on the mesh data structure, namely, smoothing, coarsening and refinement. In all cases, the input consists of a triangular mesh of a torus with edges which are two-manifold and faces oriented in the outward-pointing direction. After numerous iterations, the mesh data is checked to verify that it satisfies the EulerPoincaré formula

(2)

where G is the genus (in the case of the torus, G = 1), v is the number of vertices, e is the number of edges and f is the number of faces. In some applications, the input mesh might consist of a set of triangles bounded by an outer loop of free edges. This is common when the mesh represents a bounded

24

surface. In certain operations, faces at the boundary might be deleted, as shown in Fig. 6. Based on the class destructors, the free edge will be deleted according. This frees the user from managing the update of sub-entities associated to the face object. However, in some applications, like an advancing front remeshing algorithm, it is desired to retain the boundary edge because it is to be used by a new triangle. To retain the edge, the user simply needs to create a new edge over the existing boundary edge before the face deletion.

boundary face to be deleted

associated boundary edge

Fig. 6 Deletion of boundary face

6. Conclusions

This paper presents a C++ implementation of a hierarchic data structure to represent an unstructured triangular mesh. This data structure is able to contain information pertaining to topological entities, adjacency relations, geometric parameters, as well as any arbitrary data. The data structure is suited for fast upward and downward retrieval of adjacency information since the connectivities are stored to the second-order. This

25

makes it useful for mesh modification algorithms like refinement, coarsening and smoothing. This representation can be easily modified to cater to other element types. To extend the data structure to represent volumetric meshes, modifications need to be made on the type of connectivities stored in order to maintain a feasible memory storage.

26

References [1]

R. V. Garimella, Mesh Data Structure Selection for Mesh Generation and FEA Applications, Int. J. Numer. Meth. Engng., 55(4) (2002) 451-478.

[2]

J. F. Remacle, M. S. Shephard, An Algorithm Oriented Mesh Database, Int. J. Numer. Meth. Engng., 58(2) (2003) 349-374.

[3]

M. S. Shephard, Meshing Environment for Geometry-Based Analysis, Int. J. Numer. Meth. Engng., 47(1) (2000) 169-190.

[4]

M. W. Beall, M. S. Shephard, A General Topology-Based Mesh Data Structure, Int. J. Numer. Meth. Engng., 40(9) (1997) 1573-1596.

[5]

J. Waltz, Derived Data Structure Algorithms for Unstructured Finite Element Meshes, Int. J. Numer. Meth. Engng., 54(7) (2002) 945-963.

[6]

Y. Ohtake, A. Belyaev, I. Bogaevski, Mesh Regularization and Adaptive Smoothing, Computer-Aided Design, 33(11) (2001) 789-800.

[7]

J. Vollmer, R. Mencl, H. Muller, Improved Laplacian Smoothing of Noisy Surface Meshes, University Dortmund, Research Report No. 711, 1999.

[8]

S. A. Canann, M. B. Stephenson, T. Blacker, Optismoothing: An OptimizationDriven Approach to Mesh Smoothing, Finite Elem. Anal. Des., 13(2-3) (1993) 185-190.

[9]

H. L. De Cougny, M. S. Shephard, Parallel Refinement and Coarsening of Tetrahedral Meshes, Int. J. Numer. Meth. Engng., 46(7) (1999) 1101-1125.

[10] K. C. Chellamuthu, N. Ida, Algorithms and Data Structures for 2D and 3D Adaptive Finite Element Mesh Refinement, Finite Elem. Anal. Des., 17(3) (1994) 205-229.

27

[11] H. Shahnasser, W. Morgan, A. Raghuram, A Dynamic Data Structure Suitable for Adaptive Mesh Refinement in Finite Element Method, Finite Elem. Anal. Des., 4 (3) (1988) 237-247. [12] C. Ollivier-Gooch, Coarsening Unstructured Meshes by Edge Contraction, Int. J. Numer. Meth. Engng., 57(3) (2003) 391-414. [13] N. V. Hattangady, Coarsening of Mesh Models for Representation of Rigid Objects in Finite Element Analysis, Int. J. Numer. Meth. Engng., 44(3) (1999) 313-326. [14] G. L. Miller, D. Talmor, S.-H. Teng, Optimal Coarsening of Unstructured Meshes, Journal of Algorithms, 31(1) (1999) 29-65. [15] Dinkum C++ Library Reference Manual, DinkumWare, Ltd., http:// www.dinkumware.com/refxcpp.html.

28