Recent developments in Finite Element programming

1 downloads 15 Views 91KB Size Report
In C++, template metaprogramming is Turing-complete2, meaning that any .... 4 The name is attributed to the television show 'Monty Python's Flying Circus'. .... Part I: There is Life beyond Files, Microsoft Research, MSR-TR-2005-49, 2005.
First South-East European Conference on Computational Mechanics, SEECCM-06, (M. Kojic, M. Papadrakakis (Eds.)) June 28-30, 2006, Kragujevac, Serbia and Montenegro University of Kragujevac (please leave this part unchanged)

Recent developments in Finite Element programming F. E. Karaoulanis1, C.G. Panagiotopoulos2, E.A. Paraskevopoulos3 1

Department of Civil Engineering Aristotle University of Thessaloniki, Mail Stop 450, GR-54124 Thessaloniki, Greece e-mail: [email protected] Financial support of theGreek State Institute of Scholarships (I.K.Y.) is greatly acknowledged. 2 Department of Civil Engineering Aristotle University of Thessaloniki, Mail Stop 502, GR-54124 Thessaloniki, Greece e-mail: [email protected] 3 Department of Civil Engineering Aristotle University of Thessaloniki, Mail Stop 502, GR-54124 Thessaloniki, Greece e-mail: [email protected]

Abstract This paper presents an overview of computational techniques, recently introduced within the field of Finite Element programming and discusses how these techniques have affected development and have been incorporated in Finite Element codes. Object-oriented architectures which use the concepts of abstraction, encapsulation and inheritance as to exploit flexibility in FE codes; expression templates and template metaprogramming that boosts performance and increase efficiency in time critical procedures without sacrificing elegancy, maintainability and readability of the code; embedded modern scripting languages that increase functionality and extend the capabilities of the provided FE codes; and finally the use of SQL-compliant databases that provides an easy, generic, safe and even remote way of treating pre- and post processed data will be discussed. Examples of FE codes that make use of the above techniques will be given, including commercial and freely available FE codes, as well as a FE code developed by the authors. Key words: Finite Element programming, object-oriented approach, expression templates, template metaprogramming, scripting languages, SQL-compliant databases.

1. Introduction The design and implementation of FE Analysis programs have always tried to fulfill the requirements of flexibility, efficiency, usability and accessibility. Flexibility may be defined in different ways [1]: (1) the minimum of knowledge of the entire program is required to work on a portion of the code, (2) easiness on reusing code, (3) changes within the data structures does not affect the whole program, (4) interdependencies between the components of the design are easy to determine and (5) the integrity of the data structures is assured. Efficiency, defined by means of minimizing the computational time needed for a FE simulation, has been always a desirable; in

fact, flexibility (and even readability and maintainability of the code) is frequently sacrificed in favor of efficiency. Usability refers to the functionality that a FE code provides ‘out of the box’ plus the ability to be easily extended and parameterized. And finally accessibility may be defined as the ability to access and manipulate pre- and post processed data. In this paper an overview of computational aspects that have been introduced in the last 15 or so years is given, and how these techniques have been incorporated or affected FE programming. In Sec. 2 object-oriented design will be discussed and how this technique may be used to provide flexibility in a FE code. In Sec. 3 efficiency issues will be resolved without sacrificing flexibility, by using techniques such as expression templates and template metaprogramming. In Sec. 4 usability issues will be exploited by embedding a modern scripting language within the FE code, and finally in Sec. 5, accessibility issues will be discussed through the use of any modern SQLcompliant database.

2. Object-oriented finite element design 2.1 Background Traditionally, numerical software developers have based their work on procedural languages such as FORTAN or C. The procedures involved used to be packed into several libraries which finally linked together to produce the desired aplicaation. This approach however appears to have innumerous drawbacks in large scale applications like a typical FEA program, such as unmaintability, unreadablity and inflexibility of the code, making further software development a hard task and demanding modifications or reimplementation of large portions of code in case that new components needed to be integrated. The solution to these problems arrived in early 90’s through the object-oriented philosophy, that has been ever since under continuous research within the field of computational mechanics. To this end we distinctively mention early works on static or linear dynamic elasticity [2-6], J2-plasticity [7], template elastoplasticity [8] and matrices and tensors for use in FE codes [9]. An extended survey of recent works in object-oriented FE programming can be found in [10]. 2.2 Concepts Object-oriented programming is yet another programming paradigm, i.e. a paradigmatic style of programming, as opposed to a programming methodology, which is rather a paradigmatic style of handling software engineering. Other well-known programming paradigms are those of structured, procedural, modular, generic, pipeline (as in UNIX command line) and dataflow (as in spreadsheets) programming. The relationship between programming paradigms and programming languages may be a quite complex one, since a programming language can support multiple paradigms. For example, C++ [11] is designed to support elements of procedural programming, modular programming, object-oriented programming and generic programming. Object-oriented programming fulfills three major key concepts, namely that of o o o

Abstraction of the data into objects, usually called classes, which are described by their attributes and their methods and they are capable of performing predefined actions, encapsulation, which isolates the objects and promotes the code reuse, and inheritance of attributes or methods which permits the creation of new objects based on those already defined.

Using the C++ programming language one may also utilize the concept of

o

polymorphism, either ad-hoc by function overloading or parametric, by templates, which describes the ability of a single message to activate different actions when addressed to different objects,

and which yields from the generic programming paradigm1. 2.3. Architectures Several different object-oriented approaches for the design and implementation of the Finite Element Method can be found in the literature, a closer look though reveals that the abstractions made and the classes used can be grouped into four main categories [12]: 1. Domain component classes, i.e. classes used to describe the Initial Boundary Value Problem, as this is spatially descritized by the Finite Element Method. Within this category, objects like node, element, constraint and material can be found. 2. Model component classes, i.e. classes capable of describing the Finite Element Model created during a Finite Element Analysis and responsible for mapping the results of the Analysis to the Domain. Examples of these classes are EliminationModelElement, PenaltyModelElement and LagrangeModelElement, which are objects that describe the mapping of an element into the system of equations (SOE). 3. Analysis component classes, i.e. classes used to perform the analysis of a Finite Element Model. Examples of these classes are FullNewtonRaphsonAlgorithm, ArcLengthControl, SloanReorderer and BandSOE. 4. Numerical classes, such as Matrix, Vector and Tensor classes. While most of the researchers follows the above mentioned design concerning the class abstraction, several different architectures are provided when it comes to Analysis strategies, typically considered as the ‘black box’ in Finite Element codes. Based on a loose sense, the basic steps in a Finite Element Analysis may be stated as follows: 1. 2. 3. 4. 5. 6.

Descritization of the domain into nodes and elements. Formulation of the element matrices and vectors. Formulation of the system of equations. Incorporation of the boundary conditions. Solution of the system of equations. Computation of the response within each element.

The above steps can be followed with minor modifications in any type of analysis, such as static, transient, eigenvalue, push-over, shake-down, reliability, sensitivity etc., and can solve linear and non-linear problems when employing an iterative scheme, such as one of the NewtonRaphson family. The interested reader can find useful comparisons of most of the available strategies in [12], followed by commends on the flexibility and the efficiency that they provide. 2.4. Implementations Despite the obvious advantages of an object-oriented approach of the FEM and the research activity shown in this area within the last decade, most of the known codes still lay on the academic field. According to the authors’ opinion, the most interesting approaches, all of them freely available, are OpenSees [13] and PDD OpenSees [14], FeLyX [15] and ooFem [16]. 1

… and not an object-oriented concept as some authors tend to claim.

2.5. Authors approach The authors’ approach follows closely the abstractions mentioned in Sec. 2.5 and this resulted in a Finite Element code that will be soon released in the public domain, written in C++, consisting of over 110 classes and capable of performing static (evolving load, displacement, arclength control) and transient (generalized-α control) analyses undertaking problems of both geometrical (total Langrangian formulation) and material non-linearities (Von Mises, MohrCoulomb and Tresca yield criteria), letting different ways of building FE models (elimination, penalty and Lagrange multipliers) and providing quality locking-free elements, all of them stored into two main containers, namely Domain and Analysis, with the later shown in Fig. 1.

LagangeImposer

CuthillMcKee

Sloan

EliminationImposer Reoerderer

PenaltyImposer Imposer

AnalysisType

StaticAnalysis

Analysis

ConvergenceNorm

TransientAnalysis

LoadControl

Control

StaticControl

TransientControl

ArcLengthControl

DisplacementControl

Model

SymmSOE

Algorithm

SOE

FullSOE

FullNewtonRaphson

ModNewtonRaphson

BandSOE

Generalized-a aggregation inheritance

Fig. 1. A UML diagram of the Analysis container classes.

3. Efficient programming techniques Matrices, vectors and operations on them are the cornerstone of any scientific program; therefore any attempt to optimize the produced code and to minimize the computational time meets the need to optimize array operations. Innumerous proposals can be found in the literature throughout the computing history, within this section though the focus is set into two rather new techniques, with outstanding results in most of the cases used, namely expression templates and template metaprogramming. Both of them utilize (yet in a different manner) a mechanism that C++ provides, called templates, which allows a type (like int, double, a user defined class a.s.o.) to be a parameter in the definition of a class or a function. In what follows, expression templates will be briefly introduced, while template metaprogramming will be more extensively described, accompanied with parts of code, as implemented by the authors in their FE code. 3.1 Expression templates Passing an expression (like a function) to a function as an argument commonly occurs and it is usually done either by passing the function’s pointer through a callback function (C) or through a function object (C++). The problem is that repeated calls generate a lot of overhead and they slow down pipelined machines, since the function calls force the processor to stall. The technique of expression templates allows expressions to be generated at compile-time, instead of being

passed at run-time. Expressions templates were partially invented by Todd Veldhuizen [17] in 1994 and found almost immediately their way to array specific libraries, in order to remove the abstraction penalties introduced by object-oriented implementations. To demonstrate the need for expression templates, assume the so called daxpy operation (which is a mnemonic for vector y equals scalar a times vector x plus y), defined as y = ax + y.

(1)

Using a conventional object-oriented approach via operation overloading (here operators *, + and = must be overloaded), one loop is needed for a*x, another one for the addition a*x + y and a last one for copying the result into vector y. Also temporary objects must be created for holding a*x and a*x + y which size may be significantly large. Using expressions templates, operations such as *, + and = are created and nested during compile-time, and hence daxpy would have been evaluated within a single loop and without the need of extra allocations and deallocations. This results in a performance boost, which under certain optimization seem to outperform traditional FORTRAN implementations [18], without any loss of the flexibility that an object oriented array design provides. Expression templates have been already quite extensively popularized in scientific applications; an example of such an implementation within the field of FE programming is [15]. 3.2 Template metaprogramming Code-generating programs are sometimes called metaprograms; writing such programs is called metaprogramming; writing program that write code has innumerous applications. In 1994 Erwin Unruh wrote and circulated at a C++ standards committee meeting a C++ program that printed out a list of prime number at compile time as error messages [19]. This seems to have inspired Todd Veldhuizen to use C++ templates in order to construct specialized algorithms by weaving together fragments of code in compile-time and to invent what will be later be known as template metaprogramming [20]. In template metaprogramming, templates are leveraged so that the compiler in the act of compiling code, executes a program. The output of these programs may range from compile-time constants to data structures. No mutable variables are allowed in template metaprograms and thereafter flow control is usually achieved through recursion with template specializations to provide the ending condition. In C++, template metaprogramming is Turing-complete2, meaning that any computation expressible by a computer program can be computed by a template metaprogram. 3.2.1 Illustration and usage As to illustrate the metaprogramming technique, let the following matrix-vector operations, x = x + cAy

(2)

where c a scalar, x, y vectors and A matrix. Now define two classes for fixed size matrices and vectors, namely metaMatrix and metaVector and overload operator(), as template class metaMatrix { private: T data_[M*N]; 2

The term derives from the name of mathematician Alan Turing who introduced the model of the universal Turing machine.

public: inline T& operator() (int i, int j) {return data_[i*N+j];} }; template class metaVector { private: T data_[N]; public: T& operator() (int i) {return data_[i];} template metaVector& add_cMV(double c,metaMatrix& mat, metaVector&vec){ metaProdLoop::Loop::run(*this,mat,vec,c); return *this; } };

Then define a member function add_cMV that calls a loop unrolling routine defined as Loop, which recursively unrolls a loop from M to 1, template struct metaProdLoop{ template struct Loop{ inline static void run(metaVector&me, metaMatrix& mat, metaVector& vec, double c){ me(I-1)+=c* metaProd::f(mat,vec); Loop::run(me,mat,vec,c); } }; template struct Loop{ inline static void run(metaVector& me, metaMatrix& mat, metaVector& vec, double c){ me(0)+=c* metaProd::f(mat,vec); } }; };

calling the template metafunction metaProd, defined below, with the specialization metaProd to stop the recursion. template struct metaProd{ template inline static T f(metaMatrix& mat, metaVector& vec){ return mat(I,J-1)*vec(J-1)+metaProd::f(mat, vec); } }; template struct metaProd{ template inline static T f(metaMatrix& mat, metaVector& vec){ return mat(I,0)*vec(0); } };

Now add_cMV can be called as follows: int main() { double c=0.577; metaVector x; metaVector y; metaMatrix A; x.add_cMV(c, A, y); return 0; }

for(int i=0;i