Workbook of Applications in VectorSpace C++ Library 265

CHAPTER
Four
Finite Element Method Primer
Various methods in the last chapter are mostly applicable to small size problems. We have demonstrated that
the VectorSpace C++ Library help to ease the programming task significantly. However, if the problem size is
down to one or two variables, they might be solved by hand as well. For better approximation of the solution, we
often need to increase the number of the variables substantially. Finite difference method, finite element method,
and boundary element method are three widely accepted methods for large size problems. We have introduced
the finite difference method in Chapter 1 and the boundary element method in the Chapter 3. Yet another defi-
ciency for the variational method in the last chapter is that it is very simplistic in terms of the geometry of the
problem domains. The geometry of the problem domains is, in most case, very simple; a line segment, a square
(or rectangle), or a circle. In real world applications, the geometry of the problem domains is always much more
complicated. We devote the following two chapters for finite element method with considerable depth. The finite
element method is the most well-established method among the three methods for the large-scale problems. It is
also most capable of handling arbitrarily complicated geometry
Moreover, we would also like to demonstrate to the users of the VectorSpace C++ Library that a numerical
method is often not just about mathematical expression which is already made easy by using VectorSpace C++
Library. The programming complexities caused by complicated geometry (and its large size variables) in finite
element method serves as an excellent test bed that the object-oriented programming can make a significant dif-
ference. The source code of “fe.lib” is used to demonstrate the power of object-oriented programming in numer-
ical applications.
The object-oriented programming is the present-day programming paradigm which is supported by the indus-
trial flag-ship general purpose language—C++. Other alternative approaches for programming highly mathemat-
ical problems are symbolic languages, special purpose packages, or program generators written specifically for
dedicated application domains. These alternative approaches may have specialized capability in solving mathe-
Finite Element Method Primer
266 Workbook of Applications in VectorSpace C++ Library
Chapter 4
matical problems just like what VectorSpace C++ Library is designed for. However, for general purpose pro-
gramming, none of these alternative approaches could come close to rival that of C++. If we have chosen those
alternative approaches, we will be seriously penalized by their limited capability in non-mathematical aspects of
the programming. If you choose to program in C++ with the VectorSpace C++ Library, your programming task
will be significantly empowered by the object-oriented programming—the modern programming paradigm.
Time have proven that specific purpose languages do not last long, they come and go and never gaining any
wide acceptance. Sometimes, they are even quickly forgotten by the communities of the applications that they
are specifically targeting for. Jump on the band wagon of C++, you have entire software industry (particularly all
the first-ranked compiler vendors), professional programmers, and a vast number of C++ literatures behind you.
Our program’s potential can only be limited by our own imagination, not some un-supported language features.
4.1 Basics of Finite Element Method
4.1.1 Mathematical Abstraction of Finite Element Method
Finite element method can be considered as a special case of variational methods, with special emphases on
the a systematic treatment of complicated geometry.
Finite Element—A Systematic Treatment for Complex Geometry
In finite element method, the approximation basis functions for the variable u
e
is defined in each subdo-
main—element (see Figure 4•1, the subscript “e” denotes “element”, and “h” denotes element discretization
into a “characteristic size”—h)
Eq. 4•1
where “nen” is the number of nodes in an element. The space of u
e
is infinite dimensional, in which every point
x on the element has a variable u
e
(x) associated with it. In Figure 4•1, this infinite dimensional variable u
e
(x) is
approximated by a finite dimensional space of approximated function (x) which in turn only depends on finite

e
h
global domain —Ω
discretized global domain—

h
element—Ω
e
h
boundary—Γ
Figure 4•1 Geometry of global domain discretization.
u
e
u
e
h
φ
e
a
u
ˆ
e
a
where a ,
≡ ≅ 0 … n , en 1 – , =
u
e
h
Workbook of Applications in VectorSpace C++ Library 267
Basics of Finite Element Method
number of nodal values (“a” is the element node number, “hat” denotes a nodal value). The approximated
function, rewritten as ( ; x), is defined through a set of interpolation (basis) function

on the element as
in Eq. 4•1. The space spans by these bases, , is known as the finite element space. The trio set { , ,
}, defined as a finite element is consists of (1) element (domain) “Ω
e
”, (2) interpolation functions “ ”, and
(3) degree of freedoms “ ”.
1

We have seen some examples of linear and quadratic interpolation functions for the purpose of numerical
integration in 1-D and 2-D in the Chapter 3. For example, interpolation functions for a bilinear four-node element
can be defined with the formula
Eq. 4•2
where index “a” ( = 0, 1, 2, 3) is the element node number. The coordinate (ξ
a
, η
a
) = {{-1, -1}, {1, -1}, {1, 1}, {-
1, 1}} is the natural (or referential) coordinates of the four nodes. Therefore, the explicit forms for the interpola-
tion functions are
Eq. 4•3
The interpolation function formula for linear triangular element can be degenerated from Eq. 4•3 by setting
, and
Eq. 4•4
(or using “triangular area coordinates” as in page 454 of Chapter 5). That is
Eq. 4•5
Coordinate transformation using Eq. 4•3 for quadrilateral and Eq. 4•5 for triangular elements are shown in the
middle column of Figure 4•2. From those integration examples, we note that a reference element
1.
, Ω
e
, can be
defined in a normalized region with a coordinate transformation rule x x(Ω
e
) which maps the reference ele-
ment, Ω
e
, to a physical element, ; i.e., a normalized domain in natural coordinates ξ is transformed to a phys-
ical domain in coordinate x. The interpolation functions for the coordinate transformation rule can be chosen to
be the same as the interpolation for the approximated function (x) as in Eq. 4•1. That is
Eq. 4•6
where is the nodal coordinates (“over-bar” indicates fixed nodal values). A finite element with the same set of
interpolation functions for (1) approximation functions and (2) coordinate transformation rule is called an iso-
1. P. G. Ciarlet, 1978, “ The finite element method for elliptic problems”, North-Holland, Amsterdam.
u
ˆ
e
a
u
e
h
u
ˆ
e
a
φ
e
a
φ
e
a
≡ Ω
e
h
φ
e
a
u
e
h
φ
e
a
u
e
h
N
a
ξ η , ( )
1
4
--- 1 ξ
a
ξ + ( ) 1 η
a
η + ( ) =
N
0
1
4
--- 1 ξ – ( ) 1 η – ( ) = N
1
1
4
--- 1 ξ + ( ) 1 η – ( ) N
2
1
4
--- 1 ξ + ( ) 1 η + ( ) = N
3
1
4
--- 1 ξ – ( ) 1 η + ( ) = , , = ,
N
0
Tri
N
0
= N
1
Tri
N
1
= ,
N
2
Tri
N
2
N
3
+ =
1
4
--- 1 ξ + ( ) 1 η + ( )
1
4
--- 1 ξ – ( ) 1 η + ( ) +
1
2
--- 1 η + ( ) = =
N
0
Tri
1
4
--- 1 ξ – ( ) 1 η – ( ) = N
1
Tri
1
4
--- 1 ξ + ( ) 1 η – ( ) N
2
Tri
, = ,
1
2
--- 1 η + ( ) =


e
h
u
e
h
x Ωe ( ) φ
e
a
x
e
a
where a , ≡ 0 nen 1 – , =
x
e
a
Finite Element Method Primer
268 Workbook of Applications in VectorSpace C++ Library
Chapter 4
parametric element. The interpolation functions in finite element method are further subject to continuity and
completeness requirements. The continuity requirement demands that the approximated function to be continu-
ous both in the interior and the boundaries of the element. The completeness requirement demands arbitrary
variation, up to certain order, in the approximated function can be accurately represented. When these require-
ments are relaxed, we have the so-called non-conforming elements.
Finite Element Approximation
In the standard finite element method, the weighting functions, W, is taken as that in the Galerkin method in
the context of weighted residual methods (see page 232), which are the same as the element interpolation func-
tions in Eq. 4•1, but vanishing at boundaries corresponding to the essential boundary conditions; i.e.,
W = Eq. 4•7
g is the essential boundary conditions on the boundary Γ
g
, and (“over-bar” indicates fixed nodal values) is
the nodal value of the essential boundary condition with a boundary interpolation function on the boundary
associated with the element. Since is defined in the element domain only, this particular choice of weighting
function resembles the subdomain collocation method (see page 229) in the weighted residual method, where W
= 1 on each subdomain and W = 0 elsewhere.
ξ
4
5
2
6
3
7
0
8
1
η
ξ
η
η
ξ
ξ
η
1-D
linear
quadratic
2-D
curve
linear quadrilateral
quadratic quadrilateral
degenerated
linear triangle
degenerated
quadratic triangle
Figure 4•2 (1) 1-D linear and quadratic line elements, and (2) 2-D curve, linear quadrilateral and
trianglular elements, and quadratic quadrilateral and triangular elements.
0
1
2 3
0 1
0 1 2
0
1
2
0 1
2
1
4
0
7
5
2
8
φ
e
a
0 for u
ˆ
e
a
g φ
Γ
e
a
u
e
a
≡ = on Γ
g
,
φ
e
a
otherwise ,
u
e
a
φ
Γ
e
a
φ
e
a
Workbook of Applications in VectorSpace C++ Library 269
Basics of Finite Element Method
For a self-adjoint operator, from Eq. 3•125 in Chapter 3, the finite element approximation, at each element,
gives
Eq. 4•8
or in matrix forms
Eq. 4•9
where,
Eq. 4•10
The difference of Eq. 4•8 from Eq. 3•125 in Chapter 3 is now we have second and third terms in the right-hand-
side. The second terms is the non-homogeneous natural boundary conditions
= h on Γ
h
Eq. 4•11
where is flux q projected on the outward unit surface normal n. This term occurs when we take integration
by parts on the weighted-residual statement, then, applied the Green’s theorem to change the resultant right-
hand-side domain integral into a boundary integral. The third term is due to non-homogeneous essential bound-
ary conditions. According to the first line of Eq. 4•7, rewritten with a new index “b” as . In Eq. 4•10 the
index “a” is the element equation number, and the index “b” is the element variable (degree of freedom) number.
Since W has been taken according to Eq. 4•7, the rows (or equations) corresponding to the fixed degree of free-
doms (essential boundary conditions) will have vanishing weighting function (W = 0) multiplies through-out
every term of Eq. 4•8. Therefore, the rows (or equations) corresponding to the fixed degree of freedoms will be
eliminated at the global level. We also note that the element tensors is the element stiffness matrix, and the
element tensors is the element force vector.
In summary, for a differential equation problem, we first discretize its domain into elements (as in Figure 4•1)
and approximate its variables (Eq. 4•1), and weighting functions (Eq. 4•7) corresponding to a variational princi-
ple. These steps are known as the finite element approximation
1
. A finite element approximation depends on the
choice of (1) the variational principle, and (2) a corresponding set of variables approximated by a selected set of
interpolation functions. The various variational principles make the finite element method such an open area for
improvements. These various variational principles also bring a challenge that a finite element program should
be able to endure a dramatic impact of changes in its design structure, and to enable the reuse of existing code in
its evolutionary course. The object-oriented programming has a lot to offer in this regard.
1. p. 3 in F. Brezzi and M. Fortin, 1991, “ Mixed and hybrid finite element methods”, Springer-Verlag, New York, Inc.
a φ
e
a
φ
e
b
, ( )u
ˆ
e
b
φ
e
a
f , ( ) φ
e
a
h , ( )
Γ
a φ
e
a
φ
e
b
, ( )u
e
b
– + =
k
e
ab
u
ˆ
e
b
f
e
a
=
k
e
ab
a φ
e
a
φ
e
b
, ( ) =
f
e
a
φ
e
a
f , ( ) φ
e
a
h , ( )
Γ
a φ
e
a
φ
e
b
, ( )u
e
b
– + =
q n • φ
Γ
e
a
h
e
a

q n •
g φ
Γ
e
b
u
e
b

k
e
ab
f
e
a
Finite Element Method Primer
270 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Global Matrix and Solution Phase—Systematic Treatment for Large-Size Degree of Freedoms
Eq. 4•8 to Eq. 4•10 are defined only on an element domain— , while the variational principle needs to be
applied on the global discretized domain— ; i.e., the element stiffness matrix and the element force vec-
tor need to be assembled into a global stiffness matrix K and global force vector F as
Eq. 4•12
where is the global nodal solution vector. The symbol stands for the procedure of assembly of all ele-
ments. The index “i” is the global equation number and index “j” is the global variable number of .
4.1.2 Object-Oriented Modeling of the Finite Element Method
The central theme of the object-oriented programming is the data abstraction and inheritance. Firstly, the
data abstraction enabling software modules to be modeled after the real world objects. Each of such a software
module —class defines the states of an object as its member data, and the behaviors of the object as its member
functions. In the procedure programming method, data structure and algorithms (subroutines) performing on the
data structure are separate. In an abstract data type, they are organized into a coherent unit; i.e., the class. C++
also provides user access control mechanism to declare its member data or functions as private, protected, or
public, such that the complexities can be encapsulated inside the abstract data type. Secondly, the inheritance
relation enables factoring of common parts to define a more general base class higher in the class hierarchy.
More specific classes can be derived from the base class by adding details to facilitate the idea of programming
by specification and to enforce code reuse. The most impressive power comes out of this inheritance relation is
the dynamic binding mechanism provided to implement the concept of polymorphism. In C++ such mechanism
is provided by declare member functions as virtual. A call on the virtual function of a base class is dispatched by
the virtual function mechanism to the corresponding member function of the derived class, where a specific
behavior is actually defined. We explore all these programming concepts in the modeling of the finite element
library— “fe.lib”, in which the source codes are provided for demonstrating the object-oriented method.
Then, we go further on. The object-oriented paradigm is meant to replace the old-way—the procedure pro-
gramming. As we have mentioned earlier, the data and function are now organized together as a coherent
abstract data type—class. The objects are empowered with inheritance and virtual function mechanism. How-
ever, the dependency relations among the objects can grow to an extremely complicated network of objects. The
object-oriented analysis is applied on the problem domains to understand the dependency relations among
objects and the object-oriented design is the newly programming discipline taken to harness the rampant power
of C++. It sounds so familiar that we used to write “go to” among Fortran statements which has the potential to
grow into an extremely complicated flow chart (a network of statements). The procedure programming is the old
discipline proposed to rescue the old-world from chaos. Now, we introduce the object-oriented method and the
resultant complicated network of objects turns out to be a serious problem too. A new discipline, the object-ori-
ented design, is a lesson learned from a frequently cited costly experience from Mentor Graphics (one of the
world largest CAD company today), which is the very first company to attempt a large-scale C++ project
1
.

e
h

h
k
e
ab
f
e
a
K
ij
u
ˆ
j
F
i
=
K
i j
A
e ∀
k
e
ab
and F
i
, A
e ∀
f
e
a
= =
u
ˆ
j
A
e ∀
u
ˆ
j
Workbook of Applications in VectorSpace C++ Library 271
Basics of Finite Element Method
In the section on the mathematical abstraction of finite element method, only Eq. 4•8 to Eq. 4•10 contain the
core of mathematics of the differential equation problems. All other things in finite element method are really
complicated details. As we have mentioned earlier, the finite element method can be viewed as a systematic
treatment for these non-mathematical trivia. However, these trivia are no simple matter, which are actually quite
a challenging task that we will use object-oriented modeling for their implementation.
Step 1. Discretization Global Domain—
The first step of the finite element method is to discretize the problem domain into element— . An element
is often described as simple geometrical area like triangles or quadrilaterals. The vertices for these simple
geometrical objects are called nodes with nodal coordinates as . A node object is instantiated by its constructor
Node(int node_number,
int number_of_spatial_dimension,
double* array_of_coordinates);
Using the terminology of the relational database the “node_number” is the key to this abstract data type—
“Node”. One considers that the “node_number” as the identifier for an instance of the class Node. The following
example is to define a 2-D case with the node number “5”, and coordinates = {1.0, 2.0}
T
double *v;
v = new double[2];
v[0] = 1.0; v[1] = 2.0;
Node *nd = new Node(5, 2, v);
This instantiates an object of type “Node” pointed to by a pointer “nd”. Data abstraction is applied to model the
“Node” as an object. The states of the “Node” is consist of private data members include the node number, the
spatial_dimension, and the values of its coordinates. The behaviors of the “Node” are public member functions
that provide user to query the states of the “Node” including it node number, and spatial dimension, ... etc. The
“operator[](int)” is used to extract the components of the coordinates, and logical operators “opera-
tor==(Node&)” and “operator !=(Node&)” are used for the comparison of the values of two nodes. The data and
the functions that operating on them are now organized together into a coherent unit—class. The private mem-
bers of the object are encapsulated from users that the access are only possible through its public members. The
encapsulation mechanism provides a method to hidden complexities from bothering users (see Figure 4•3).
An element— is constructed by
1 Omega_eh(int element_number,
2 int element_type_number,
3 int material_type_number,
int element_node_number,
int *node_number_array);
1. see p. 1 in J. Soukup, 1994, “Taming C++”, Addison-Wesley, Reading, Massachusetts, and preface in J. Lakos, 1996,
“Large-scale C++ software design”, Addison-Wesley, Reading, Massachusetts.

h

e
h

e
h
x
x

e
h
Finite Element Method Primer
272 Workbook of Applications in VectorSpace C++ Library
Chapter 4
The “element_number” play the role of the key for the element class “Omega_eh”. The “element_type_number”
and the “material_type_number” are integers greater or equal to “0”. The default values for the both numbers are
“0”. For example, the “element_node_number” is “3” for a triangular element, and “4” for a four-node element.
The “node_number_array” points to an int pointer array of global node numbers for the element. An example is
1 int *ena; // 10 11
2 ena = new int[4]; // +-------------+
3 ena[0] = 0; ena[1] = 1; // | |
4 ena[2] = 11; ena[3] = 10; // | |
5 Omega_eh *elem = // +-------------+
6 new Omega_eh(0, 0, 0, 4, ena); // 0 1
The order of global node numbers in the “node_number_array” is counter-clockwise from the lower-left corner,
as illustrated in the comment area after each statement, which is conventional in finite element method.
A discretized global domain— basically consists of a collection of all nodes and elements as
1 class Omega_h { // discretized global domain—
2 protected:
3 Dynamic_Array<Node> the_node_array;
4 Dynamic_Array<Omega_eh> the_omega_eh_array;
5 public:
6 Omega_h(); // declared by not defined
7 ...
8 };
Figure 4•3 The class Node is consists of private data members to describe its
states, and public member functions provide the access to query its states. The
private members are encapsulated away from the controlled access through the
public members.
==
[]
node_no()
int node_no
int spatial_dim
double* value
...
...
controlled access
class Node

h

h
Workbook of Applications in VectorSpace C++ Library 273
Basics of Finite Element Method
The data structure Dynamic_Array<T> does what it means, which is declared and defined in “dynamic_array.h”.
It is a simplified version of <dynarray> in the standard C++ library
1
. Two protected member data consist of
“the_node_array” and “the_omega_eh_array” (element array). The default constructor “Omega_h::Omega_h()”
is declared in the header file, The users of the “fe.lib” are responsible for its definition. The following code seg-
ment shows an example of a user defined discretized global domain as illustrated in Figure 4•4.
1 Omega_h::Omega_h() { // define default constructor
2 int row_node_no = 4;
3 row_element_no = row_node_number -1;
4 double v[2];
5 for(int i = 0; i < row_node_no; i++)
6 for(int j = 0; j < row_node_no; j++) { // ena[3] ena[2]
7 int nn = i * row_node_no + j; // +-------------------+
8 v[0] = (double) j; v[1] = (double) i; // | |
9 Node *node = new Node(nn, 2, v); // | |
10 the_node_array.add(node); // | |
11 } // | |
12 int ena[4]; // +-------------------+
13 for(int i = 0; i < row_element_no; i++) // ena[0] ena[1]
14 for(int j = 0; j < row_element_no; j++) {
15 int nn = i * row_node_no + j; // node number at lower left corner
16 ena[0] = nn; ena[1] = ena[0] + 1;
17 ena[3] = nn + row_node_no; ena[2] = ena[3] +1;
18 int en = i * row_element_no + j; // element number
1. P.J. Plauger, 1995, “The draft standard C++ library”, Prentice-Hall, Inc., Englewood Cliffs, New Jersey.
0 1
2
3
4
5 6
7
8 9
10 11
12 13 14 15
0
1 2
3 4 5
6 7 8
Figure 4•4 Nine elements in a rectangular area consist of 16 nodes.
Finite Element Method Primer
274 Workbook of Applications in VectorSpace C++ Library
Chapter 4
19 Omega_eh *elem =
20 new Omega_eh(en, 0, 0, 4, ena);
21 the_omega_eh_array.add(elem);
22 }
23 }
Then, we can make an instance of the discretized global domain “Omega_h” by declaring in main() function
Omega_h oh;
The instance “oh” calls the default constructor “Omega_h::Omega_h()” that is custom made by the user.
Remark: For users who are familiar with database languages
1
, the class definitions of Node, Omega_eh, and
Omega_h per se define the database schema; i.e., the format of the data, which serves the function of the data
definition language (DDL). The function “Dynamic_Array<T>::add(T*)” is an example of data manipulation
language (DML) that assists user to modify the database. And two most important features of data query lan-
guage provided by “fe.lib” are the node selector “Node& Omega_h::operator [ ](int)” and the element selector
“Omega_eh& Omega_h::operator ( )(int)”.
Step 2. Free and Fixed Variables
The discretized global free degree of freedoms are (“hat” indicate a nodal value)
on Ω
h
.
The essential boundary conditions (fixed degree of freedoms) and natural boundary conditions are
on , and on
respectively, where the “over-bar” indicates a fixed value. The global variables are modeled as class “U_h”.
And, the global boundary conditions and are modeled as class “gh_on_Gamma_h”. A constraint flag is
used to switch in between “Dirichlet” and “Neumann” to indicate whether the stored values are essential or nat-
ural boundary conditions, respectively.
All three kinds of values , , and are nodal quantities, which are somewhat similar to the coordinates
of a node; i.e., . Therefore, we can factor out the code segment on coordinates in the class Node and create a
more abstract class Nodal_Value for all of them.
1 class Nodal_Value {
2 protected:
3 int the_node_no,
4 nd; // number of dimension
1. e.g., Al Stevens, 1994, “ C++ database development”, 2nd eds., Henry Holt and Company, Inc., New York, New York.
u
ˆ
h
g
h
Γ
h
g
h
h
Γ
h
h
u
ˆ
h
g
h
h
h
u
ˆ
h
g
h
h
h
x
Workbook of Applications in VectorSpace C++ Library 275
Basics of Finite Element Method
5 double *the_value;
6 public:
7 operator[](int);
8 ...
9 };
Now the three classes are publicly derived from the base class “Nodal_Value” as
1 class Node : public Nodal_Value { ... }
2 class U_h : public Nodal_Value { ... }
3 class gh_on_Gamma_h : public Nodal_Value { ... }
All three derived classes inherit the public interfaces (member functions) of the class Nodal_Value. For example,
now all three derived classes can use the operator[](int) to access the nodal values. If “nd” is an instance of the
class Node and “uh” is an instance of the class U_h, and “gh” is an instance of the class gh_on_Gamma_h, then,
the access is performed by
1 nd[0]; // first coordinate value
2 uh[1]; // second degree of freedom
3 gh[0]; // first constraint values
The common part of the three classes are factored out to form a new base class “Nodal_Value”. The code will
be significantly duplicated, if we have not done so. In addition, factoring out this common part is good for the
maintenance of the code. If we have found out in the future that the way we modeled the “nodal values” is unsat-
isfactory, changes made in this single class are sufficient comparing to changes needed to be made in all three
classes. In general, the object-oriented programming method not only use data abstraction to organize data and
functions (the algorithm operating upon data), it also help to classify these software modules, which are modeled
after real world objects, into a hierarchical structure. We note that classification of things into hierarchical struc-
ture is one of the most powerful tools that human beings have to built knowledge.
We now consider an example of heat conduction (see Figure 4•5) using the discretized global domain,
declared as “oh” previously, and was illustrated in Figure 4•4. The number of degree of freedom “ndf” = 1; i.e.,
the temperature. We should instantiate, in the “main()” program, the variable “uh” of class U_h, and the bound-
ary conditions “gh” of class gh_on_Gamma_h as the followings
1 int main() {
...
2 int ndf = 1;
3 U_h uh(ndf, oh);
4 gh_on_Gamma_h gh(ndf, oh);
...
5 }
The constructor of class U_h is defined in “fe.lib”. The users do not need to worry about it. However, the essen-
tial and natural boundary conditions in the class gh_on_Gamma_h are parts of every differential equation prob-
Finite Element Method Primer
276 Workbook of Applications in VectorSpace C++ Library
Chapter 4
lems. Therefore, defining the constructor of class gh_on_Gamma_h is users’ responsibility. This constructor
needed to be defined before it is instantiated in the above. For the problem at hand, we have
1 gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& oh) {
2 __initialization(df, omega_h);
3 int row_node_no = 4;
4 for(int i = 0; i < row_node_no; i++) {
5 the_gh_array[node_order(i)](0) = gh_on_Gamma_h::Dirichlet;
6 the_gh_array[node_order(row_node_no*(row_node_no-1)+i)](0) = gh_on_Gamma_h::Dirichlet;
7 the_gh_array[node_order(row_node_no*(row_node_no-1)+i)][0] = 30.0;
8 }
9 }
The first line in the constructor (line 2) called a private member function of class gh_on_Gamma_h. This func-
tion initiates a private data member “Dyanmic_Array<Nodal_Constraint> the_gh_array” for the class
gh_on_Gamma_h. This is a mandatory first statement for every constructor of this class for ochestrating internal
data structure. The first line in the for loop uses a constraint type selector “operator ( )(int degree_of_freedom)”.
It can be assigned, for each degree of freedom, to either as “gh_on_Gamma_h::Dirichlet” to indicate an essential
boundary condition or as “gh_on_Gamma_h::Neumann” to indicate a natural boundary condition. Line 7 uses a
constraint value selector “operator [ ](int degree_of_freedom)” to assign 30
o
C to the nodes on the upper bound-
ary. The default condition and default value, following finite element method convention, are natural boundary
condition and “0”, respectively. Therefore, for the present problem, the natural boundary condition with “0” on
two sides can be neglected. On the bottom boundary conditions, we only need to specify their constraint type as
essential boundary conditions, the assignment of value of “0.0” (the default value) can be skipped too.
0 1
2
3
4
5 6
7
8 9 10 11
12 13 14 15
0 1 2
3 4 5
6 7 8
Figure 4•5 Heat conduction problem with two side insulated, bottom and top temperature
boundary conditions are set to 0
o
C and 30
o
C, respectively.
g = 0
o
C
g = 30
o
C
h=0 h=0
Workbook of Applications in VectorSpace C++ Library 277
Basics of Finite Element Method
Step 3. Element Formulation
At the very heart of finite element program is the element formulation. This part does every thing that is most
relevant to the variational methods we have introduced in Chapter 3. Henceforth, this part is highly mathemati-
cal. The VectorSpace C++ Library is therefore most heavily used in the element formulation. For every differen-
tial equation problem, the element formulation is different. The impact of change to the code from one problem
to the other is a routine rather than an exception. Under the procedure programming paradigm, it is soon recog-
nized that an element subroutine should be used to form an replaceable module. In object-oriented programming,
further flexibility for element formulation can be obtained through the polymorphism supported in C++.
We have seen that for data abstraction C++ provides class to organize data and functions into a coherent
object. The inheritance is provided to build hierarchical structure of objects and enable code reuse. Now the
objects put into the hierarchical structure can be made to be intelligent to perform some autonomous tasks. For
example, we may have a base class of “Animal”. Then, we derived from this class of “Animal” to form classes of
“Lion”, “Horse”, and “Whale”. Next, we declare three instances “lion”, “horse”, and “whale” of general type
“Animal”, each of polymorphic concrete types “Lion”, “Horse”, and “Whale”. Now, God says “Animals eat food
!” The “lion” goes to catch a zebra, the “horse” bites grass, and the “whale” catches tons of fishes. The advantage
of this higher level of intelligent is enormous. Now we can have one single generic command for all kinds of des-
perately different individual objects.
A simple algebraic example is described in root-finding problem in page 40 of Chapter 1, where the New-
ton’s formula gives the increment of solution dx as
dx = - f / df
The corresponding C++ code can be written as a function
C0 dx(const C0& f, const C0& df) { return - f / df; }
For one dimensional problem, f, df, and dx are all Scalar object of C0 type. For n-dimensional problem, n > 1, f
and dx are Vector object of C0 type with length “n” and df is a Matrix object of C0 type with size “n n”. The
“C0::operator / (const C0&)” now no longer implies “divide” operation. It actually means to invoke matrix solver
that use df as the left-hand-side matrix and “-f” as the right-hand-side vector. The default behavior of Vector-
Space C++ Library is the LU decomposition, although you have the freedom to change the default setting to
Cholesky decomposition (for symmetrical case only), QR decomposition (for ill-conditioned case) or even the
singular value decomposition (for rank deficient case). This single function is sufficient for the very different
arguments taken, and different operations intelligently dispatched to perform upon themselves.
In Chapter 3, we have introduced the non-linear and transient problems in the context of variational methods
which are now the kernel of the element formulation. We considers the impact of change by these two types of
problems that will be played out in the element formulation. We note that an even greater impact will be played
out in the mixed formulation, introduced in Chapter 3 in page 217, if we use global matrix substructuring solu-
tion method (or “static condensation”). We defer the more complicated matrix substructuring until Section 4.2.5.
First, from “fe.lib” user’s perspective, the design of the “element formulation definition language”, if you
would, is for (1) definition of an element formulation and (2) registration of an element type. The user code seg-
ment for the declaration and instantiation of a class HeatQ4 is
×
Finite Element Method Primer
278 Workbook of Applications in VectorSpace C++ Library
Chapter 4
1 class HeatQ4 : public Element_Formulation {
2 public:
3 HeatQ4(Element_Type_Register a) : Element_Formulation(a) {}
4 Element_Fomulation *make(int, Global_Discretization&);
5 HeatQ4(int, Global_Discretization&);
6 };
7 Element_Formulation* HeatQ4::make(int en, Global_Discretization& gd) {return new HeatQ4(en, gd);}
8 HeatQ4::HeatQ4(int en, Global_Discretization&) : Element_Formulation(en, gd) {
9 ...
10 }
From this code, the line 5 which is the declaration of the constructor of the heat conduction element formula-
tion—“HeatQ4(int, Global_Discretization&)”. The definition of this constructor is user customized, the contents
of this constructor is the variational formulation of differential equation problem at hand. We will get to the
details of definitions for the constructor (line 8) at the end of this section.
Polymorphism: First, let’s look at the fe.lib implementation of polymorphism, in this code segment, enhanced by
emulating symbolic language by C++
1
. The class Element_Formulation and the custom defined user class
HeatQ4 are used hand-in-hand. The Element_Formulation is like a symbol class for its actual content class—
HeatQ4. The symbol class Element_Formulation is responsible for doing all the chores including memory man-
agement and default behaviors of the element formulation. The content class HeatQ4 does what application
domain actually required; i.e., the variational formulation. The class Element_Formulation has a private data
member “rep_ptr” (representing pointer) which is a pointer to an Element_Formulation type as
1 class Element_Formulation {
2 ...
3 Element_Formulation *rep_ptr;
4 C0 stiff, force, ...;
5 protected:
6 virtual C0& __lhs() { return stiff; }
7 virtual C0& __rhs() { return force; }
8 ...
9 public:
10 ...
11 C0& lhs() { return rep_ptr->__lhs(); }
12 C0& rhs() { return rep_ptr->__rhs(); }
13 ...
14 };
Since the derived class HeatQ4 is publicly derived from the base class Element_Formulation, an instance of
HeatQ4 has its own copy of Element_Formulation as its “header”. Therefore, the rep_ptr can point to an instance
1. see (1) p. 58 “handle / body idiom”, (2) p. 70 “envelope / letter” idiom, and (3) p. 315 “symbolic canonical form” in J.O.
Coplien, 1992, “ Advanced C++: Programming styles and idioms”, Addison-Wesley, Reading, Massachusetts.
Workbook of Applications in VectorSpace C++ Library 279
Basics of Finite Element Method
of HeatQ4. This is done by invoking “Element_Formulation* HeatQ4::make(int, Global_Discretization&)” to
produce a pointer to HeatQ4 instance. We also see that the two public member functions lhs() and rhs() are for-
warding, by its delegate “rep_ptr”, to its derived class protected member functions __lhs() and __rhs(), in the
present case, forwarding to an instance of HeatQ4’s two protected virtual member function __lhs() and __rhs().
The default behaviors of these two protected virtual member function has been defined to return element stiffness
matrix and element force vector.
We have explained the mechanisms built for polymorphism. Now we can consider how the impact of change
bring out by nonlinear and transient problems can be accommodated under this design. For a nonlinear problem
the solution is obtained from an iterative scheme u
i+1
= u
i
+ δu
i
for the convergence of the residual vector R = F
- K(u) u (from Eq. 4•12) defined as
Eq. 4•13
From this approximated equation, we have the incremental solution δu
i
as the solution of the simultaneous linear
algebraic equations
Eq. 4•14
where both the tangent stiffness matrix and the residual vector are functions of u
i
. That is at the
element level, the nodal values— must be available. Therefore, a new class derived from class
Element_Formulation is
1 class Nonlinear : public Element_Formulation {
2 C0 ul;
3 void __initialization(int, Global_Discretization&) { ul &= gd.element_free_variable(en); }
4 public:
Figure 4•6 Emulating symbolic language features using C++.
Element_Formulation
rep_ptr
Element_Formulation
HeatQ4
Symbol
Content
R
i 1 +
R u
i 1 +
( ) ≡ R u
i
δu
i
+ ( ) R u
i
( )
R ∂
u ∂
-------
u
i
δu
i
+ ≅ 0 = =
δu
i
R ∂
u ∂
-------
u
i ¸ ,

¸ _
1 –
R u
i
( ) – = K
T
1 –
u
i
( ) R u
i
( ) ≡
K
T
1 –
u
i
( )
R u
i
( )

i
Finite Element Method Primer
280 Workbook of Applications in VectorSpace C++ Library
Chapter 4
5 Nonlinear(int, Global_Discretization&);
6 ...
7 };
8 Nonlinear::Nonlinear(int en, Global_Discretization& gd) : Element_Formulation(en, gd) {
9 __initialization(en, gd);
10 ...
11 };
The class “Nonlinear” inherits all the public interfaces of the class Element_Formulation. On top of that we have
declared a private data member “ul”, the element nodal variables, for this nonlinear element. When the class
“Nonlinear” is defined, it is imperative to invoke its private member function “Nonlinear::__initialization(int,
Global_Discretization&)” to setup the element nodal variables. In this case, the use of inheritance for program-
ming by specification is very straight forward. An example of a simple nonlinear problem is shown in Section
4.2.3. In Chapter 5, we investigate state-of-the-art material nonlinear (elastoplasticity) and geometrical nonlin-
ear (finite deformation problems).
For a transient problem, the polymorphic technique is much more complicated. We show the parabolic case
here. From Eq. 3•191 in Chapter 3 (page 253) we have
Eq. 4•15
In this case, the nodal values from the last time step— is also needed. In addition, we also need to compute
the mass (heat capacitance) matrix “M”.
1 class Transient::public Element_Formulation {
2 C0 mass, ul;
3 void __initialization(int, Global_Discretization&) { ul &= gd.element_free_variable(en); }
4 public:
5 Transient(Global_Discretization&);
6 ...
7 C0& __lhs();
8 C0& __rhs();
9 };
10 Transient::Transient(int en, Global_Discretization& gd) : Element_Formulation(en, gd) {
11 __initialization(en, gd);
12 ...
13 };
14 static double theta = 0.5, dt = 0.01; // central difference θ = 0.5
15 C0& Transient::__lhs() {
16 the_lhs &= mass + dt*theta *stiff; //
17 return the_lhs;
18 }
19 C0& Transient::__rhs() {
20 Element_Formulation::__rhs(); // - f; the default force vector
21 the_rhs += (mass -dt*(1-theta)*stiff)*ul; //
M ∆tθK + ( )uˆ
n 1 +
M ∆t 1 θ – ( )K – ( )uˆ
n
f – =

n
M ∆tθK +
M ∆t 1 θ – ( )K – ( )u
n
f –
Workbook of Applications in VectorSpace C++ Library 281
Basics of Finite Element Method
22 return the_rhs;
23 }
Note that in the definition of class Element_Formulation the default behaviors of the last two protected member
functions are through two virtual member functions to return element “stiff” matrix and element “force” vector
as
virtual C0& __lhs() { return stiff; }
virtual C0& __rhs() { return force; }
This is standard for the static, linear finite element problems. When an instance of Element_Formulation calls its
public member functions “Element_Formulation::lhs()” and “Element_Formulation::rhs()”, the requests are for-
warding to its delegates’ virtual member functions. If these two protected virtual member functions have been
overwritten (lines15-23), the default behaviors in the base class will be taken over by the derived class. An exam-
ple of transient program is shown in Section4.2.4.
Element Type Register: A differential equation problem, solved by a finite element method may apply different
elements for different regions. For example, we can choose triangular elements to cover some of the areas, while
quadrilateral elements to cover the rest of the areas. We can have a “truss” element on certain parts of “planner”
elements to simulated a strengthened structure. From user’s perspective, he needs to register multi-elements as
1 Element_Fomulation* Element_Formulation::type_list = 0; // register element type
2 Element_Type_Register element_type_register_instance;
3 static Truss truss_instance(element_type_register_instance); // element type number “2”
4 static T3 t3_instance(element_type_register_instance); // element type number “1”
5 static Q4 q4_instance(element_type_register_instance); // element type number “0”
The element type register uses a list data structure. We number the last registered element’s element type number
as “0”. This number increases backwards to the first registered element in the “type_list”. When we define an
element as introduced in page 271. The second argument is supplied with this number such as
Omega_eh *elem;
elem = new Omega_eh(0, element_type_number, 0, 4, ena);
The C++ idiom to implement the element type register is discussed in Section 4.1.3.
Element Formulation Definition: Now we finally get to the core of the Element_Formulation. That is the defi-
nition of its constructor. We show an example of heat conduction four-node quadrilateral element
1 HeatQ4::HeatQ4(int en, Global_Discretization& gd) : Element_Formulation(en, gd) {
2 Quadrature qp(2, 4);
3 H1 Z(2, (double*)0, qp), // natrual coordinates
4 N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE(4, 2, qp),
5 Zai, Eta; // alias
6 Zai &= Z[0]; Eta &= Z[1];
Finite Element Method Primer
282 Workbook of Applications in VectorSpace C++ Library
Chapter 4
7 N[0] = (1-Zai)*(1-Eta)/4; N[1] = (1+Zai)*(1-Eta)/4; //
8 N[2] = (1+Zai)*(1+Eta)/4; N[3] = (1-Zai)*(1+Eta)/4;
9 H1 X = N*xl // coordinate transformation
10 H0 Nx = d(N)*d(X).inverse(); // derivative of shape functions
11 J dv(d(X).det()); // the Jacobian
12 double k = 1.0, q = 1.0; // conductivity and heat source
13 stiff &= (Nx * k * (~Nx)) | dv; // element stiffness matrix
14 force &= (((H0)N)*q) | dv; // element force vector
15 }
The “xl” is the element nodal coordinates which is a C0 type Matrix object of size nen nsd(number of element
nodes) (number of spatial dimension). The “stiff” is the element stiffness matrix, a square matrix of size
(nen ndf) (nen ndf) (“ndf” as number of degree of freedoms). The “force” is the element force vector of
size (nen ndf). The VectorSpace C++ Library is most heavily used in this code segment, since it concerns the
subject of variational methods the most. If you have mastered Chapter 3 already, these lines should be com-
pletely transparent to you.
The treatment of the terms on natural boundary conditions and the essential boundary conditions
, in Eq. 4•8 in page 269, requires some explanation. “fe.lib” adopts the conventional treatment that
the natural boundary conditions are taken care of at the global level in Matrix_Representation::assembly() where
the user input equivalent nodal forces of natural boundary condition are directly added to the global force vector.
The treatment of the third term is also conventional that when the Element_Formulation::__rhs() is called it
automatically call Element_Formulation::__reaction() which is defined as
C0 & Element_Formulation::__reaction() {
the_reaction &= -stiff *gl; // “gl” is the element fixed boundary conditions
return the_reaction;
}
The the “reaction” is added to the element force vector as
C0 & Element_Formulation::__rhs() {
the_rhs &= __reaction();
if(force.rep_ptr()) the_rhs += force;
return the_rhs;
}
These two default behaviors can be overwritten as in the class “Transient” in the above. Another example is that
we might want to have different interpolation function to approximate the boundary conditions. In such case,
first we need to call “Matrix_Representation::assembly()” in main() program as
assembly(FALSE); // FALSE turns off nodal force loading
N
a
ξ η , ( )
1
4
--- 1 ξ
a
ξ + ( ) 1 η
a
η + ( ) =
×
×
× × ×
×
φ
e
i
h , ( )
Γ
a φ
e
i
φ
e
j
, ( )u
e
j

Workbook of Applications in VectorSpace C++ Library 283
Basics of Finite Element Method
Then, redefine “__rhs()” in user defined element. In the definition of the user element, we can define boundary
integration of these two terms to the element force vector. The basic idea is just like we can overwrite the virtual
functions “__lhs()” and “__rhs()” for the transient problem.
Now we have shown that object-oriented programming does provide unprecedented flexibility to implement
seemly incompatible problems in finite element method. Most importantly, the flexibility does not come by sac-
rifying the organization or simplicity of the code. A beginner of “fe.lib” can always study the same simple kernel
code. The kernel code does not grow because of the irrelevant details have been added during the course of evo-
lution of “fe.lib” to encompass more advanced problems. The “code-reuse” and “programming by specification”
can be repeated applied to the “fe.lib” relentlessly, while the very kernel of the “fe.lib” may reside in the ever
grander architecture un-disturbed.
Step 4. Matrix Representation and Solution Phase
The user’s code for the steps of matrix representation and solution phase is
1 int main() {
... // instantiation of Global_Discretization object
2 Matrix_Representation mr(gd);
3 mr.assembly();
4 C0 u = ((C0)(mr.rhs())) / ((C0)(mr.lhs()));
5 gd.u_h() = u;
6 gd.u_h() = gd.gh_on_gamma_h();
7 cout << gd.u_h();
8 return 0;
9 }
We show an example illustrated in Figure 4•7. Step 4a. A global stiffness matrix is a square matrix of size
tnn ndf = 7 7 per side, and global force vector is of size tnn ndf = 7, respectively (where “tnn” is the total
number of node, and “ndf” is number of degree of freedoms assumed as “1” for simplicity). The fixed degree of
freedoms are then removed from the global stiffness matrix (with remaining size = 5 5) and global force vector
(with remaining size = 5). This is done at line 2 when an instance of Matrix_Representation “mr” is initialized
with an instance of Global_Discretization “gd”. Step 4b. The mapping relationship of element stiffness matrix to
global stiffness matrix, and element force vector to global force vector can be constructed element by element.
This global-element relation is also established in line 2. Step 4c. The maps in Step 4b are used to add element
stiffness matrices and element force vectors to the global stiffness matrix and global force vector as in line 3,
where the public member function “Matrix_Representation::assembly()” is called. Then, the global stiffness
matrix and global force vector are used for linear algebraic solution of the finite element problem as in line 4.
Step 4d. The solution is in the order of free degree of freedom number which is then mapped back to the global
degree of freedom number for output of the solution. This is done in line 5 where the global solution vector
gd.u_h() is updated with the solution “u”. The values for the fixed degree of freedoms can be retrieved from the
program input of the problem. That is the line 6 where the same global solution vector gd.u_h() is updated with
fixed degree of freedom “gd.gh_on_gamma_h()”.
In between the Step 4c and Step 4d, the variational problem has been reduced to a matrix solution problem. A
regular matrix solver provided in C0 type Matrix in Chapter 1 can be applied to solve this problem, although
× × ×
×
Finite Element Method Primer
284 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Figure 4•7 Element connectivity example. Step 1. elimination of fixed degree of freedoms, Step 2.
element to global mapping, Step 3. assembly all elements, and Step 4. equation number to global
degree of freedoms number.
0
1
2
3
4
5
6
7
0
1 2
3 4
0 1 2 3 4 5 6 7
0
1
2
3
4
5
6
7
0 1 2 4 5
0
1
2
4
5
Element connectivity
Step 4a: eliminate fixed degree of freedoms
Step 4b: map element stiffness matrix and element force vector to global matrix and global vector, respectively
Element 0:
Element 1: Element 2: Element 3: Element 4:
Step 4c: assembly of all elements
0
1
2
4
5
0
1
2
3
4
5
6
7
Step 4d: map equation number to global degree of freedom
number equation number global degree of freedom number
Workbook of Applications in VectorSpace C++ Library 285
Basics of Finite Element Method
there are many matrix computational methods specifically designed for the finite element method. To name a
few, profiled sparse matrix, frontal method, and nested dissection
1
. These methods are not supported by “fe.lib”.
However, we reserve an entry point to declare the Matrix_Representation as
Matrix_Representation mr(gd, “ ... user defined string to identify special matrix ...”);.
The global stiffness matrix and global force vector can be replaced by corresponding special matrix and vector,
provided you have code all the needed interfaces for retrieving the components in the special forms of the global
matrix and vector.
Just as in the Element_Formulation, object-oriented programming provides mechanisms to deal with impact
of change for a swift evolution of “fe.lib”. Examples of these changes are mixed and hybrid method and contact
mechanics. In abstract mathematical form, they all belong to the category of constrained optimization problems.
4.1.3 Object-Oriented Analysis and Design of Finite Element Method
As in many books on object-oriented analysis and design have suggested, we define that the object-oriented
analysis is to understand the object dependency relation, and the object-oriented design is the discipline to man-
age the potentially complicated dependency relation among objects.
We may think of analysis and design probably is the first thing to consider, logically, even before the model-
ing in the previous section. However, an experienced programmer will point out that the nature of the program-
ming is more like an iterative process that one goes over again and again from analysis/design to modeling then
re-analysis/re-design and then to re-modeling. Some problems are unraveled only after first model has been pro-
posed. In this perspective, the modeling in the previous section provides us the materials to begin with for analy-
sis and design process.
Dependency Graph
The four major components in the modeling of finite element method are (1) the discretized global
domain , (2) variables , (3) element formulation (EF), and (4) matrix representation (MR). We can draw a
tetrahedron with the four vertices represent the four components and the six edges represents their mutual rela-
tions (see Figure 4•8). The first thing we can do is this tetrahedron can be reduced to a planner graph, meaning
that no edge among them can cross each other; i.e., to reduce it to a lower dimension. This step can not always be
done. If there is any such difficulty, we need to applied dependency breakers (to be discussed later) to the graph
to reduce it to a lower dimension. In a planner graph, we represent a component as a node, and their relations as
the arrows. For a component, the number of arrows pointing towards the node is called degree of entrance. In the
convention of object-oriented method, an arrow stands for a dependency relation that the node on the starting
point of an arrow depends on the node at the ending point of the arrow.
We briefly explain these dependency relations. The entrance number “0” says the global discretized variables
depends on the global discretization . is defined as interpolation of nodal variables as in Eq. 4•1; i.e.,
conceptually , and the nodal variables depends on how nodes and element, , are defined. The
1. Johnson, C., 1987, “ Numerical solution of partial differential equations by the finite element method”, Press Syndicate of
the University of Cambridge, UK.

h
u
h
u
h

h
u
h
u
h
φ u
ˆ
, ( ) u
ˆ

h
Finite Element Method Primer
286 Workbook of Applications in VectorSpace C++ Library
Chapter 4
entrance number “1” says the element formulation depends on the global variables , since the element stiff-
ness matrix and element force vector are all calculated corresponding to the interpolated value of the element
nodal variables . The entrance number “2” says the matrix representation depends on the element formula-
tion, since element formulation supplies the element stiffness matrices and element force vectors to be mapped
to the global matrix and global vector. The entrance number “3” is a redundant dependency relation. Since
depends on and EF depends on , we can conclude that EF must depend on . The entrance number 4 is
a similar redundant relation with one more step of MR depending on EF. The entrance number 5 and 7 show a
mutual dependency relation that MR depends on for MR is just the lhs and rhs to solve for , and after we
get solution from solving MR we need to map the solution vector from MR back to , since the fixed degree of
freedom is excluded from the MR, the variable number in MR is different from the number of global degree of
freedom. Therefore, depends on the knowledge of MR. The entrance number “6” has depends on EF.
When we define elements, we need to specify the element type number.
Graph Level Structure
A complicated network such as the one in Figure 4•8 may look aesthetically pleasant, but it isn’t the best for
human mind to comprehend. A clique is formed if we starts the flows of dependency steps from node MR to EF
then to it goes right back to MR itself. The members in a clique depend on each other so strongly that they
are not separable. It is much easier to understand if the relation is hierarchical. In our mind we only need to pic-
ture a simple sequence of states and top-dwon relations. We would like to change the graph into a level structure
such as a tree or even better a simple chain. These are same structures that we always preferred in procedure pro-
gramming method. Therefore, we proceed to sort out the planner graph into a graph level structure.

h
u
h
MR
EF

h
u
h
EF
MR
tetrahedron
planner graph
Figure 4•8Tetrahedron to show four components on the vertices with six edges. This can be
transformed to a planner graph with arrows to show dependency relation. The numbers
marked are the entrance numbers.
0
1
2
3
4
5
6
7
u
h
u
ˆ
e
u
h

h
u
h

h
u
h
u
h
u
h
u
h

h
u
h
Workbook of Applications in VectorSpace C++ Library 287
Basics of Finite Element Method
First we compare the degree of entrance of the four components (see TABLE 4•1) to transform, by escalation
and demotion
1
of nodes on the planner graph in Figure 4•8, into a graph level structure.
The has highest degree of entrance that means it should be at the highest root of class hierarchy. However,
and EF have same degree of entrance. Since the EF explicitly depends on . is to be escalated and EF is to
be demoted. The order in the class hierarchical is, therefore, , , EF, MR, as the order shown in TABLE 4•1
The pseudo-level structure is shown in the right-hand-side of Figure 4•9. The redundant relations, entrance num-
bers 3, 4, and 5, are drawn as light arrows. These redundant dependencies are first to be eliminated. Next, there
are still two un-resolved entrances (entrance 6 and 7 pointing downwards) in the left-hand-side of Figure 4•9,
which make the graph not to be a level structure. Therefore, in the rest of this section we will explore C++ level-
ization idioms
1
that help us to break these two dependency relations. Now not only the graph is simple to under-
stand for human mind, but also it will have a profound impact on the organization of the software components.
Firstly, with a simplified dependency hierarchy, the interfaces of the software components are much more simpli-
fied. The interaction among the components can be understood easier. For example one can just bear in mind that
only components that are lower in the hierarchy depend on those on the above. And , then, if there are exception,
such as entrances 6 and 7, we just mark them as such. On the other hand, the complicated network of software
components such as the one in the left-hand-side of Figure 4•8 will be extremely difficult to follow. There are so
many cliques among them. One nodes can lead to the other and then back to itself. The dynamical interaction
patterns among the components seems to have a life of its own. The sequence of events can be acted out differ-
ently every time. Therefore, the model based on the graph level structure will be less error proned. Secondly, the
complicated network demands all module to be developed, tested and maintained all together. Divide and con-
quer is the principal strategy that we always need to deploy in the development, testing and maintenance of a pro-
gram. The graph level structure in the right-hand-side of Figure 4•9 means that now these processes can be done
in a more modulized fashion from top level 0 down to level 3 incrementally. We discuss two dependency break-
ers in the followings.
Pointer to a Forward Declaration Class: We can apply a traditional C technique to break the dependency rela-
tion caused by entrance number 7. That is the output for solution needs the knowledge of class
Matrix_Representation. The the order of the solution vector “u”, in the main(), is corresponding to the order of
variable number in the Matrix_Representation. For output of solution, we need to map this internal order of the
Matrix_Representation back to the order of global nodal degree of freedoms according to the specification
from the problem. This breaking of dependency relations can be done with the forward declaration in traditional
1. J. Lakos, 1996, “Large-scale C++ software design”, Addison-Wesley, Reading, Massachusetts.
Component Degree of entrance
3
2
EF 2
MR 1
TABLE 4•1 Degree of entrance of the four components.

h
u
h

h
u
h
u
h
u
h

h
u
h
u
h
u
h
Finite Element Method Primer
288 Workbook of Applications in VectorSpace C++ Library
Chapter 4
C. Four separate files “u_h.h”, “u_h.cpp”, “matrix_representation.h” and “matrix_representation.cpp”, are
shown in the followings.
Ia. “u_h.h”
1 class Matrix_Representation;
2 class U_h {
3 Matrix_Representation *mr;
4 ...
5 public:
6 ...
7 Matrix_Representation* &matrix_representation() { return mr; }
8 U_h& operator=(C0&);
9 U_h& operator+=(C0&);
10 U_h& operator-=(C0&);
11 };
Ib, “u_h.cpp”
12 #include “u_h.h”
13 ...
IIa. “matrix_representation.h”
14 class Matrix_Representation {
15 ...
16 protected:
17 Global_Discretization &the_global_discretization;
18 ...
19 public:

h
u
h
EF
MR
Figure 4•9levelization of non-hierachical network into a level structure then to a
chain. The entrances 6 and 7 remained. We need to apply C++ levelization
idioms to reslove them.
Level 0
Level 1
Level 2
Level 3
6
7
0
3
4
1
5
2

h
u
h
EF
MR
6
7
0
1
2
eliminate
redundant
dependancies
(a) levelization
(b) simplify to a chain
Workbook of Applications in VectorSpace C++ Library 289
Basics of Finite Element Method
20 void __initialization(char *s);
21 ...
22 };
IIb. “matrix_representation.cpp”
23 #include “u_h.h”
24 #include “matrix_representation.h”
25 void Matrix_Representation::__initialization(char *s) {
26 if(!(the_global_discretization.u_h().matrix_representation()) )
27 the_global_discretization.u_h().matrix_representation() = this;
28 ...
29 }
30 U_h& U_h::operator=(C0& a) { ... }
31 U_h& U_h::operator+=(C0& a) { ... }
32 U_h& U_h::operator-=(C0& a) { ... }
The class U_h and class Matrix_Representation are actually depend on each other. Therefore, the implementa-
tions of them in the “cpp” extension files will require the knowledge of their definitions. That is to include the
“.h” extension files. Traditional C language (note that class can be viewed as a special case of struc) provides
mechanism to break this mutual dependency relation by forward declaration such as in line 1 that the class name
Matrix_Representation is introduced in the name scope of the translation unit “u_h.h”, on the condition that only
the name of class Matrix_Representation, not its member data or member functions are to be used in the defini-
tion of class U_h. In class U_h, we at most refer to a pointer of class Matrix_Representation, which is only an
address in the computer memory, not an actually instance of the class Matrix_Representation, because the transla-
tion unit has no knowledge yet of what class Matrix_Representation really is. Now a programmer in the devel-
oper team can compile and test “u_h.cpp” separately, without having to define class Matrix_Representation at all.
One scenario of using the forward declaration of a class and using a member pointer to it is after the entire
product has been completed and sale to the customer, if we want to change the definition and implementation of
class Matrix_Representation we do not need to recompile the file “u_h.cpp”. The changes in “.h” and “.cpp” files
of the class Matrix_Representation do not affect the object code of class U_h module. A less dramatic scenario of
using a member pointer is that a developing process is iterative and the files always need to be compiled many
times. During developing cycles, class U_h module does not need to be recompiled every time that class
Matrix_Representation is changed. Therefore we have seen a most primitive form of a compilation firewall been
set to separate the compile-time dependency among source files. In a huge project, such as the one developed in
Mentor Graphics we mentioned earlier. They may have thousands of files. It will be ridiculous that when an un-
important change of a tiny file higher in the dependency hierarchy is made. The “make” command may trigger
tens of hours in compile time to update all modules that are depending on it. Not for long you will refuse to do
any change at all. In yet another scenario, when class Matrix_Representation is intended to be encapsulated from
end-users, this same technique insulates end-users from accessing the class Matrix_Representation directly.
Certainly, the dependency relation of entrance number 7 exists, which is demanded by the problem domain,
we can only find a way to get around it. We successfully break this particular dependency and make class U_h an
independent software module, but how do we re-connect them as the problem domain required. When we define
the constructor of the class Matrix_Representation, the first line of the constructor is to call its private member
function “__initialization(char*)”. This private member function set up the current instance of
Finite Element Method Primer
290 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Matrix_Representation as the pointer to Matrix_Representation in the class U_h. We break up the dependency
relation using forward declaration now we reconnect them when an instance of class Matrix_Representation is
initiated. This closes the cyclic dependency relation, at link-time, that was broken at compile-time for making an
independent module of class U_h. Furthermore, the definitions of three public member operators “=”, “+=”, and
“-=”, which map the equation number of solution vector back to global degree of freedoms for output, are push
down the hierarchical levels. They are not defined in “u_h.cpp” with other class U_h member functions, because
the independent module class U_h has no idea what is a class Matrix_Represenation, let alone to access its infor-
mation for the mapping. Therefore, these three public member functions of class U_h are defined in
“matrix_representation.cpp” with other member functions of class Matrix_Representation. Certainly, had we not
defined these three operators anywhere, at link-time, the linker will refuse to build the executable module and
will complain that these three operators, declared in “u_h.h”, are un-resolved external references.
Element Type Register: In page 281, we have discussed the element type register from user’s code segment as
registration by
1 Element_Fomulation* Element_Formulation::type_list = 0;
2 Element_Type_Register element_type_register_instance;
3 static Truss truss_instance(element_type_register_instance); // element type number “2”
4 static T3 t3_instance(element_type_register_instance); // element type number “1”
5 static Q4 q4_instance(element_type_register_instance); // element type number “0”
The element types are registered in a list data structure. The last registered element type number is “0”, and then
the number increases backwards to the first registered element in the “type_list”. This element type numbers are
referred to when we define the element as
Omega_eh *elem = new Omega_eh(element_number, element_type_number,
material_number, nodes_per_element,
node_number_array);
This user interface design itself breaks the dependency of the definition of an element on element types. The
C++ technique to implement this design is the autonomous virtual constructor
1
. Let’s first look at the definitions
of the class Element_Formulation
1 class Element_Type_Register { public: Element_Type_Register() {} };
2 class Element_Formulation {
3 Global_Discretization& the_global_discretization;
4 ...
5 public:
6 static Element_Formulation *type_list;
7 Element_Formulation *next;
8 Element_Formulation(Element_Type_Register) :
1. see autonomous generic constructor in J. O. Coplien, 1992, “ Advanced C++: Programming styles and idioms”, Addison-
Wesley, Reading, Massachusetts.
Workbook of Applications in VectorSpace C++ Library 291
Basics of Finite Element Method
9 the_global_discretization(Globa_Discretization()) { next = type_list; type_list = this; }
10 Element_Formulation& create(int, Global_Discretization&);
11 virtual Element_Formulation* make(int, Globa_Discretization&);
12 ...
13 };
The class Element_Type_Register, in line 1, is a dummy one that is used like a signature in line 8 to indicate that
the instance of class Element_Formulation generated is for element type identifier, and the static member
type_list embedded in the Element_Formulation will be maintained automatically. This element_type_number
information is used in “Matrix_Representation::assembly()” as
1 Element_Formulation *element_type = Element_Formulation::type_list;
2 for(int i = 0; i < element_type_number; i++) element_type = element_type->next;
3 Element_Formulation ef = element_type->create(element_no, the_global_discretization);
Line 3 is to compute the Element_Formulation, and form an instance of Element_Formulation, say “ef”, it can be
used as “ef.lhs()” and “ef.rhs()” to query information. The task of “create()” is to call “make()” forward by its
delegate “rep_ptr->make()”. Since “make()” is virtual and to be redefined in the derived class. The request in line
3 is dispatched to a user defined element class. The virtual function mechanism is usually referred to as the late-
binding technique at run-time. In this case, the cyclic dependence of an element on element formulation, deliber-
ately broken for the software modulization, is re-connected at the run-time by the late-binding technique sup-
ported by C++.
Composite Class from a Dependency Graph
In Figure 4•8 and Figure 4•9, the four nodes are actually the software modules in “fe.lib” which are consist of
the classes. A class dependency graph, not including all classes, is shown in Figure 4•10. The entire picture is
much more complicated one. The definition of a compoiste class is similar to the partitioning of the graph to a
(quotient) tree structure with sets of composite vertices as composite nodes. In software design, the choice of the
composite class is somewhat more arbitrary than that of composite vertices in graph theory; as long as it is con-
ceptual meaningful to emphasis the essential and eliminate the irrelevant (i.e., the process of abstraction). For
example, it makes all sense to combined the level 0 and level 1 together and called it a Global_Discretization,
which is a discretization made to both the domain and the variables. We can even combined the
Global_Discretization class and Element_Formulation class to form a new conceptual class of
“Finite_Element_Approximation”. In this way, the designer may want to emphasize that the finite element
method is mainly consist of only two steps. One step is the finite element approximation, and the other step is the
solution in its matrix form. The coalescence of several composite classes into yet higher level of composite class
shows that the recognition of a composite class may depend on design decision on what conceptual abstraction
the designer wants to emphasize (an art), not just physical dependency relations and technical requirements to
separate them. Sometimes, the decision depends on the intent of the final product. For a product designed to be
used as a canned program, the abstraction can be put to a coherently higher level in which all the details are
encapsulated from the end users as much as possible. On the contrary, if the product is intended to be open, such
as “fe.lib” that large-scale change to the backbone structure of the program is to be permissible. Abstraction is
put down to a granularly lower level to facilitate the re-use of each composite class and therefore more flexibility
for change.
Finite Element Method Primer
292 Workbook of Applications in VectorSpace C++ Library
Chapter 4

h
u
h
Node

e
h
u
h

h
g Γ
g
h Γ
h
∈ , ∈
EF
User Defined
Elements
MR
Global
Tensors
Element
Tensors
Finite_Element_Approximation
Global_Discretization
Figure 4•10Composite class in the hierachical level structure.
Level 0
Level 1
Level 2
Level 3
Finite_Element_
Approximation
MR
Globla_Discretization
EF
MR
MR
EF
Workbook of Applications in VectorSpace C++ Library 293
Basics of Finite Element Method
4.1.4 A Program Template for Using “fe.lib”
We summarize the Section 4.1 with a template for using fe.lib to write finite element programs. It is very
much like we have an extended C++ language features that are specialized in finite element method. “fe.lib” is a
framework-based package very similar to if you are writing a graphic user interface (GUI) program. In GUI pro-
gramming, there are some routine code that you need to incorporate with its framework to make the GUI kernel
up and running. On the other hand, since finite element method requires a lot of user input to specified the prob-
lem, the fe.lib acts much like a database engine that you write a database language to define the database
schema, manipulate the data and query its contents. The fancy term client-server package may even more appro-
priate for “fe.lib”. The client-server packages for writing business applications provide a high-level library for
routine database services and GUI interfaces. Under such model, the fe.lib is the server that provides the basic
mechanisms in finite element method for user programs to implement their own design policies in the vast area
of finite element problem domain.
A user program template is illustrated in the followings
//==========================================================================
// Step 1: Global_Discretization
//==========================================================================
1 Omega_h::Omega_h{ // define discretizaed global domain
// define nodes
2 ...
// define elements
3 ...
4 }
5 gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& oh) { // define boundary conditions
6 __initiialization(df, oh); // initialize internal data structure
// define b.c.
7 ...
8 }
//==========================================================================
// Step 2: Element_Formulation
//==========================================================================
9 class UserEL : public Element_Formulation { // define user element
10 public:
11 UserEL(Element_Type_Register a) : Element_Formulation(a) {}
12 Element_Formulation *make(int, Global_Discretization&);
13 UserEL(int, Global_Discretization&);
14 };
15 Element_Formulation* UserEL::make(int en, Global_Discretization& gd) {
16 return new UserEL(en, gd);
17 }
18 UserEL::UserEL(int en, Global_Discretization& gd) : Element_Formulation(en, gd) {
Finite Element Method Primer
294 Workbook of Applications in VectorSpace C++ Library
Chapter 4
// define element formulation constructor
19 ...
20 }
21 Element_Formulation* Element_Formulation::type_list = 0; // register elements
22 Element_Type_Register element_type_register_instance;
23 static UserEL userel_instance(element_type_register_instance);
//==========================================================================
// Step 3: Matrix_Representation and Solution Phase
//==========================================================================
24 int main() {
25 int ndf = 1; // instantiation of Global_Discretization
26 Omega_h oh;
27 gh_on_Gamma_h gh(ndf, oh);
28 U_h uh(ndf, oh);
29 Global_Discretization gd(oh, gh, uh);
30 Matrix_Representation mr(gd);
31 mr.assembly(); // assemble the global matrix
32 C0 u = ((C0)(mr.rhs())) / ((C0)(mr.lhs())); // solution phase
33 gd.h_h(); = u; gd.u_h() = gd.gh_on_gamma_h(); // update solution
34 cout << gd.u_h(); // output solution
35 return 0;
36 }
Many segments and their variations of this template have been discussed in 4.1.2. The rest of this Chapter con-
sists of concrete examples of writing user programs using this template.
Workbook of Applications in VectorSpace C++ Library 295
One Dimensional Problems
4.2 One Dimensional Problems
We intent to go through many proto-type problems, in one dimension, to demonstrate a wide mathematical
variety in the finite element method.
4.2.1 A Second-Order Ordinary Differential Equation (ODE)
Considering a second-order differential equation we have solved using Rayleigh-Ritz method (Eq. 3•55 of
Chapter 3 in page 201)
1
Eq. 4•16
with three sets of different boundary conditions
1. Dirichlet boundary conditions—u(0) = u(1) = 0
2. Neumann boundary condition—u’(0) = u’(1) = 0
3. Mixed boundary conditions—u(0) = 0, and u’(1) = 0
The Galerkin weak formulation is
a(φ
e
i
, φ
e
j
) - (φ
e
i
, f) =
Eq. 4•17
1. Dirichlet boundary conditions: From Eq. 4•9 and Eq. 4•10 we have the element stiffness matrix as
Eq. 4•18
and the element force vector as
Eq. 4•19
The last identity is obtained, since the essential and natural boundary conditions are all homogeneous the second
term and the third term always vanish. In more general cases that they are not homoge-
1. p. 367-371 in J.N. Reddy, 1986, “Applied functional analysis and variational methods in engineering”, McGraw-Hill, Inc.
x
2
2
d
d u
– πx 0 x 1 < < , cos =
φ
e
i
d
2
φ
e
j
dx
2
----------- φ
e
i
πx cos –
¸ ,
¸ _
dx
0
1


e
i
dx
--------- –

e
j
dx
--------- φ
e
i
πx cos +
¸ ,
¸ _
dx φ
e
i

e
j
dx
---------
0
1
+
0
1


e
i
dx
--------- –

e
j
dx
--------- φ
e
i
πx cos +
¸ ,
¸ _
dx
0
1

0 = = =
k
e
ij
a φ
e
i
φ
e
j
, ( )

e
i
dx
---------

e
j
dx
---------
¸ ,
¸ _
dx
0
1

= =
f
e
i
φ
e
i
f , ( ) φ
e
i
h , ( )
Γ
a φ
e
i
φ
e
j
, ( )u
e
j
– + φ
e
i
πx cos ( )dx
0
1

= =
φ
e
i
h , ( )
Γ
a φ
e
i
φ
e
j
, ( )u
e
j

Finite Element Method Primer
296 Workbook of Applications in VectorSpace C++ Library
Chapter 4
nous conditions, the default behaviors of “fe.lib” will deal with these two terms behind the scene as long as you
have not overwritten them as we have discussed in the previous section.
Linear Line Element
We can choose the linear interpolation functions for both variable interpolation (Eq. 4•1) and
coordinate transformation rule (Eq. 4•6); i.e., an isoparametric element as
Eq. 4•20
This is the linear interpolation functions we have used for integration of a line segment in Chapter 3 (Eq. 3•10
and Eq. 3•11 of Chapter 3).
The finite element program using VectorSpace C++ Library and “fe.lib” to implement the linear element is
shown in Program Listing 4•1. We use the program template in the previous section. First, we define nodes and
elements in “Omega_h::Omega_h()”. This constructor for the discretized global domain defines nodes with their
node numbers and nodal coordinates as
1 double v = (double)i/(double)element_no; // nodal coordinates, 0 < x < 1
2 Node *node = new Node(global_node_number,
3 spatial_dimension_number,
4 &v);
5 the_node_array.add(node);
The elements are defined with global node number associated with the element as
1 int ena[2]; ena[0] = first_node_number; ena[1] = ena[0]+1;
2 Omega_eh* elem = new Omega_eh(element_number,
3 element_type_number,
4 matrial_type_number,
5 number_of_node_per_element,
6 ena);
7 the_omega_eh_array.add(elem);
Three sets of boundary conditions are (1) Dirichlet (2) Neumann, and (3) Mixed. The corresponding code seg-
ments can be turned on or off with a macro definitions set, at compile time, as
1 #if defined(__TEST_MIXED_BOUNDARY_CONDITION)
2 gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
3 __initialization(df, omega_h);
4 the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet;
5 the_gh_array[node_order(0)][0] = 0.0;
6 the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Neumann;
7 the_gh_array[node_order(node_no-1)][0] = 0.0;
u
e
h
φ
e
i
u
ˆ
e
i

x φ
e
i
x
e
i

φ
e
0
1
2
--- 1 ξ – ( ) and φ
e
1
1
2
--- 1 ξ + ( ) = , =
Workbook of Applications in VectorSpace C++ Library 297
One Dimensional Problems
#include "include\fe.h"
static const int node_no = 9; static const int element_no = 8; static const int spatial_dim_no = 1;
Omega_h::Omega_h() {
for(int i = 0; i < node_no; i++) {
double v; v = ((double)i)/((double)element_no);
Node* node = new Node(i, spatial_dim_no, &v); the_node_array.add(node);
}
int ena[2];
for(int i = 0; i < element_no; i++) {
ena[0] = i; ena[1] = ena[0]+1;
Omega_eh* elem = new Omega_eh(i, 0, 0, 2, ena); the_omega_eh_array.add(elem);
}
}
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
__initialization(df, omega_h);
the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Dirichlet;
}
class ODE_2nd_Order : public Element_Formulation {
public:
ODE_2nd_Order(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
ODE_2nd_Order(int, Global_Discretization&);
};
Element_Formulation* ODE_2nd_Order::make(int en, Global_Discretization& gd) {
return new ODE_2nd_Order(en,gd);
}
static const double PI = 3.14159265359;
ODE_2nd_Order::ODE_2nd_Order(int en, Global_Discretization& gd)
: Element_Formulation(en, gd) {
Quadrature qp(spatial_dim_no, 2);
H1 Z(qp),
N=INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 2, 1, qp);
N[0] = (1-Z)/2; N[1] = (1+Z)/2;
H1 X = N*xl;
H0 Nx = d(N)(0)/d(X);
J dv(d(X));
stiff &= (Nx * (~Nx)) | dv;
force &= ( ((H0)N)*cos(PI*((H0)X)) )| dv;
}
Element_Formulation* Element_Formulation::type_list = 0;
static Element_Type_Register element_type_register_instance;
static ODE_2nd_Order ode_2nd_order_instance(element_type_register_instance);
int main() {
const int ndf = 1;
Omega_h oh; gh_on_Gamma_h gh(ndf, oh);
U_h uh(ndf, oh); Global_Discretization gd(oh, gh, uh);
Matrix_Representation mr(gd);
mr.assembly();
C0 u = ((C0)(mr.rhs())) / ((C0)(mr.lhs()));
gd.u_h() = u; gd.u_h() = gd.gh_on_gamma_h();
cout << gd.u_h();
return 0;
}
Definte discretizaed global domain
define nodes
define elements
define boundary conditions
u(0) = u(1) = 0
instantiate fixed and free variables and
Global_Discretization
Define user element “ODE_2nd_Order”
1d Gauss Quadrature
N
0
= (1-ξ)/2, N
1
= (1+ξ)/2
coordinate transformation rule
N
,x
the Jacobian
, and
register element
Matrix Form
assembly all elements
solve linear algebraic equations
update solution and B.C.
output
k
e
ij

e
i
dx
---------

e
j
dx
---------
¸ ,
¸ _
dx
0
1

= f
e
i
φ
e
i
πx cos dx
0
1

=
Listing 4•1 Dirichlet boundary condition u(0) = u(1) = 0, for the differential equation - u” = f (project:
“2nd_order_ode” in project workspace file “fe.dsw” (in case of MSVC) under directory “vs\ex\fe”).
Finite Element Method Primer
298 Workbook of Applications in VectorSpace C++ Library
Chapter 4
8 }
9 #elif defined(__TEST_NEUMANN_BOUNDARY_CONDITION)
10 gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
11 __initialization(df, omega_h);
12 the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Neumann;
13 the_gh_array[node_order(0)][0] = 0.0;
14 the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Neumann;
15 the_gh_array[node_order(node_no-1)][0] = 0.0;
16 the_gh_array[node_order((node_no-1)/2)](0) = gh_on_Gamma_h::Dirichlet;
17 the_gh_array[node_order((node_no-1)/2)][0] = 0.0;
18 }
19 #else
20 gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
21 __initialization(df, omega_h);
22 the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet;
23 the_gh_array[node_order(0)][0] = 0.0;
24 the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Dirichlet;
25 the_gh_array[node_order(node_no-1)][0] = 0.0;
26 }
27 #endif
The Dirichlet boundary conditions is taken as the default macro definition. The constraint type selector is the
“operator () (int dof)”. We can assign type of constraint to the corresponding degree of freedom as
“gh_on_Gamma_h::Neumann” or “gh_on_Gamma_h::Dirichlet”. The default constraint type is Neumann con-
dition. The constraint value selector is the “operator [ ](int dof)”. The default constraint value is “0.0”. In other
words, you can eliminate lines 5-7, lines12-15, and lines 17, 23, 25, and the results should be the same.
The added essential boundary conditions on the middle point of the problem domain (line 16, and 17) are
necessary for the Neumann boundary conditions for this problem, because the solution is not unique under such
boundary conditions only.
“fe.lib” requires the following codes to ochestrate the polymorphic mechanism of the Element_Formulation
to setup the element type register. For a user defined class of “ODE_2nd_Order” derived from class
Element_Formulation we have
1 class ODE_2nd_Order : public Element_Formulation {
2 public:
3 ODE_2nd_Order(Element_Type_Register a) : Element_Formulation(a) {}
4 Element_Formulation *make(int, Global_Discretization&);
5 ODE_2nd_Order(int, Global_Discretization&);
6 };
7 Element_Formulation* ODE_2nd_Order::make(int en, Global_Discretization& gd) {
8 return new ODE_2nd_Order(en,gd);
9 }
10 Element_Formulation* Element_Formulation::type_list = 0;
Workbook of Applications in VectorSpace C++ Library 299
One Dimensional Problems
11 static Element_Type_Register element_type_register_instance;
12 static ODE_2nd_Order ode_2nd_order_instance(element_type_register_instance);
Lines 10 and 11 setup the data for registration and Line 12 register the element formulation “ODE_2nd_Order”.
Line 5 is the constructor for class ODE_2nd_Order where we defined the user customized element formulation as
1 static const double PI = 3.14159265359;
2 ODE_2nd_Order::ODE_2nd_Order(int en, Global_Discretization& gd)
3 : Element_Formulation(en, gd) {
4 Quadrature qp(spatial_dim_no, 2); // 1d, 2-pts Gauss quadrature
5 H1 Z(qp), // natural coordinate—ξ
6 N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( // “shape functions”
7 "int, int, Quadrature", 2/*nen*/, 1/*nsd*/, qp);
8 N[0] = (1-Z)/2; N[1] = (1+Z)/2; // N
0
=(1-ξ)/2, N
1
= (1+ξ)/2
9 H1 X = N*xl; // coordinate transformation
10 H0 Nx = d(N)(0)/d(X); // N
,x

11 J dv(d(X)); // the Jacobian, X


12 stiff &= (Nx * (~Nx)) | dv;
13 force &= ( ((H0)N)*cos(PI*((H0)X)) )| dv; // , and
14 }
For the element stiffness matrix, instead of “stiff &= (Nx* (~Nx)) | dv;”, the tensor product operator “H0&
H0::operator%(const H0&)” in VectorSpace C++ can be used for expressing
Eq. 4•21
as
stiff &= (Nx%Nx) | dv;
The instantiation of global discretized domain, fixed and free variables, and matrix representation and solu-
tion phase are taken directly from the template without modification
1 int main() {
2 const int ndf = 1;
3 Omega_h oh; // global discretizaed domain—
4 gh_on_Gamma_h gh(ndf, oh); // fixed variables —
5 U_h uh(ndf, oh); // free variables—
6 Global_Discretization gd(oh, gh, uh); // the class Global_Discretization
7 Matrix_Representation mr(gd);
8 mr.assembly(); // assembly all elements
9 C0 u = ((C0)(mr.rhs())) / ((C0)(mr.lhs())); // matrix solver
x N
i
x
e
i

k
e
i j

e
i
dx
---------

e
j
dx
---------
¸ ,
¸ _
dx
0
1

=
f
e
i
φ
e
i
πx cos dx
0
1

=
k
e

e
dx
--------

e
dx
-------- ⊗
¸ ,
¸ _
dx
0
1

=

h
g Γ
g
h Γ
h
∈ , ∈
u
h
Finite Element Method Primer
300 Workbook of Applications in VectorSpace C++ Library
Chapter 4
10 gd.u_h() = u; gd.u_h() = gd.gh_on_gamma_h(); // update free and fixed degree of freedom
11 cout << gd.u_h(); // output solution
12 return 0;
13}
The instances of global discretization, “oh”, and fixed and free variables, “gh” and “uh”, respectively, are then
all go to instantiate an instance of class Global_Discretization, “gd”. The results of using the linear line element
for the second order differential equation in finite element method are shown in Figure 4•11.
Quadratic Line Element
The quadratic interpolation functions for both variable interpolation (Eq. 4•1) and coordinate
transformation rule (Eq. 4•6) are
Eq. 4•22
These are the same quadratic interpolation functions in the Chapter 3 (Eq. 3•22).
The finite element program using VectorSpace C++ Library and “fe.lib” to implement the quadratic line ele-
ment is shown in Program Listing 4•2. The definitions of 5 nodes and 2 quadratic elements are
1 static const int node_no = 5;
2 static const int element_no = 2;
3 static const int spatial_dim_no = 1;
4 Omega_h::Omega_h() {
5 for(int i = 0; i < node_no; i++) {
6 double v; v = ((double)i)/((double)(node_no-1));
7 Node* node = new Node(i, spatial_dim_no, &v);
8 the_node_array.add(node);
0.2 0.4 0.6 0.8 1
-0.2
-0.15
-0.1
-0.05
0.2 0.4 0.6 0.8 1
-0.1
-0.05
0.05
0.1
0.2 0.4 0.6 0.8 1
-0.02
-0.01
0.01
0.02
Figure 4•11 The results from eight linear elements for (1) Dirichelt (2) Neumann and (3) Mixed
boundary condtions for the second-order ordinary differentail equation. Line segments with open
squares are finite element solutions, and continuous curves are analytical solutions.
Dirichlet
Neumann Mixed
u
e
h
φ
e
i
u
ˆ
e
i

x φ
e
i
x
e
i

φ
e
0
ξ –
2
------ 1 ξ – ( ) φ
e
1
, 1 ξ – ( ) 1 ξ + ( ) and φ
e
2
ξ
2
--- 1 ξ + ( ) = = =
Workbook of Applications in VectorSpace C++ Library 301
One Dimensional Problems
#include "include\fe.h"
static const int node_no = 5; static const int element_no = 2; static const int spatial_dim_no = 1;
Omega_h::Omega_h() {
for(int i = 0; i < node_no; i++) {
double v; v = ((double)i)/((double)element_no);
Node* node = new Node(i, spatial_dim_no, &v); the_node_array.add(node);
}
int ena[3];
for(int i = 0; i < element_no; i++) {
ena[0] = i; ena[1] = ena[0]+1; ean[2] = ena[0] + 2;
Omega_eh* elem = new Omega_eh(i, 0, 0, 3, ena); the_omega_eh_array.add(elem);
}
}
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
__initialization(df, omega_h);
the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Dirichlet;
}
class ODE_2nd_Order_Quadratic : public Element_Formulation {
public:
ODE_2nd_Order_Quadratic(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
ODE_2nd_Order_Quadratic(int, Global_Discretization&);
};
Element_Formulation* ODE_2nd_Order_Quadratic::make(int en, Global_Discretization& gd) {
return new ODE_2nd_Order_Quadratic(en,gd);
}
static const double PI = 3.14159265359;
ODE_2nd_Order::ODE_2nd_Order_Quadratic(int en, Global_Discretization& gd)
: Element_Formulation(en, gd) {
Quadrature qp(spatial_dim_no, 2);
H1 Z(qp),
N=INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 3, 1, qp);
N[0] = -Z*(1-Z)/2; N[1] = (1-Z)*(1+Z); N[2] = Z*(1+Z)/2;
H1 X = N*xl;
H0 Nx = d(N)(0)/d(X);
J dv(d(X));
stiff &= (Nx * (~Nx)) | dv;
force &= ( ((H0)N)*cos(PI*((H0)X)) )| dv;
}
Element_Formulation* Element_Formulation::type_list = 0;
static Element_Type_Register element_type_register_instance;
static ODE_2nd_Order_Quadratic
ode_2nd_order_quadratic_instance(element_type_register_instance);
int main() {
const int ndf = 1; Omega_h oh; gh_on_Gamma_h gh(ndf, oh);
U_h uh(ndf, oh); Global_Discretization gd(oh, gh, uh);
Matrix_Representation mr(gd);
mr.assembly();
C0 u = ((C0)(mr.rhs())) / ((C0)(mr.lhs()));
gd.u_h() = u; gd.u_h() = gd.gh_on_gamma_h();
cout << gd.u_h();
return 0;
}
Definte discretizaed global domain
define nodes
define elements
define boundary conditions
u(0) = u(1) = 0
instantiate fixed and free variables and
Global_Discretization
Define user element “ODE_2nd_Order”
1d Gauss Quadrature
N
0
=-ξ (1-ξ) / 2, N
1
=(1-ξ) (1+ξ),
N
2
= ξ (1+ξ) / 2
coordinate transformation rule
N
,x
the Jacobian
, and
register element
Matrix Form
assembly all elements
solve linear algebraic equations
update solution and B.C.
output
k
e
i j

e
i
dx
---------

e
j
dx
---------
¸ ,
¸ _
dx
0
1

= f
e
i
φ
e
i
πx cos dx
0
1

=
Listing 4•2 Quadratic Element for Dirichlet boundary condition u(0) = u(1) = 0 of the differential equa-
tion - u” = f (project: “quadratic_ode” in project workspace file “fe.dsw” under directory “vs\ex\fe”).
Finite Element Method Primer
302 Workbook of Applications in VectorSpace C++ Library
Chapter 4
9 }
10 int ena[3];
11 for(int i = 0; i < element_no; i++) {
12 ena[0] = i*2; ena[1] = ena[0]+1; ena[2] = ena[0]+2;
13 Omega_eh* elem = new Omega_eh(i, 0, 0, 3, ena);
14 the_omega_eh_array.add(elem);
15 }
16 }
The interpolation functions for Eq. 4•22 in the constructor of the user defined element is
1 H1 Z(qp),
2 N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE(
3 "int, int, Quadrature", 3/*nen*/, 1/*nsd*/, qp);
4 N[0] = -Z*(1-Z)/2; N[1]=(1-Z)*(1+Z); N[2]=Z*(1+Z)/2; //
The results of using only two quadratic elements are shown in Figure 4•12.
Cylindrical Coordinates For Axisymmetrical Problem
In cylindrical coordinates (r, θ, z), the Laplace operator is written as
1
Eq. 4•23
We consider an axisymmetrical heat conduction problem governing by the Laplace equation
shown in Figure 4•13.
2
This is a cross-section of two coaxial hollow cylinders. The inner and outer cylinder
1. see for example p. 667, Eq (II.4.C4) in L.E. Malvern, 1969, “Introduction to the mechanics of a continuous medium”,
Prentice-Hall, Inc., Englewood Cliffs, N.J.
2. example in p. 364-367 in J.N. Reddy, 1986, “Applied functional analysis and variational methods in engineering”,
McGraw-Hill, Inc.
φ
e
0
ξ –
2
------ 1 ξ – ( ) φ
e
1
, 1 ξ – ( ) 1 ξ + ( ) φ ,
e
2
ξ
2
--- 1 ξ + ( ) = = =
0.2 0.4 0.6 0.8 1
-0.2
-0.15
-0.1
-0.05
0.2 0.4 0.6 0.8 1
-0.02
-0.01
0.01
0.02
0.2 0.4 0.6 0.8 1
-0.1
-0.05
0.05
0.1
Figure 4•12 The results from two quadratic elements for (1) Dirichelt (2) Neumann and (3)
Mixed boundary condtions for the second-order ordinary differentail equation. Dashed curves
with open squares are finite element solutions, and continuous curves are analytical solutions.
Dirichlet
Neumann Mixed

2
u
1
r
---

∂r
----- r
∂u
∂r
------
¸ ,
¸ _
1
r
2
----

2
u
∂θ
2
---------

2
u
∂z
2
-------- + + =

2
u – 0 =
Workbook of Applications in VectorSpace C++ Library 303
One Dimensional Problems
have different thermal diffusivity “5” and “1”, respectively. For this axisymmetrical problem u depends only on
r, the second and the third terms in the left-hand-side of Eq. 4•23 dropped out. The Laplace equation becomes
Eq. 4•24
Replace dΩ = 2πr dr in the volume integral, the element stiffness matrix in Eq. 4•9 and Eq. 4•10 is obtained by
integration by parts of the weighted-residual statement with Eq. 4•24
Eq. 4•25
The C++ code for Eq. 4•25 is
“stiff &= (kapa[matrial_type_no] *(Nr%Nr)*2*PI((H0)r) ) | dr”
where “Nr” is the derivative of shape functions “N” with respect to “r”. This is implemented in Program Listing
4•3. The results are shown in Figure 4•14.
Figure 4•13Cross-section of two hollow cylinder with diffusivity of k = 5, and k =
1 for the inner and outer cylinder, respectively.
20mm
31.6mm
50mm
κ = 5
κ = 1
r
20mm 31.6mm
50mm
κ = 5
κ = 1
100
o
C 0
o
C
1
r
---
d
dr
----- κr
du
dr
------
¸ ,
¸ _
– 0 =
k
e
κ

e
dr
--------

e
dr
-------- ⊗
¸ ,
¸ _
2πrdr

=
25 30 35 40 45 50
20
40
60
80
100
Figure 4•14The solution of heat conduction of an axisymmetrical problem with two
hollow cylinders.
r
T
o
C
Finite Element Method Primer
304 Workbook of Applications in VectorSpace C++ Library
Chapter 4
#include "include\fe.h"
static const int node_no = 9; static const int element_no = 8; static const int spatial_dim_no = 1;
Omega_h::Omega_h() {
double r[9] = {20.0, 22.6, 25.1, 28.4, 31.6, 35.7, 39.8, 44.9, 50.0};
for(int i = 0; i < node_no; i++) {
Node* node = new Node(i, spatial_dim_no, r+i); the_node_array.add(node); }
int ena[2], material_type_no;
for(int i = 0; i < element_no; i++) {
ena[0] = i; ena[1] = ena[0]+1;
if(i < element_no / 2) material_type_no = 0; else material_type_no = 1;
Omega_eh* elem = new Omega_eh(i, 0, material_type_no, 2, ena);
the_omega_eh_array.add(elem);
}
}
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
__initialization(df, omega_h);
the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(0)][0] = 100.0;
the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(node_no-1)][0] = 0.0;
}class ODE_Cylindrical_Coordinates : public Element_Formulation {
public:
ODE_Cylindrical_Coordinates(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
ODE_Cylindrical_Coordinates(int, Global_Discretization&);
};
Element_Formulation* ODE_Cylindrical_Coordinates::make(int en, Global_Discretization& gd) {
return new ODE_Cylindrical_Coordinates(en,gd); }
static const double PI = 3.14159265359; static const double kapa[2] = {5.0, 1.0};
ODE_Cylindrical_Coordinates::ODE_Cylindrical_Coordinates(int en, Global_Discretization& gd)
: Element_Formulation(en, gd) {
Quadrature qp(spatial_dim_no, 2);
H1 Z(qp),
N=INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int, int, Quadrature", 2, 1, qp);
N[0] = (1-Z)/2; N[1] = (1+Z)/2;
H1 r = N*xl;
H0 Nr = d(N)(0)/d(r);
J dr(d(r));
stiff &= ( ( kapa[material_type_no]*2.0*PI*((H0)r) ) * (Nr%Nr) ) | dr;
}
Element_Formulation* Element_Formulation::type_list = 0;
static Element_Type_Register element_type_register_instance;
static ODE_Cylindrical_Coordinates ode_cylindrical_instance(element_type_register_instance);
int main() {
const int ndf = 1; Omega_h oh; gh_on_Gamma_h gh(ndf, oh);
U_h uh(ndf, oh); Global_Discretization gd(oh, gh, uh);
Matrix_Representation mr(gd);
mr.assembly();
C0 u = ((C0)(mr.rhs())) / ((C0)(mr.lhs()));
gd.u_h() = u; gd.u_h() = gd.gh_on_gamma_h();
cout << gd.u_h();
return 0;
}
Definte discretizaed global domain
define 9 nodes
define 8 elements
define boundary conditions
u(20) = 100, u(50) = 0
instantiate fixed and free variables and
Global_Discretization
Define user element “ODE_2nd_Order”
1d Gauss Quadrature
N
0
= (1-ξ) / 2, N
1
= (1+ξ) / 2
coordinate transformation rule
N
,x
, and the Jacobian
register element
Matrix Form
assembly all elements
solve linear algebraic equations
update solution and B.C.
output
k
e
κ

e
dr
--------

e
dr
-------- ⊗
¸ ,
¸ _
2πrdr

=
Listing 4•3 Axisymmetrical problem using cylindrical coordinates for the differential equation - u” = 0
(project: “cylindrical_ode” in project workspace file “fe.dsw” under directory “vs\ex\fe”).
Workbook of Applications in VectorSpace C++ Library 305
One Dimensional Problems
4.2.2 A Fourth-Order ODE —the Beam Bending Problem
We recall, from the last chapter in the sub-section on fourth-order ODE (in page 205), that from balance of
force, the transverse loading (f) is equal to the derivative of shear force (V) as
dV/dx =- f Eq. 4•26
and the shear force V is equal to the derivative of bending moment (M) as
dM/dx =- V Eq. 4•27
Therefore,
Eq. 4•28
The transverse deflection of the beam is denoted as w, and the curvature (d
2
w/dx
2
) of the beam is related to the
bending moment “M” and the flexure rigidity “EI” as
Eq. 4•29
Substituting “M” in Eq. 4•29 into Eq. 4•28 gives the fourth-order ordinary differential equation
Eq. 4•30
We consider a boundary value problem that the Eq. 4•30 is subject to the boundary conditions
1
Eq. 4•31
In the previous chapter, we solved this boundary value problem using Rayleigh-Ritz method with four weak for-
mulations—(1) irreducible formulation, (2) mixed formulation, (3) Lagrange multiplier formulation, and (4)
penalty function formulation. We use finite element method in this section to implement these four weak formu-
lations.
1. J.N. Reddy, 1986, “Applied functional analysis and variational methods in engineering”, McGraw-Hill, Inc.
x
2
2
d
d M
f =
x
2
2
d
d w M
2EI
--------- =
x
2
2
d
d
EI
x
2
2
d
d w
¸ ,

¸ _
f 0 x L < < , =
w 0 ( )
x d
dw
0 ( ) 0 EI
x
2
2
d
d w
L ( ) , M,
x d
d
EI
x
2
2
d
d w
¸ ,

¸ _
– L ( ) V L ( ) 0 = = = = =
Finite Element Method Primer
306 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Irreducible Formulation—Piecewise Cubic Hermite Shape Functions
The Lagrangian functional is obtained from integrating by parts twice on the weighted residual statement
from Eq. 4•30
Eq. 4•32
The last two terms are natural boundary conditions generated from integration by parts. Using δw = εv, where ε
is a small real number. Taking the variation of J and setting δJ(u) = 0 gives
Dropping ε, since it is arbitrary, we have
Eq. 4•33
The integrand of Eq. 4•33 contains derivative of variables up to second order. For this equation to be integrable
through out Ω, we have to require that the first derivative of the variable be continuous through out the integra-
tion domain. If the first derivative of the variable is not continuous at any point on the integration domain and its
boundaries, the second derivative of the variable on that point will be infinite, therefore, Eq. 4•33 is not integra-
ble. In other words, the first derivative of the variable at nodal points should be required to be continuous. This
is to satisfy the so-called continuity requirement. For example, we consider a two nodes line element with two
degrees of freedom associated with each notes. That is the nodal degrees of freedom are set to be = [w
0
, -dw
0
/
dx, w
1
, -dw
1
/dx] on the two nodes. The node numbers are indicated by subscripts “0” and “1”. The variables,
defined in an element domain, are defined as
Eq. 4•34
where the piecewise cubic Hermit shape functions , i = 0, 1, 2, 3 are
1,

2
1. see derivation in p. 383 in J.N. Reddy, 1986, “Applied functional analysis and variational methods in engineering”,
McGraw-Hill, Inc.
J w ( )
EI
2
------
x
2
2
d
d w
¸ ,

¸ _
2
fw – x w – VΓ
h
x d
dw
– MΓ
h
d


=
δJ w ( ) EI
d
2
δw
dx
2
-------------
x
2
2
d
d w
¸ ,

¸ _
δwf – x δ – wVΓ
h
dδw
dx
----------- –
¸ ,
¸ _

h
+ d


=
ε EI
x
2
2
d
d v
¸ ,

¸ _
x
2
2
d
d w
¸ ,

¸ _
vf – x v – VΓ
h
dv
dx
------ –
¸ ,
¸ _

h
+ d


¹ ¹
¹ ¹
' ;
¹ ¹
¹ ¹
0 = =
EI
x
2
2
d
d v
¸ ,

¸ _
x
2
2
d
d w
¸ ,

¸ _
vf – x vVΓ
h
dv
dx
------ –
¸ ,
¸ _

h
+ – d


0 =
u
ˆ
e
u
e
h
φ
e
i
u
ˆ
e
i

φ
e
i
Workbook of Applications in VectorSpace C++ Library 307
One Dimensional Problems
Eq. 4•35
The element stiffness matrix is
Eq. 4•36
The element force vector is
Eq. 4•37
where essential boundary conditions are = [w
0
, , w
1
, ], and
Eq. 4•38
where P = {V
0
,- M
0
, V
L
, -M
L
}
T
is the natural boundary conditions on boundary shear forces and boundary bend-
ing moments. Notice that in the previous chapter we take counter clockwise direction as positive for bending
moment. The sign convention taken here for the bending moment is just the opposite. The natural boundary con-
ditions are programmed to automatically taken care of in “Matrix_Representation::assembly()” where the left-
hand-side is assumed to be a positive term instead of what happened in the left-hand-side of Eq. 4•43. This is the
reason of take a minus sign in front of M for the definition of the vector P. The Program Listing 4•4 implemented
the irreducible formulation for the beam bending problem.
The solutions of the transverse deflection w and slope -dw/dx can be calculated from nodal values according
to Eq. 4•34. They are almost identical to the exact solutions in Figure 3•16 and Figure 3•17 of the last chapter in
page 208 and page 212, respectively. Therefore, the error instead are shown in Figure 4•15. Note that the exact
2. or alternative form from p. 49 in T.J.R. Hughes, 1987,”The finite element method: Linear static and dynamic finite element
analysis”, Prentice-Hall, Inc.
φ
e
0
1 3
ξ
h
e
-----
¸ ,
¸ _
2
– 2
ξ
h
e
-----
¸ ,
¸ _
3
+ =
φ
e
1
ξ – 1
ξ
h
e
-----
¸ ,
¸ _

2
=
φ
e
2
3
ξ
h
e
-----
¸ ,
¸ _
2
2
ξ
h
e
-----
¸ ,
¸ _
3
– =
φ
e
3
ξ –
ξ
h
e
-----
¸ ,
¸ _
2
ξ
h
e
-----
¸ ,
¸ _
– =
k
e
a φ
e
φ
e
, ( ) EI
x
2
2
d
d φ
e
x
2
2
d
d φ
e

¸ ,

¸ _
dx

e

= =
f
e
i
φ
e
i
f , ( ) φ
e
i
h , ( )
Γ
a φ
e
i
φ
e
j
, ( )u
e
j
– + =
u
e
x d
dw
0

x d
dw
1

φ
e
i
f , ( ) φ
e
i
fdx and φ
e
i
h , ( )
Γ
,

e

φ
e
i
Pdx
Γ

= =
Finite Element Method Primer
308 Workbook of Applications in VectorSpace C++ Library
Chapter 4
#include "include\fe.h"
static const int node_no = 5; static const int element_no = 4; static const int spatial_dim_no = 1;
static const double L_ = 1.0; static const double h_e = L_/((double)(element_no));
static const double E_ = 1.0; static const double I_ = 1.0; static const double f_0 = 1.0;
static const double M_ = -1.0;
Omega_h::Omega_h() {
for(int i = 0; i < node_no; i++) {
double v = ((double)i)*h_e;
Node* node = new Node(i, spatial_dim_no, &v); the_node_array.add(node); }
int ena[2];
for(int i = 0; i < element_no; i++) {
ena[0] = i; ena[1] = ena[0]+1;
Omega_eh* elem = new Omega_eh(i, 0, 0, 2, ena); the_omega_eh_array.add(elem); }
}
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
__initialization(df, omega_h);
the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(0)](1) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(node_no-1)][1] = M_;
}
static const int ndf = 2; static Omega_h oh; static gh_on_Gamma_h gh(ndf, oh);
static U_h uh(ndf, oh); static Global_Discretization gd(oh, gh, uh);
class Beam_Irreducible_Formulation : public Element_Formulation {
public:
Beam_Irreducible_Formulation(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
Beam_Irreducible_Formulation(int, Global_Discretization&);
};
Element_Formulation* Beam_Irreducible_Formulation::make(int en,Global_Discretization& gd) {
return new Beam_Irreducible_Formulation(en,gd); }
Beam_Irreducible_Formulation::Beam_Irreducible_Formulation(int en, Global_Discretization&
gd) : Element_Formulation(en, gd) {
double weight[3] = {1.0/3.0, 4.0/3.0, 1.0/3.0},
h_e = fabs( ((double)(xl[0] - xl[1])) );
Quadrature qp(weight, 0.0, h_e, 3);
J d_l(h_e/2.0);
H2 Z((double*)0, qp), z = Z/h_e,
N = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE(
"int, int, Quadrature", 4/*nen x ndf*/, 1/*nsd*/, qp);
N[0] = 1.0-3.0*z.pow(2)+2.0*z.pow(3); N[1] = -Z*(1.0-z).pow(2);
N[2] = 3.0*z.pow(2)-2.0*z.pow(3); N[3] = -Z*(z.pow(2)-z);
H0 Nxx = INTEGRABLE_VECTOR("int, Quadrature", 4, qp);
for(int i = 0; i < 4; i++) Nxx[i] = dd(N)(i)[0][0];
stiff &= ( (E_*I_)* (Nxx*(~Nxx)) ) | d_l;
force &= ( ((H0)N) * f_0) | d_l;
}
Element_Formulation* Element_Formulation::type_list = 0;
static Element_Type_Register element_type_register_instance;
static Beam_Irreducible_Formulation beam_irreducible_instance(element_type_register_instance);
static Matrix_Representation mr(gd);
int main() {
mr.assembly(); C0 u= ((C0)(mr.rhs())) / ((C0)(mr.lhs()));
gd.u_h() = u; gd.u_h() = gd.gh_on_gamma_h(); cout << gd.u_h(); return 0;
}
Definte discretizaed global domain
define nodes
define elements
define boundary conditions
M(L) = -1 (positive clockwise)
instantiate fixed and free variables and
Global_Discretization
“Beam_Irreducible_Formulation”
Simpson’s rule
Hermit cubics

φ
e
0
1 3
ξ
h
e
-----
¸ ,
¸ _
2
– 2
ξ
h
e
-----
¸ ,
¸ _
3
+ =
φ
e
1
ξ – 1
ξ
h
e
-----
¸ ,
¸ _

2
=
φ
e
2
3
ξ
h
e
-----
¸ ,
¸ _
2
2
ξ
h
e
-----
¸ ,
¸ _
3
– =
φ
e
3
ξ –
ξ
h
e
-----
¸ ,
¸ _
2
ξ
h
e
-----
¸ ,
¸ _
– =
k
e
EI
x
2
2
d
d φ
e
x
2
2
d
d φ
e

¸ ,

¸ _
dx

e

=
f
e
i
φ
e
i
fdx

e

=
Listing 4•4 Beam-bending problem irreducible formulation using Hermit cubics (project:
“beam_irreducible_formulation” in project workspace file “fe.dsw” under directory “vs\ex\fe”).
Workbook of Applications in VectorSpace C++ Library 309
One Dimensional Problems
solution of the transverse deflection w is a polynomial of x up to fourth-order (see Eq. 3•68 in page 207). The
cubic approximation will not give solution identical to the exact solution.
We consider two more examples for different types of boundary conditions and loads.
1
The first example is to
have unit downward nodal load on a simply supported beam at location of x = 120 in. (Figure 4•16). The flexure
rigidity of the beam is EI = 3.456x10
10
lb in.
2
The length of the beam is 360 in. We divide the beam to two cubic
Hermit elements. The definitions of the problem is now
1 static const int node_no = 3; static const int element_no = 2; static const int spatial_dim_no = 1;
2 static const double L_ = 360.0; static const double E_I_ = 144.0*24.0e6;
3 Omega_h::Omega_h() { // discritized global 4domain
5 double v = 0.0; Node* node = new Node(0, spatial_dim_no, &v);
6 the_node_array.add(node);
7 v = 120.0; node = new Node(1, spatial_dim_no, &v);
8 the_node_array.add(node);
9 v = 360.0; node = new Node(2, spatial_dim_no, &v);
10 the_node_array.add(node);
1. Example problems from p. 390 in J.N. Reddy, 1986, “Applied functional analysis and variational methods in engineering”,
McGraw-Hill, Inc.
0.2 0.4 0.6 0.8 1
-0.0001
-0.00005
0.00005
0.0001
0.2 0.4 0.6 0.8 1
-6
2. 10
-6
4. 10
-6
6. 10
-6
8. 10
0.00001
Figure 4•15 The error (= exact solution - finite element solution) of the irreducible
formulation for beam bending problem.
∆w
x
Error
x
Error
dw
dx

Figure 4•16 Unit downward nodal loading on position x = 120. The flexure
rigidity of the beam is 3.456x10
10
. Two cubic Hermict elements are used.
120 in.
240 in.
P = -1.0 lb
flexure rigidity (EI) = 3.456x10
10
lb in.
2
0 1
2
0 1
Finite Element Method Primer
310 Workbook of Applications in VectorSpace C++ Library
Chapter 4
11 int ena[2];
12 for(int i = 0; i < element_no; i++) {
13 ena[0] = i; ena[1] = ena[0]+1;
14 Omega_eh* elem = new Omega_eh(i, 0, 0, 2, ena);
15 the_omega_eh_array.add(elem);
16 }
17 }
18 gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) { // boundary conditions
19 __initialization(df, omega_h);
20 the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet; // w(0) = 0
21 the_gh_array[node_order(0)][0] = 0.0;
22 the_gh_array[node_order(1)](0) = gh_on_Gamma_h::Neumann; // P(120) = -1.0
23 the_gh_array[node_order(1)][0] = -1.0;
24 the_gh_array[node_order(2)](0) = gh_on_Gamma_h::Dirichlet; // w(360) = 0
25 the_gh_array[node_order(2)][0] = 0.0;
26 }
Now in the computation for element force vector, you can either set f_0 = 0.0, or use conditional compilation,
with macro definition, to leave that line out. The results of this problem is shown in Figure 4•17.
The second example have distributed load
Eq. 4•39
where L = 180 in. and set f
0
= -1.0. This distributed load is a linear downward loading increases from zero at the
left to unity at the right. The moment of inertia is I = 723 in.
4
, and Young’s modulus is E = 29x10
6
psi. with
boundary conditions w(0) = w(L) = dw/dx (L) = 0. We divide the beam into four equal size cubic Hermit ele-
ments. The problem definitions for nodes, elements, and boundary conditions are
1 static const int node_no = 5; static const int element_no = 4; static const int spatial_dim_no = 1;
2 static const double L_ = 180.0; static const double element_size = L_/((double)(element_no));
3 static const double E_ = 29.0e6; static const double I_ = 723.0; static const double f_0 = -1.0;
4 Omega_h::Omega_h() { // discritized global domain
50 100 150 200 250 300 350
-0.0002
-0.00015
-0.0001
-0.00005
50 100 150 200 250 300 350
-6
-2. 10
-6
-1. 10
-6
1. 10
Figure 4•17 Finite element solution for the nodal load problem for irreducible
formulation of beam bending problem.
dw
dx
w
x
x
f x ( ) f
0
x
L
--- =
Workbook of Applications in VectorSpace C++ Library 311
One Dimensional Problems
5 for(int i = 0; i < node_no; i++) {
6 double v = ((double)i)*element_size;
7 Node* node = new Node(i, spatial_dim_no, &v);
8 the_node_array.add(node);
9 }
10 int ena[2]; // element node number array
11 for(int i = 0; i < element_no; i++) {
12 ena[0] = i; ena[1] = ena[0]+1;
13 Omega_eh* elem = new Omega_eh(i, 0, 0, 2, ena);
14 the_omega_eh_array.add(elem);
15 }
16 }
17 gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) { // boundary conditions
18 __initialization(df, omega_h);
19 the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet; // w(0) = 0
20 the_gh_array[node_order(0)][0] = 0.0;
21 the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Dirichlet; // w(L) = 0
22 the_gh_array[node_order(node_no-1)][0] = 0.0;
23 the_gh_array[node_order(node_no-1)](1) = gh_on_Gamma_h::Dirichlet; // dw/dx(L) = 0
24 the_gh_array[node_order(node_no-1)][1] = 0.0;
25 }
In the constructor of the class Beam_Irreducible_Formulation the element force vector is computed as
1 H0 X = (1-((H0)z))*xl[0]+((H0)z)*xl[1], // global coordinates; xl is the nodal coordinates
2 f = (f_0/L_)*X; // distributed load function
3 force &= ( ((H0)N) * f) | d_l;
The results of this distributed load problem using the irreducible formulation are shown in Figure 4•18. These
two extra problems are actually coded in the same project “beam_irreducible_formulation” in project workspace
file “fe.dsw” (in case of MSVC) under directory “vs\ex\fe”. They can be activated by setting corresponding
macro definitions at compile time.
25 50 75 100 125 150 175
-0.00012
-0.0001
-0.00008
-0.00006
-0.00004
-0.00002
25 50 75 100 125 150 175
-6
-2. 10
-6
-1. 10
-6
1. 10
Figure 4•18 Finite element solution of the distributed load problem for the irreducible formulation of beam
bending problem. The distributed load is a linear downward loading increases from zero at the left to unit
load at the right.
w
x
x
dw
dx
Finite Element Method Primer
312 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Mixed Formulation
In the irreducible formulation the second derivative appears in the weak formulation. We use the cubic Her-
mite functions, however, these interpolation functions are quite formidable. In the mixed formulation, we trade
somewhat more complicated variational formulations for reducing the order of derivative to satisfy the continu-
ity requirement (stated earlier in page 268). That is if “n” order derivative appears in the weak formulation, we
should have C
n-1
-continuity at the nodes, in order to have entire domain to be integrable. For example, the first
derivative w,
x
is included in the nodal variables in the irreducible formulation in the last section, which has sec-
ond derivative in the weak formulation. In the cases of higher dimensions, e.g., plate and shell, the irreducible
formulations always lead to extremely complicated schemes. The current trend for these problems is to develop
formulations that requires only C
0
-continuity.
1
Recall Eq. 4•28 and Eq. 4•29
Eq. 4•40
Integration by parts on both equations, we have the Lagrangian functional
Eq. 4•41
where the boundary conditions on the shear force and slope are and
The Euler-Lagrange equations are obtained by setting δJ(w, M)= 0 (where δw = ε
w
v
w
and δM = ε
M
v
M
)
Eq. 4•42
For the Bubnov-Galerkin method we use interpolation functions for both w and v
w
, and interpolation func-
tions for both M and v
M
. In matrix form finite element formulation from Eq. 4•42 is (dropping ε
w
and ε
M
)
1. p. 310 in T.J.R. Hughes, 1987, “The finite element method: Linear static and dynamic finite element analysis”, Prentice-
Hall, inc., Englewood cliffs, New Jersey.
x
2
2
d
d w M
2EI
--------- and
x
2
2
d
d M
, f = =
J
M
w M , ( )
x d
dw
x d
dM M
2
2EI
--------- fw + +
¸ ,
¸ _
dx M
x d
dw
¸ ,
¸ _
Γ
h
w
x d
dM
¸ ,
¸ _
Γ
h
– –
0
L

=
V
x d
dM
– = ψ
x d
dw
– =
δ
w
J
M
ε
w
x d
dv
w
x d
dM
v
w
f +
¸ ,
¸ _
dx v
w
x d
dM
¸ ,
¸ _
Γ
h

¸ ,
¸ _
0
L

0 = =
δ
M
J
M
ε
M
x d
dv
M
x d
dw
v
M
M
EI
------ +
¸ ,
¸ _
dx v
M
x d
dw
¸ ,
¸ _
Γ
h

0
L

0 = =
φ
e
w
φ
e
M
Workbook of Applications in VectorSpace C++ Library 313
One Dimensional Problems
Eq. 4•43
The natural boundary conditions specified through V is hard-wired in “fe.lib” to be automatically taken care of in
“Matrix_Representation::assembly()” where the left-hand-side is assumed to be a positive term instead of what
happened in the left-hand-side of Eq. 4•43. We can choose to take an opposite sign convention on the boundary
condition as what we have done for the bending moment boundary condition in the irreducible formulation. The
disadvantage of doing that is that we have put the burden on user to specify the program correctly. That may
often cause serious confusion. Therefore, we prefer to make the sign of Eq. 4•43 to be consistent with what is
done in the “assembly()” by changing sign as
Eq. 4•44
The Program Listing 4•5 implement the beam bending problem subject to boundary conditions in Eq. 4•31, using
Eq. 4•44. In finite element convention, the degree of freedoms for a node are packed together. We can re-arrange
the degree of freedom, for every node, corresponding to the essential boundary conditions as {w, M}
T
, and natu-
ral boundary conditions are {V, ψ}
T
The Eq. 4•44 becomes
Eq. 4•45
where subscripts indicate the element nodal number and each component in the matrix or vectors is defined as
Eq. 4•46
The submatrix/subvector component access through either continuous block selector “operator ()(int, int)” or
regular increment selector “operator[](int)” in VectorSpace C++ library makes the coding in the formula of
either Eq. 4•44 or Eq. 4•45 equally convenient.
0

e
w
dx
----------

e
M
dx
---------- ⊗
¸ ,
¸ _
dx

e


e
M
dx
----------

e
w
dx
---------- ⊗
¸ ,
¸ _
dx

e

φ
e
M
φ
e
M

EI
----------------------
¸ ,
¸ _
dx

e

w
ˆ
e
M
ˆ
e
φ –
e
w
fdx φ
e
w

h


e

φ –
e
M
ψ
Γ
h
=
0

e
w
dx
----------

e
M
dx
---------- ⊗
¸ ,
¸ _
dx

e



e
M
dx
----------

e
w
dx
---------- ⊗
¸ ,
¸ _
dx

e


φ
e
M
φ
e
M

EI
----------------------
¸ ,
¸ _
dx

e


w
ˆ
e
M
ˆ
e
φ
e
w
fdx φ
e
w

h
+

e

φ
e
M
ψ
Γ
h
=
0 a
00
0 a
01
a
00
T
b
00
a
01
T
b
01
0 a
10
0 a
11
a
10
T
b
10
a
11
T
b
11
w
ˆ
0
M
ˆ
0
w
ˆ
1
M
ˆ
1
f
0
r
0
f
1
r
1
=
a
i j

i
w
dx
----------

j
M
dx
----------dx b
i j
,

e


φ
i
M
φ
j
M
EI
---------------dx f
i
,

e

– φ
i
w
fdx φ
i
w

h
and r
i
, +

e

φ
i
M
ψ
Γ
h
= = = =
Finite Element Method Primer
314 Workbook of Applications in VectorSpace C++ Library
Chapter 4
#include "include\fe.h"
static const int node_no = 5; static const int element_no = node_no-1;
static const int spatial_dim_no = 1; static const double L_ = 1.0;
static const double h_e = L_/((double)(element_no)); static const double E_ = 1.0;
static const double I_ = 1.0; static const double f_0 = 1.0; static const double M_ = 1.0;
Omega_h::Omega_h() {
for(int i = 0; i < node_no; i++) {
double v = ((double)i)*h_e;
Node* node = new Node(i, spatial_dim_no, &v); the_node_array.add(node); }
int ena[2];
for(int i = 0; i < element_no; i++) {
ena[0] = i; ena[1] = ena[0]+1;
Omega_eh* elem = new Omega_eh(i, 0, 0, 2, ena); the_omega_eh_array.add(elem); }
}
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
__initialization(df, omega_h);
the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet; // w(0) = 0
the_gh_array[node_order(node_no-1)](1) = gh_on_Gamma_h::Dirichlet; // M(L) = M_
the_gh_array[node_order(node_no-1)][1] = M_;
}
class Beam_Mixed_Formulation : public Element_Formulation {
public:
Beam_Mixed_Formulation(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
Beam_Mixed_Formulation(int, Global_Discretization&);
};
Element_Formulation* Beam_Mixed_Formulation::make( int en, Global_Discretization& gd) {
return new Beam_Mixed_Formulation(en,gd); }
Beam_Mixed_Formulation::Beam_Mixed_Formulation(int en, Global_Discretization& gd)
: Element_Formulation(en, gd) {
Quadrature qp(spatial_dim_no, 2);
H1 Z(qp),
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE(
"int, int, Quadrature", 2/*nen*/, 1/*nsd*/, qp);
N[0] = (1-Z)/2; N[1] = (1+Z)/2;
H1 X = N*xl;
H0 Nx = d(N)(0)/d(X); J d_l(d(X));
stiff &= C0(4, 4, (double*)0); C0 stiff_sub = SUBMATRIX("int, int, C0&", 2, 2, stiff);
stiff_sub[0][1] = -(Nx * (~Nx)) | d_l; stiff_sub[1][0] = stiff_sub[0][1];
stiff_sub[1][1] = -(1.0/E_/I_)* ( (((H0)N)*(~(H0)N)) | d_l );
force &= C0(4, (double*)0); C0 force_sub = SUBVECTOR("int, C0&", 2, force);
force_sub[0] = ( (((H0)N)*f_0) | d_l );
}
Element_Formulation* Element_Formulation::type_list = 0;
static Element_Type_Register element_type_register_instance;
static Beam_Mixed_Formulation beam_mixed_instance(element_type_register_instance);
int main() {
const int ndf = 2;
Omega_h oh; gh_on_Gamma_h gh(ndf, oh);
U_h uh(ndf, oh); Global_Discretization gd(oh, gh, uh);
Matrix_Representation mr(gd);
mr.assembly(); C0 u = ((C0)(mr.rhs()))/((C0)(mr.lhs()));
gd.u_h() = u; gd.u_h() = gd.gh_on_gamma_h(); cout << gd.u_h();
return 0;
Definte discretizaed global domain
define nodes
define elements
define boundary conditions
M(L) = 1
instantiate fixed and free variables and
Global_Discretization
“Beam_Mixed_Formulation”
= {(1-ξ)/2, (1+ξ)/2}
T
φ
e
w
φ
e
M
=
k
e
10
k
e
01

e
w
dx
----------

e
M
dx
---------- ⊗
¸ ,
¸ _
dx

e

– = =
k
e
11
φ
e
M
φ
e
M

EI
----------------------
¸ ,
¸ _
dx

e

– =
f
e
0
φ
e
w
fdx

e

=
Listing 4•5 Beam-bending problem mixed formulation using linear line element (project:
“beam_mixed_formulation” in project workspace file “fe.dsw” under directory “vs\ex\fe”).
Workbook of Applications in VectorSpace C++ Library 315
One Dimensional Problems
The results are shown in Figure 4•19. The solutions at the nodal points match the exact solutions of the trans-
verse deflection and the bending moment. That is,
w
exact
(x) = ((2M+fL
2
)/4EI) x
2
- fL/(6EI) x
3
+ f/(24EI) x
4
, and
M
exact
(x) = f/2 (x-L)
2
+ M Eq. 4•47
Now we proceed to the same (1) nodal loading and (2) distributed loading cases solved in the irreducible for-
mulation. For the nodal loading case, the code for the definition of the problem gives
1 static const int node_no = 3; static const int element_no = 2; static const int spatial_dim_no = 1;
2 static const double L_ = 360.0; static const double E_ = 24.0e6; static const double I_ = 144.0;
3 Omega_h::Omega_h() {
4 double v = 0.0; Node* node = new Node(0, spatial_dim_no, &v); the_node_array.add(node);
5 v = 120.0; node = new Node(1, spatial_dim_no, &v); the_node_array.add(node);
6 v = 360.0; node = new Node(2, spatial_dim_no, &v); the_node_array.add(node);
7 int ena[2];
8 for(int i = 0; i < element_no; i++) {
9 ena[0] = i; ena[1] = ena[0]+1;
10 Omega_eh* elem = new Omega_eh(i, 0, 0, 2, ena);
11 the_omega_eh_array.add(elem);
12 }
13 }
14 gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
15 __initialization(df, omega_h);
16 the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet; // w(0) = 0
17 the_gh_array[node_order(0)](1) = gh_on_Gamma_h::Dirichlet; // M(0) = 0
18 the_gh_array[node_order(1)](0) = gh_on_Gamma_h::Neumann; // V(120) = -1.0; shear force
19 the_gh_array[node_order(1)][0] = -1.0;
20 the_gh_array[node_order(2)](0) = gh_on_Gamma_h::Dirichlet; // w(360) = 0
21 the_gh_array[node_order(2)](1) = gh_on_Gamma_h::Dirichlet; // M(360) = 0
22 }
0.2 0.4 0.6 0.8 1
1.1
1.2
1.3
1.4
1.5
0.2 0.4 0.6 0.8 1
0.1
0.2
0.3
0.4
0.5
0.6
Figure 4•19 Transverse deflection “w” and bending moment “M” from mixed
formulation. The dashed line segments with open squares are finite element
solutions, and the solid curves are the exact solutions.
w
M
x x
Finite Element Method Primer
316 Workbook of Applications in VectorSpace C++ Library
Chapter 4
For the element force vector we can either set f_0 = 0 or just comment out the corresponding statement for effi-
ciency. The result of the nodal loading case is shown in Figure 4•20. The bending moment solution is exact for
this case.
The problem definition in C++ code for the distributed loading case is
1 static const int node_no = 5; static const int element_no = 4; static const int spatial_dim_no = 1;
2 static const double L_ = 180.0; static const double element_size = L_/((double)(element_no));
3 static const double E_ = 29.0e6; static const double I_ = 723.0; static const double f_0 = -1.0;
4 Omega_h::Omega_h() {
5 for(int i = 0; i < node_no; i++) {
6 double v = ((double)i)*element_size;
7 Node* node = new Node(i, spatial_dim_no, &v); the_node_array.add(node);
8 }
9 int ena[2];
10 for(int i = 0; i < element_no; i++) {
11 ena[0] = i; ena[1] = ena[0]+1;
12 Omega_eh* elem = new Omega_eh(i, 0, 0, 2, ena); the_omega_eh_array.add(elem);
13 }
14 }
15 gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
16 __initialization(df, omega_h);
17 the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet; // w(0) = 0
18 the_gh_array[node_order(0)](1) = gh_on_Gamma_h::Dirichlet; // M(0) = 0
19 the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Dirichlet; // w(L) = 0
20 the_gh_array[node_order(node_no-1)](1) = gh_on_Gamma_h::Neumann; // dw/dx(L) = 0
21 }
50 100 150 200 250 300 350
20
40
60
80
50 100 150 200 250 300 350
-0.0002
-0.00015
-0.0001
-0.00005
Figure 4•20Transverse deflection w and bending moment M for the nodal loading
problem using linear interpolation functions for both w and M.
x
x
w
M
Workbook of Applications in VectorSpace C++ Library 317
One Dimensional Problems
The element force vector in the constructor of class “Beam_Mixed_Formulation” is to define the loading function
1 H0 f = (f_0/L_)*((H0)X);
2 force &= C0(4, (double*)0);
3 C0 force_sub = SUBVECTOR("int, C0&", 2, force);
4 force_sub[0] = ( (((H0)N)*f) | d_l );
The results of this problem are shown in Figure 4•21.
In the irreducible formulation, we are required to include the higher-order derivatives be interpolated using
the abstruse cubic Hermite functions. In the mixed formulation this requirement is relaxed. However, both the
irreducible and the mixed formulation require one more variable (-dw/dx, and M, respectively) to be solved
together with w. This increases the number of degrees of freedom in the matrix solution process. This can be dis-
advantageous for a large-size problem.
25 50 75 100 125 150 175
-2000
-1500
-1000
-500
500
1000
25 50 75 100 125 150 175
-0.00012
-0.0001
-0.00008
-0.00006
-0.00004
-0.00002
Figure 4•21Transverse deflection w and bending moment M for the distributed
loading problem using linear interpolation functions for both w and M.
w
x
x M
Finite Element Method Primer
318 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Lagrange Multiplier Formulation
Recall Eq. 4•32 that the Lagrangian functional for the irreducible formulation is
Eq. 4•48
Now, in the context of constrained optimization discussed in Chapter 2, we define constraint equation for nega-
tive slope ψ that
Eq. 4•49
Substituting into Eq. 4•48, we have
Eq. 4•50
The minimization of Eq. 4•50 subject to constraint of Eq. 4•49 using Lagrange multiplier method (with the
Lagrange multiplier λ) leads to the Lagrangian functional in the form of Eq. 2•11 of Chapter 2 in page 118 as
Eq. 4•51
The Euler-Lagrange equations are obtained from δL = 0 as (where δψ = ε
ψ
v
ψ
, δw = ε
w
v
w
, and δλ = ε
λ
v
λ
)
Eq. 4•52
Dropping the arbitrary constants of ε
ψ
, ε
w
, and ε
λ
and use interpolation functions for each of the variables {ψ, w,
λ}
T
we have, in matrix form, the finite element formulation as
J w ( )
EI
2
------
x
2
2
d
d w
¸ ,

¸ _
2
fw – x w – VΓ
h
x d
dw
– MΓ
h
d


=
C ψ w , ( ) ψ
dw
dx
------- + ≡ 0 =
ψ
dw
dx
------- – =
J ψ w , ( )
EI
2
------
x d

¸ ,
¸ _
2
fw – x w – VΓ
h
ψMΓ
h
+ d


=
ψ w λ , , ( ) J ψ w , ( ) λ C ψ w , ( ) + ≡
EI
2
------
x d

¸ ,
¸ _
2
fw – x λ ψ
dw
dx
------- +
¸ ,
¸ _
x w – d



h
ψMΓ
h
+ + d


=
δ
ψ
ε
ψ
EI
dv
ψ
dx
---------
x d

x v
ψ
λ x d


v
ψ

h
+ + d


0 = =
δ
w
ε
w
v
w
f x
dv
w
dx
----------λ x v
w
– d



h
+ d


– 0 = =
δ
λ
ε
λ
v
λ
ψ
dw
dx
------- +
¸ ,
¸ _
x d


0 = =
Workbook of Applications in VectorSpace C++ Library 319
One Dimensional Problems
Eq. 4•53
Again, the bending moment boundary conditions appears on the right-hand-side of the first equation is negative.
This is in conflict with the nodal loading input is positive on the right-hand-side assumed in the implementation
of the “Matrix_Rxpresentation::assembly()”. In order to keep the convention of counter clock-wise rotation as
positive, we can change sign on the first row of Eq. 4•53 as
Eq. 4•54
Again, the degree of freedoms for each node can be packed together just as in Eq. 4•45. With the aid of the regu-
lar increment selector “operator[](int), the Eq. 4•54 is sufficient clear without really needing to rewrite to the
form of Eq. 4•45. The Program Listing 4•6 implemented the Eq. 4•54 with linear interpolation functions
{ }
T
for all three variables. The essential boundary conditions are {ψ, w, λ}
T
, and the natural boundary
conditions are {M, V, 0}
T
The results are shown in Figure 4•22 which are compared to the exaction solutions.

Eq. 4•55
ψ and λ is obtained by differentiating the exact solution of w(x) in the first line from the corresponding defini-
tions. The shear force solution, the lagrange multiplier λ per se, coincides with the exact solution..
EI

e
ψ
dx
----------

e
ψ
dx
---------- ⊗ x d

e

0 φ
e
ψ
φ
e
λ
⊗ x d

e

0 0

e
w
dx
---------- φ
e
λ
⊗ x d

e

φ
e
λ
φ
e
ψ
⊗ x d

e

φ
e
λ

e
w
dx
---------- ⊗ x d

e

0
ψ
ˆ
e
w
ˆ
e
λ
ˆ
e
φ
e
ψ

h

φ
e
w
f x φ
e
w
V
Γ
h
+ d

e

0
=
E – I

e
ψ
dx
----------

e
ψ
dx
---------- ⊗ x d

e

0 φ
e
ψ
φ
e
λ
⊗ x d

e


0 0

e
w
dx
---------- φ
e
λ
⊗ x d

e

φ
e
λ
φ
e
ψ
⊗ x d

e

φ
e
λ

e
w
dx
---------- ⊗ x d

e

0
ψ
ˆ
e
w
ˆ
e
λ
ˆ
e
φ
e
ψ

h
φ
e
w
f x φ
e
w
V
Γ
h
+ d

e

0
=
φ
e
ψ
φ
e
w
φ
e
λ
, ,
w x ( )
2M fL
2
+
4EI
----------------------
¸ ,
¸ _
x
2
fL
6EI
---------x
3

f
24EI
------------x
4
+ =
ψ x ( )
2M fL
2
+ ( ) –
2EI
------------------------------
x
fL
2EI
---------
x
2
f
6EI
---------
x
3
– + =
λ x ( ) f L x – ( ) =
Finite Element Method Primer
320 Workbook of Applications in VectorSpace C++ Library
Chapter 4
#include "include\fe.h"
static const int node_no = 5; static const int element_no = 4; static const int spatial_dim_no = 1;
static const double L_ = 1.0; static const double h_e = L_/((double)(element_no));
static const double E_ = 1.0; static const double I_ = 1.0; static const double f_0 = 1.0;
static const double M_ = 1.0;
Omega_h::Omega_h() {
for( int i = 0; i < node_no; i++) {
double v = ((double)i)*h_e;
Node* node = new Node(i, spatial_dim_no, &v); the_node_array.add(node); }
for( int i = 0; i < element_no; i++) {
int ena[2]; ena[0] = i; ena[1] = ena[0]+1;
Omega_eh* elem = new Omega_eh(i, 0, 0, 2, ena); the_omega_eh_array.add(elem); }
}
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
__initialization(df, omega_h);
the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet; // psi(0) = -dw/dx(0) = 0
the_gh_array[node_order(0)](1) = gh_on_Gamma_h::Dirichlet; // w(0) = 0
the_gh_array[node_order(node_no-1)](2) = gh_on_Gamma_h::Dirichlet; // lambda(L) = 0;
the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Neumann; // M(L) = M_
the_gh_array[node_order(node_no-1)][0] = M_; // end bending moment
}
class Beam_Lagrange_Multiplier_Formulation : public Element_Formulation {
public:
Beam_Lagrange_Multiplier_Formulation(Element_Type_Register a)
: Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
Beam_Lagrange_Multiplier_Formulation(int, Global_Discretization&);
};
Element_Formulation* Beam_Lagrange_Multiplier_Formulation::make(int en,
Global_Discretization& gd) { return new Beam_Lagrange_Multiplier_Formulation(en,gd); }
Beam_Lagrange_Multiplier_Formulation::Beam_Lagrange_Multiplier_Formulation(int en,
Global_Discretization& gd) : Element_Formulation(en, gd) {
Quadrature qp(spatial_dim_no, 2);
H1 Z(qp), N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE(
"int, int, Quadrature", 2/*nen*/, 1/*nsd*/, qp);
N[0] = (1-Z)/2; N[1] = (1+Z)/2;
H1 X = N*xl; H0 Nx = d(N)(0)/d(X); J d_l(d(X));
stiff &= C0(6, 6, (double*)0); C0 stiff_sub = SUBMATRIX("int, int, C0&", 3, 3, stiff);
stiff_sub[0][0] = -((E_*I_) * Nx * (~Nx)) | d_l; stiff_sub[0][2] = -(((H0)N) % ((H0)N)) | d_l;
stiff_sub[2][0] = -( ~stiff_sub[0][2] ); stiff_sub[1][2] = (Nx % ((H0)N)) | d_l;
stiff_sub[2][1] = ~stiff_sub[1][2];
force &= C0(6, (double*)0); C0 force_sub = SUBVECTOR("int, C0&", 3, force);
force_sub[1] = (((H0)N)*f_0) | d_l;
}
Element_Formulation* Element_Formulation::type_list = 0;
static Element_Type_Register element_type_register_instance;
static Beam_Lagrange_Multiplier_Formulation lagrange(element_type_register_instance);int
main() {
const int ndf = 3; Omega_h oh; gh_on_Gamma_h gh(ndf, oh);
U_h uh(ndf, oh); Global_Discretization gd(oh, gh, uh);
Matrix_Representation mr(gd);
mr.assembly(); C0 u = ((C0)(mr.rhs()))/((C0)(mr.lhs()));
gd.u_h() = u; gd.u_h() = gd.gh_on_gamma_h();cout << gd.u_h(); return 0;
}
Definte discretizaed global domain
define nodes
define elements
define boundary conditions
M(L) = 1
instantiate fixed and free variables and
Global_Discretization
“Beam_Lagrange_Multiplier_Formulati
on”
= {(1-ξ)/2, (1+ξ)/2}
T
φ
e
ψ
φ
e
w
φ
e
λ
= =
k
e
00
EI

e
ψ
dx
----------

e
ψ
dx
---------- ⊗
¸ ,
¸ _
dx

e

– =
k
e
02
k
e
20
( ) –
T

e
ψ
dx
----------

e
λ
dx
--------- ⊗
¸ ,
¸ _
dx

e

– = =
k
e
12
k
e
21
( )
T

e
w
dx
---------- φ
e
λ
⊗ dx

e

= =
f
e
1
φ
e
w
fdx

e

=
Listing 4•6 Beam-bending problem Lagrange multipler formulation using linear line element (project:
“beam_lagrange_multiplier” in project workspace file “fe.dsw” under directory “vs\ex\fe”).
Workbook of Applications in VectorSpace C++ Library 321
One Dimensional Problems
The problem definitions for the nodal load case can be coded as the followings
1 static const int node_no = 4; static const int element_no = node_no-1; static const int spatial_dim_no = 1;
2 static const double L_ = 360.0; static const double E_ = 24.0e6; static const double I_ = 144.0;
3 static const double P_ = 1.0;
4 Omega_h::Omega_h() {
5 double v = 0.0; Node* node = new Node(0, spatial_dim_no, &v); the_node_array.add(node);
6 v = 120.0; node = new Node(1, spatial_dim_no, &v); the_node_array.add(node);
7 v = 240.0; node = new Node(2, spatial_dim_no, &v); the_node_array.add(node);
8 v = 360.0; node = new Node(3, spatial_dim_no, &v); the_node_array.add(node);
9 for(int i = 0; i < element_no; i++) {
10 int ena[2]; ena[0] = i; ena[1] = ena[0]+1;
11 Omega_eh* elem = new Omega_eh(i, 0, 0, 2, ena); the_omega_eh_array.add(elem);
12 }
13 }
14 gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
15 __initialization(df, omega_h);
16 the_gh_array[node_order(0)](1) = gh_on_Gamma_h::Dirichlet; // w(0) = 0
17 the_gh_array[node_order(1)](1) = gh_on_Gamma_h::Neumann; // f(120) = - P; shear force
18 the_gh_array[node_order(1)][1] = -P_;
19 the_gh_array[node_order(node_no-1)](1) = gh_on_Gamma_h::Dirichlet; // w(360) = 0
20 }
Again, we can just comment out the element force vector computation in the constructor of class
Beam_Lagrange_Multiplier for efficiency. The results are shown in Figure 4•23. The solution for this boundary
condition case is not acceptable. The exact solution shear force is constant within each element, while we use lin-
ear interpolation functions for the shear force. The problem is overly constrained. On the other hand, the slope
and transverse deflection require higher order of interpolation functions than the linear functions. The choice of
different order of interpolation functions and the number of nodes per variable/per element to obtain a meaning-
0.2 0.4 0.6 0.8 1
0.2
0.4
0.6
0.8
1
0.2 0.4 0.6 0.8 1
0.1
0.2
0.3
0.4
0.5
0.6 0.2 0.4 0.6 0.8 1
-1
-0.8
-0.6
-0.4
-0.2
Figure 4•22 Lagrange multiplier formulation for beam bending problem using
linear interpolation function for all three variables.
ψ
x
x x
w λ
Finite Element Method Primer
322 Workbook of Applications in VectorSpace C++ Library
Chapter 4
ful result depends on the so-called LBB-condition in finite element method that we will discussed in details in
Section 4.4
The distributed load case is defined as
1 static const int node_no = 5; static const int element_no = 4; static const int spatial_dim_no = 1;
2 static const double L_ = 180.0; static const double element_size = L_/((double)(element_no));
3 static const double E_ = 29.0e6; static const double I_ = 723.0; static const double f_0 = -1.0;
4 Omega_h::Omega_h() {
5 for(int i = 0; i < node_no; i++) {
6 double v = ((double)i)*element_size;
7 Node* node = new Node(i, spatial_dim_no, &v); the_node_array.add(node);
8 }
9 for(int i = 0; i < element_no; i++) {
10 int ena[2]; ena[0] = i; ena[1] = ena[0]+1;
11 Omega_eh* elem = new Omega_eh(i, 0, 0, 2, ena); the_omega_eh_array.add(elem);
12 }
13 }
14 gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
15 __initialization(df, omega_h);
16 the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Neumann; // M(0) = 0
17 the_gh_array[node_order(0)](1) = gh_on_Gamma_h::Dirichlet; // w(0) = 0
18 the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Dirichlet; // psi(L) = -dw/dx(L) = 0
19 the_gh_array[node_order(node_no-1)](1) = gh_on_Gamma_h::Dirichlet; // w(L) = 0
20 }
The element force vector is implemented as
1 H0 f = (f_0/L_)*((H0)X);
2 force &= C0(6, (double*)0); C0 force_sub = SUBVECTOR("int, C0&", 3, force);
3 force_sub[1] = (((H0)N)*f) | d_l;
50 100 150 200 250 300 350
-2
-1.5
-1
-0.5
0.5
50 100 150 200 250 300 350
-0.0002
-0.00015
-0.0001
-0.00005
50 100 150 200 250 300 350
-6
-1. 10
-7
-5. 10
-7
5. 10
-6
1. 10
-6
1.5 10
-6
2. 10
-6
2.5 10
Figure 4•23 The Lagrange multiplier method with all three variables interpolated
using linear element for the nodal load problem does not produce satisfactory
result.
ψ
w λ
x
x
x
exact soln.
Workbook of Applications in VectorSpace C++ Library 323
One Dimensional Problems
The results of the distributed load case are un-acceptable that the solution of λ and ψ show oscillation, while
transverse deflection w is partially “locking” which systematically underestimates the magnitude of the exact
solution (see Figure 4•24).
0.2 0.4 0.6 0.8 1
-50
-25
25
50
75
100
0.2 0.4 0.6 0.8 1
-0.0001
-0.00008
-0.00006
-0.00004
-0.00002
0.2 0.4 0.6 0.8 1
-6
-1.5 10
-6
-1. 10
-7
-5. 10
-7
5. 10
-6
1. 10
-6
1.5 10
Figure 4•24 The results of the distrubted loading case using Lagrange
multiplier formulation for the beam bending problem.
ψ w
λ
x
x
x
Finite Element Method Primer
324 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Penalty Function Formulation
From Eq. 4•49 and Eq. 4•50, the Lagrangian functional for the penalty function formulation can be written in
the form of Eq. 2•61 of Chapter 2 in page 153
Eq. 4•56
where the popular quadratic form of the penalty function is taken. The Euler-Lagrange equations obtained from
setting δ = 0 are (where δψ = ε
ψ
v
ψ
, δw = ε
w
v
w
)
Eq. 4•57
Dropping the arbitrary constants ε
ψ
and ε
w
and substituting interpolation functions for {ψ, w}, and {v
ψ
, v
w
}, the
Euler-Lagrange equations, Eq. 4•57, are re-written for the element formulation in matrix form as
Eq. 4•58
Changing the sign of the first equation to keep the right-hand-side positive, we have
Eq. 4•59
As discussed in a sub-section “Penalty Methods” on page 153 in Chapter 2, the penalty parameter ρ should be
initially set to a small number, then gradually increase its values in subsequent iterations. Starting out with a
small ρ means we are to weight more on the minimization of the objective functional (for this problem the mini-
mum energy principle in mechanics). Subsequently increasing the penalty parameter enforces the constraint
p
ψ w ρ ; , ( ) J ψ w , ( )
ρ
2
---C
2
ψ w , ( ) + ≡
EI
2
------
x d

¸ ,
¸ _
2
fw – x
ρ
2
--- ψ
dw
dx
------- +
¸ ,
¸ _
2
x w – d



h
ψMΓ
h
+ + d


=
p
δ
ψ
p
ε
ψ
EI
dv
ψ
dx
---------
x d

x ρ v
ψ
ψ
dw
dx
------- +
¸ ,
¸ _
x d


v
ψ

h
+ + d


0 = =
δ
w
p
ε
w
v
w
f x ρ
dv
w
dx
---------- ψ
dw
dx
------- +
¸ ,
¸ _
x d


v
w
– VΓ
h
+ d


– 0 = =
EI

e
ψ
dx
----------

e
ψ
dx
---------- ⊗ x ρ φ
e
ψ
φ
e
ψ
⊗ x d


+ d

e

¸ ,

¸ _
ρ φ
e
ψ

e
w
dx
---------- ⊗ x d


ρ

e
w
dx
---------- φ
e
ψ
⊗ x d


ρ

e
w
dx
----------

e
w
dx
---------- ⊗ x d


ψ
ˆ
e
w
ˆ
e
φ
e
ψ

h

φ
e
w
f x φ
e
w
V
Γ
h
+ d

e

=
EI

e
ψ
dx
----------

e
ψ
dx
---------- ⊗ x ρ φ
e
ψ
φ
e
ψ
⊗ x d


+ d

e

¸ ,

¸ _
– ρ – φ
e
ψ

e
w
dx
---------- ⊗ x d


ρ

e
w
dx
---------- φ
e
ψ
⊗ x d


ρ

e
w
dx
----------

e
w
dx
---------- ⊗ x d


ψ
ˆ
e
w
ˆ
e
φ
e
ψ

h
φ
e
w
f x φ
e
w
V
Γ
h
+ d

e

=
Workbook of Applications in VectorSpace C++ Library 325
One Dimensional Problems
gradually. In principle the, exact solution is obtained at . However, when ρ is too large the left-hand-side
matrix in Eq. 4•59 becomes ill-conditioned. The solution will be corrupted.
The Program Listing 4•7 implements Eq. 4•59 with an ad hoc penalty iterative procedure which find a local
minimum solution with respect to “w” by monitoring the convergence of “∆w”. When the divergence of ∆w first
occurs we terminate the penalty loop The choice of this termination criterion is that we do not have the value of
the original objective functional available for determining the convergence of this problem. The solutions are
shown in Figure 4•25.
In general, the two constrained cases using lagrange multiplier formulation and penalty function formulation
do not work well. The penalty method is also not very efficient. Sometimes, the results are even disastrous. The
conditions to obtain an accurate formulation in constrained formulations were area of intensive interest in the
development of the finite element method. We devote entire Section 5.1 to this issue with some canonical formu-
lations in two-dimension are discussed in details.
ρ ∞ →
0.2 0.4 0.6 0.8 1
0.1
0.2
0.3
0.4
0.5
0.6
0.2 0.4 0.6 0.8 1
-1
-0.8
-0.6
-0.4
-0.2
Figure 4•25 The solutions of end-bending moment case with penalty formulation.
ψ
x
w
x
Finite Element Method Primer
326 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Definte discretizaed global domain
define nodes
define elements
define boundary conditions
M(L) = 1
instantiate fixed and free variables and
Global_Discretization
= {(1-ξ)/2, (1+ξ)/2}
T

monitor convergence with norm(∆w)
φ
e
ψ
φ
e
w
=
k
e
00
EI

e
ψ
dx
----------

e
ψ
dx
---------- ⊗
¸ ,
¸ _
dx –

e

– =
ρ φ
e
ψ
φ
e
ψ
⊗ x d


k
e
01
k
e
10
( ) –
T
ρ

e
ψ
dx
----------

e
λ
dx
--------- ⊗
¸ ,
¸ _
dx

e

– = =
k
e
11
ρ

e
w
dx
----------

e
w
dx
---------- ⊗ x d


=
f
e
1
φ
e
w
fdx

e

=
Listing 4•7 Beam-bending problem with penalty function formulation using linear line element (project:
“beam_penalty_function_formulation” in project workspace file “fe.dsw” under directory “vs\ex\fe”).
#include "include\fe.h"
static const int node_no = 5; static const int element_no = 4; static const int spatial_dim_no = 1;
static const double L_ = 1.0; static const double h_e = L_/((double)(element_no));
static const double E_ = 1.0; static const double I_ = 1.0; static const double f_0 = 1.0;
static const double M_ = 1.0; static double k_ = 1.0;
Omega_h::Omega_h() { for(int i = 0; i < node_no; i++) { double v = ((double)i)*h_e;
Node* node = new Node(i, spatial_dim_no, &v); the_node_array.add(node); }
for(int i = 0; i < element_no; i++) { int ena[2]; ena[0] = i; ena[1] = ena[0]+1;
Omega_eh* elem = new Omega_eh(i, 0, 0, 2, ena); the_omega_eh_array.add(elem); } }
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {__initialization(df, omega_h);
the_gh_array[node_order(0)](0)=the_gh_array[node_order(0)](1)=gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(node_no-1)][0] = M_; }class Beam_Penalty_Function_Formulation :
public Element_Formulation { public:
Beam_Penalty_Function_Formulation(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make( int, Global_Discretization&);
Beam_Penalty_Function_Formulation( int, Global_Discretization&); };
Element_Formulation* Beam_Penalty_Function_Formulation::make(int en,
Global_Discretization& gd) { return new Beam_Penalty_Function_Formulation(en,gd); }
Beam_Penalty_Function_Formulation::Beam_Penalty_Function_Formulation( int en,
Global_Discretization& gd) : Element_Formulation(en, gd) {
Quadrature qp(spatial_dim_no, 2);
H1 Z(qp), N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE(
"int, int, Quadrature", 2/*nen*/, 1/*nsd*/, qp);
N[0] = (1-Z)/2; N[1] = (1+Z)/2; H1 X = N*xl; H0 Nx = d(N)(0)/d(X); J d_l(d(X));
stiff &= C0(4, 4, (double*)0); C0 stiff_sub = SUBMATRIX("int, int, C0&", 2, 2, stiff);
stiff_sub[0][0] = -( (E_*I_) * Nx * (~Nx) + k_ * (((H0)N)*(~(H0)N)) ) | d_l;
stiff_sub[0][1] = -k_* ( (((H0)N) * (~Nx)) | d_l ); stiff_sub[1][0] = -(~stiff_sub[0][1]);
stiff_sub[1][1] = k_* ( (Nx * (~Nx)) | d_l );
force &= C0(4, (double*)0); C0 force_sub = SUBVECTOR("int, C0&", 2, force);
force_sub[1] = (((H0)N)*f_0) | d_l;
}
Element_Formulation* Element_Formulation::type_list = 0;
static Element_Type_Register element_type_register_instance;
static Beam_Penalty_Function_Formulation beam_penalty_function_formulation_instance(
element_type_register_instance);
int main() {
const int ndf = 2; Omega_h oh; gh_on_Gamma_h gh(ndf, oh);
U_h uh(ndf, oh); Global_Discretization gd(oh, gh, uh);
Matrix_Representation mr(gd);
C0 w(node_no, (double*)0), w_old(node_no, (double*)0),
delta_w(node_no, (double*)0), u_optimal;
double min_energy_norm = 1.e20, k_optimal;
for( int i = 0; i < 10; i++) {
mr.assembly(); C0 u = ((C0)(mr.rhs()))/((C0)(mr.lhs()));
gd.u_h() = u; gd.u_h() = gd.gh_on_gamma_h();
for( int j = 0; j < node_no; j++) w[j] = gd.u_h()[j][1];
delta_w = ((i) ? w-w_old : w); w_old = w;
if((double)norm(delta_w) < min_energy_norm) {
min_energy_norm = norm(delta_w); u_optimal = u; k_optimal = k_; }
cout << "penalty parameter: " << k_ << " energy norm: " << norm(delta_w) << endl
<< gd.u_h() << endl; k_ *= 2.0; }
gd.u_h() = u_optimal; gd.u_h() = gd.gh_on_gamma_h();
cout << "penalty parameter: " << k_optimal << endl << gd.u_h() << endl; return 0;
}
Workbook of Applications in VectorSpace C++ Library 327
One Dimensional Problems
4.2.3 Nonlinear ODE
Consider the nonlinear problem in Chapter 3 (page 236)
Eq. 4•60
with exact solution
Eq. 4•61
Eq. 4•60 can be rewritten as,
Eq. 4•62
Parallel to the development in Chapter 3, we solve this problem in finite element with (1) Galerkin formulation,
and (2) least squares formulation.
Galerkin Formulation
Define the residuals of the problem as
Eq. 4•63
With Galerkin weightings v
h
, which is homogeneous at the boundaries, and u
h
= v
h
+ u
Γ
g
, where u
Γ
g
is the essen-
tial boundary conditions, the weighted residuals statement gives
Eq. 4•64
Integrating by parts on the first term gives the weak formulation
Eq. 4•65
An iterative algorithm is employed for this non-linear problem with u
h
interpolated at the element level as
, where “hat” denotes the nodal values.
u
x
2
2
d
d u
x d
du
¸ ,
¸ _
2
+ 1 0 x 1 with u’ 0 ( ) , < < , 0 and u 1 ( ) , 2 = = =
u
exact
x ( ) 1 x
2
+ =
x d
d
u
x d
du
1 0 x 1 with u’ 0 ( ) , < < , 0 u 1 ( ) , 2 = = =
R u
h
( )
x d
d
u
h
du
h
dx
-------- 1 – ≡
I u
h
( ) v
h
R u
h
( ) x d
0
1

≡ v
h
x d
d
u
h
du
h
dx
--------
¸ ,
¸ _
1 – x d
0
1

0 = =
I u
h
( ) u –
h
dv
h
dx
--------
du
h
dx
-------- v
h
– x d
0
1

0 = =
u
e
h
φ
e
i
u
ˆ
e
i

Finite Element Method Primer
328 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Eq. 4•66
where . The approximation in this equation is the Taylor expansion to the first-order derivatives.
That is the increment of the solution can be solved by
Eq. 4•67
where the tangent stiffness matrix, I
T
, can be defined as
Eq. 4•68
and,
Eq. 4•69
is an arbitrary constant of global nodal vector and appears on both the nominator and denominator of Eq.
4•67. Therefore, it can be dropped. We define the element tangent stiffness matrix and element residual vector as
, and Eq. 4•70
The Program Listing 4•8 implements element formulation in Eq. 4•70, then, uses an iterative algorithmic solve
for the increment of the solution with Eq. 4•67. An initial values of zero, u
0
= 0, will lead to singular left-
hand-side matrix, therefore, the initial values are set to unity, u
0
= 1.0. In the element level the nodal value of u
e
is supplied by a private member function __initialization(int) of class Non_Linear_ODE_Quadratic as “ul”
1 static int initial_newton_flag;
2 void Non_Linear_ODE_Quadratic::__initialization(int en) {
3 ul &= gd.element_free_variable(en) + gd.element_fixed_variable(en);
4 if(!initial_newton_flag) gl = 0.0;
5 }
The line 3 in the above assigns nodal free degree of freedom values plus nodal fixed degree of freedom values to
“ul”. The values of u
e
itself can be computed at the element level as
I u
ˆ
k 1 +
( ) I u
ˆ
k
δu
ˆ
k
+ ( ) I u
ˆ
k
( )
u
ˆ

∂I
u
ˆ
k
δu
ˆ
k
+ ≅ 0 = =
u
ˆ
k 1 +
u
ˆ
k
δu
ˆ
k
+ ≡
δu
ˆ
k
δu
ˆ
k
u
ˆ

∂I
u
ˆ
k
1 –
I u
ˆ
k
( )
I – u
ˆ
k
( )
I
T
---------------- = =
I
T
u
ˆ

∂I
u
ˆ
k
≡ A
e ∀
d v
ˆ
i
φ
e
i
( )
dx
------------------ φ
e
j
du
e
k
dx
--------- u
e
k

e
j
dx
--------- +
¸ ,
¸ _
x d

e


¹ ¹
' ;
¹ ¹
v
ˆ
A
e ∀

e
dx
-------- φ
e
du
e
k
dx
--------- u
e
k

e
dx
-------- +
¸ ,
¸ _
⊗ x d

e


¹ ¹
' ;
¹ ¹
= =
I u
ˆ
k
( ) v
ˆ
A
e ∀
u –
e
k

e
dx
--------
du
e
k
dx
--------- φ
e
– x d

e

=
v
ˆ
k
e
T

e
dx
-------- φ
e
du
e
k
dx
---------

e
dx
--------u
e
k
+
¸ ,
¸ _
⊗ – x d

e

≡ r
e
u
e
k

e
dx
--------
du
e
k
dx
--------- φ
e
+ x d

e


δu
h
( )k
Workbook of Applications in VectorSpace C++ Library 329
One Dimensional Problems
#include "include\fe.h"
static const int node_no = 5; static const int element_no = 2; static const int spatial_dim_no = 1;
Omega_h::Omega_h() {
for(int i = 0; i < node_no; i++) {
double v; v = ((double)i)/((double)(node_no-1));
Node* node = new Node(i, spatial_dim_no, &v); the_node_array.add(node); }
for(int i = 0; i < element_no; i++) {
int ena[3]; ena[0] = i*2; ena[1] = ena[0]+1; ena[2] = ena[0]+2;
Omega_eh* elem = new Omega_eh(i, 0, 0, 3, ena); the_omega_eh_array.add(elem); }
}
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
__initialization(df, omega_h);
the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(node_no-1)][0] = sqrt(2.0); }
static const int ndf = 1; static Omega_h oh; static gh_on_Gamma_h gh(ndf, oh);
static U_h uh(ndf, oh); static Global_Discretization gd(oh, gh, uh);
class Non_Linear_ODE_Quadratic : public Element_Formulation {
C0 ul; void __initialization(int);
public:
Non_Linear_ODE_Quadratic(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
Non_Linear_ODE_Quadratic(int, Global_Discretization&); };
static int initial_newton_flag;
void Non_Linear_ODE_Quadratic::__initialization(int en) {
ul &= gd.element_free_variable(en) + gd.element_fixed_variable(en);
if(!initial_newton_flag) gl = 0.0; }
Element_Formulation* Non_Linear_ODE_Quadratic::make(int en, Global_Discretization& gd) {
return new Non_Linear_ODE_Quadratic(en,gd); }
Non_Linear_ODE_Quadratic::Non_Linear_ODE_Quadratic(int en, Global_Discretization& gd)
: Element_Formulation(en, gd) {
__initialization(en); Quadrature qp(spatial_dim_no, 3);
H1 Z(qp), N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE(
"int, int, Quadrature", 3/*nen*/, 1/*nsd*/, qp);
N[0] = -Z*(1-Z)/2; N[1] = (1-Z)*(1+Z); N[2] = Z*(1+Z)/2;
H1 X = N*xl; J d_l(d(X)); H0 Nx = d(N)(0)/d(X); H1 U = N*ul; H0 Ux = d(U)/d(X);
stiff &= -(Nx * ~( ((H0)N)*Ux + Nx * ((H0)U) ) ) | d_l;
force &= ( ((H0)U) * Nx * Ux + ((H0)N) ) | d_l; }
Element_Formulation* Element_Formulation::type_list = 0;
static Element_Type_Register element_type_register_instance;
static Non_Linear_ODE_Quadratic non_linear_ode_quadratic_instance
(element_type_register_instance);
static Matrix_Representation mr(gd); static const double EPSILON = 1.e-12;
int main() {
C0 u, du, unit(gd.u_h().total_node_no(), (double*)0); unit = 1.0; gd.u_h() = unit;
gd.u_h() = gd.gh_on_gamma_h(); initial_newton_flag = TRUE;
do {
mr.assembly(); initial_newton_flag = FALSE; du = ((C0)(mr.rhs())) / ((C0)(mr.lhs()));
if(!(u.rep_ptr())) { u = du; u = 1.0; }
u += du; gd.u_h() = u;
cout << norm((C0)(mr.rhs())) << " , " << norm(du) << endl << gd.u_h();
(C0)(mr.lhs()) = 0.0; (C0)(mr.rhs()) = 0.0;
} while((double)norm(du) > EPSILON);
cout << gd.u_h();
return 0; }
Definte discretizaed global domain
define nodes
define elements
define boundary conditions

instantiate fixed and free variables and
Global_Discretization
reset left-hand-side and right-hand-side
x d
du
0 ( ) 0 u 1 ( ) , 2 = =
k
e
T

e
dx
-------- φ
e
du
e
k
dx
---------

e
dx
--------u
e
k
+
¸ ,
¸ _
⊗ – x d

e


r
e
u
e
k

e
dx
--------
du
e
k
dx
--------- φ
e
+ x d

e


δu
ˆ
k
u
ˆ

∂I
u
ˆ
k
1 –
I – u
ˆ
k
( ) [ ] I
T
1 –
I – u
ˆ
k
( ) [ ] = =
u
ˆ
k 1 +
u
ˆ
k
δu
ˆ
k
+ ≡
Listing 4•8 Solution of nonlinear ordinary differential equation using Galerkin formulation for finite ele-
ment (project: “nonlinear_ode” in project workspace file “fe.dsw” under directory “vs\ex\fe”).
Finite Element Method Primer
330 Workbook of Applications in VectorSpace C++ Library
Chapter 4
H1 U = N*ul; //
H0 Ux = d(U)/d(X); //
du
e
/dx
The default behavior of the class Element_Formulation is that essential boundary conditions “gl” will be
included in the computation of reaction, which is “stiff * gl”, and to be subtracted out from the right-hand-side
vector. For the iterative algorithm which solves the increment of solution, , only at the initial loop (k = 0)
when we compute , the reaction need to be subtracted out of the right-hand-side once for all. For k > 0, “gl”
is set to zero, as in line 4, to prevent the reaction to be subtracted out of the right-hand-side at every iteration.
This ad hoc mechanism is incorporated by a “initial_newton_flag” in the main() function as
1 int main() {
...
2 initial_newton_flag = TRUE;
3 do { // Newton iteration loop
4 mr.assembly();
5 intial_newton_flag = FALSE;
...
6 } while (... );
...
7 }
The “initial_newton_flag” is set to TRUE initially (line 2). After the global matrix and global vector have been
assembled for the first time (line 4), the initial_newton_flag is set to FALSE (line 5). Therefore, at the element
level the reaction can be prevent from subtracting out of the right-hand-side again. The error of this computation,
defined as the difference of the exact solution ( ) and finite element solution, is shown in Fig-
ure 4•26.The nodal solutions are almost identical to the exact solution.
u
e
h
φ
e
i
u
ˆ
e
i

δu
ˆ
k
δu
ˆ 0
u
ex
x ( )
1 x
2
+ =
0.2 0.4 0.6 0.8 1
-0.0008
-0.0006
-0.0004
-0.0002
0.0002
0.0004
0.0006
Figure 4•26 Nonlinear finite element method using Galerkin formulation.
Error =
exact - f.e. solution
x
Workbook of Applications in VectorSpace C++ Library 331
One Dimensional Problems
Least Squares Formulation
The basic idea of the least squares method is introduced in Eq. 1•26 of Chapter 1 in page 35. The first-order
condition for the minimization of the squares of the residual (Euclidean-) norm is
Eq. 4•71
Comparing to the weighted-residual statement , the weighting function w is
Eq. 4•72
For a non-linear problem, we define
Eq. 4•73
For the non-linear problem in the previous section, the residual, at the element level, is
Eq. 4•74
and the first derivative of the residual, with respect to the nodal variables ( ), is
Eq. 4•75
The second derivatives is
Eq. 4•76
From Eq. 4•73, the element tangent stiffness matrix and the element residual vector are
R u
h
( )
2
2

u
h

------------------------- 2
R u
h
( ) ∂
u
h

------------------ R u
h
( ) ,
¸ ,
¸ _
0 = =
w R u
h
( ) , ( ) 0 =
w
R u
h
( ) ∂
u
h

------------------ =
I u
h
( )
R u
h
( ) ∂
u
h

------------------ R u
h
( ) ,
¸ ,
¸ _
and I
T
u
h

∂I
u
h
≡ , ≡

2
R u
h
( )
u
h

2
--------------------- R u
h
( ) ,
¸ ,
¸ _
R u
h
( ) ∂
u
h

------------------
R u
h
( ) ∂
u
h

------------------ ,
¸ ,
¸ _
+ =
R u
e
( ) u
e
d
2
u
e
dx
2
-----------
du
e
dx
--------
¸ ,
¸ _
2
1 – + ≡
u
e
φ
e
i
u
ˆ
e
i

R u
ˆ
e
i
( ) ∂
u
ˆ
e
i

------------------ φ
e
i
d
2
u
e
dx
2
----------- u
e
d
2
φ
e
i
dx
2
----------- 2

e
i
dx
---------
du
e
dx
-------- + + =

2
R u
ˆ
e
i
( )
u
ˆ
e
i

2
--------------------- φ
e
d
2
φ
e
dx
2
----------- ⊗
d
2
φ
e
dx
2
----------- φ
e
⊗ 2

e
dx
--------

e
dx
-------- ⊗ + + =
Finite Element Method Primer
332 Workbook of Applications in VectorSpace C++ Library
Chapter 4
, and
Eq. 4•77
The Program Listing 4•9 implements Eq. 4•77. An immediate difficulty associates with the least squares formu-
lation is the presence of the second derivatives. As we have discussed in the irreducible formulation for beam
bending problem in page 306, the C
1
-continuity on node is required for the entire problem domain to be integra-
ble. Otherwise, if first derivative is not continuous on node, the second derivative on node will be infinite, and
the entire problem domain is not integrable. This means that we need to have du/dx in the set of nodal variables
to ensure the first derivative is continuous on the nodes. As in the irreducible formulation for beam bending
problem, a 2-node element can be used with the Hermite cubics discussed previously. At the element level, we
have
1 double weight[3] = {1.0/3.0, 4.0/3.0, 1.0/3.0},
2 h_e = fabs( ((double)(xl[0] - xl[1])) );
3 Quadrature qp(weight, 0.0, h_e, 3);
4 J d_l(h_e/2.0);
5 H2 Z((double*)0, qp),
6 z = Z/h_e,
7 N = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE(
8 "int, int, Quadrature", 4/*nen x ndf*/, 1/*nsd*/, qp); // Hermite cubics
9 N[0] = 1.0-3.0*z.pow(2)+2.0*z.pow(3); // u
0
10 N[1] = Z*(1.0-z).pow(2); // du
0
/dx
11 N[2] = 3.0*z.pow(2)-2.0*z.pow(3); // u
1
12 N[3] = Z*(z.pow(2)-z); // du
1
/dx
13 H0 Nx = INTEGRABLE_VECTOR("int, Quadrature", 4, qp),
14 Nxx = INTEGRABLE_VECTOR("int, Quadrature", 4, qp);
15 Nx = d(N)(0);
16 for(int i = 0; i < 4; i++) { Nxx[i] = dd(N)(i)[0][0]; }
17 H2 U = N*ul;
18 H0 Ux, Uxx; // du
e
/dx, d
2
u
e
/dx
2
19 Ux = d(U)(0);
20 Uxx = dd(U)[0][0];
21 H0 uR = ((H0)U)*Uxx + Ux.pow(2) - 1.0, // R(u)
22 Ru = ((H0)N)*Uxx + ((H0)U)*Nxx + 2.0*Nx*Ux, // dR/du
23 Ruu = (((H0)N)%Nxx) + (Nxx%((H0)N)) + 2.0*(Nx%Nx); // d
2
R/du
2
24 stiff &= ( (Ru%Ru + Ruu*uR) ) | d_l; // tangent stiffness
25 force &= -(Ru*uR) | d_l ; // residual
The Hermite cubics (lines 9-12) are the same as those in the irreducible formulation except that we have positive signs for
both du
0
/dx and du
1
/dx variables (which is taken as negative in bending problem conventionally to improve the symmetry
of the formulation).
k
e
T
R u
ˆ
e
( ) ∂
u
ˆ
e

-----------------
R u
ˆ
e
( ) ∂
u
ˆ
e

-----------------

2
R u
ˆ
e
( )
u
ˆ
e

2
--------------------R u
e
( ) + ⊗ dx

e


r
e
R u
ˆ
e
( ) ∂
u
ˆ
e

-----------------R u
e
( ) x d

e

– ≡
Workbook of Applications in VectorSpace C++ Library 333
One Dimensional Problems
#include "include\fe.h"
static const int node_no = 5;
static const int element_no = 4;
static const int spatial_dim_no = 1;
Omega_h::Omega_h() {
for( int i = 0; i < node_no; i++) {
double v; v = ((double)i)/((double)(node_no-1));
Node* node = new Node(i, spatial_dim_no, &v); the_node_array.add(node);
}
for( int i = 0; i < element_no; i++) {
int ena[2]; ena[0] = i; ena[1] = ena[0]+1;
Omega_eh* elem = new Omega_eh(i, 0, 0, 2, ena); the_omega_eh_array.add(elem);
}
}
gh_on_Gamma_h::gh_on_Gamma_h( int df, Omega_h& omega_h) {
__initialization(df, omega_h);
the_gh_array[node_order(0)](1) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(node_no-1)][0] = sqrt(2.0);
}
static const int ndf = 2; static Omega_h oh;
static gh_on_Gamma_h gh(ndf, oh);
static U_h uh(ndf, oh);
static Global_Discretization gd(oh, gh, uh);
class Non_Linear_Least_Squares : public Element_Formulation {
C0 ul; void __initialization(int);
public:
Non_Linear_Least_Squares(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
Non_Linear_Least_Squares(int, Global_Discretization&);
};
static int initial_newton_flag;
void Non_Linear_Least_Squares::__initialization(int en) {
ul &= gd.element_free_variable(en) + gd.element_fixed_variable(en);
if(!initial_newton_flag) gl = 0.0;
}
Element_Formulation* Non_Linear_Least_Squares::make(int en,
Global_Discretization& gd) { return new Non_Linear_Least_Squares(en,gd); }
Non_Linear_Least_Squares::Non_Linear_Least_Squares(int en,
Global_Discretization& gd) : Element_Formulation(en, gd) {
__initialization(en);
double weight[3] = {1.0/3.0, 4.0/3.0, 1.0/3.0},
h_e = fabs( ((double)(xl[0] - xl[1])) );
Quadrature qp(weight, 0.0, h_e, 3);
J d_l(h_e/2.0);
H2 Z((double*)0, qp),
z = Z/h_e,
N = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE(
"int, int, Quadrature", 4/*nen x ndf*/, 1/*nsd*/, qp);
N[0] = 1.0-3.0*z.pow(2)+2.0*z.pow(3);
N[1] = Z*(1.0-z).pow(2);
N[2] = 3.0*z.pow(2)-2.0*z.pow(3);
N[3] = Z*(z.pow(2)-z);
H0 Nx = INTEGRABLE_VECTOR("int, Quadrature", 4, qp),
Nxx = INTEGRABLE_VECTOR("int, Quadrature", 4, qp);
Nx = d(N)(0);
for(int i = 0; i < 4; i++) { Nxx[i] = dd(N)(i)[0][0]; }
Definte discretizaed global domain
define nodes
define elements
define boundary conditions

instantiate fixed and free variables and
Global_Discretization
Hermite cubics
x d
du
0 ( ) 0 u 1 ( ) , 2 = =
Finite Element Method Primer
334 Workbook of Applications in VectorSpace C++ Library
Chapter 4
H2 U = N*ul;
H0 Ux, Uxx;
Ux = d(U)(0);
Uxx = dd(U)[0][0];
H0 uR = ((H0)U)*Uxx + Ux.pow(2) - 1.0,
Ru = ((H0)N)*Uxx + ((H0)U)*Nxx + 2.0*Nx*Ux,
Ruu = (((H0)N)%Nxx) + (Nxx%((H0)N)) + 2.0*(Nx%Nx);
stiff &= ( (Ru%Ru + Ruu*uR) ) | d_l;
force &= -(Ru*uR) | d_l ;
}
Element_Formulation* Element_Formulation::type_list = 0;
static Element_Type_Register element_type_register_instance;
static Non_Linear_Least_Squares
non_linear_least_squares_instance(element_type_register_instance);
static Matrix_Representation mr(gd);
static const double EPSILON = 1.e-12;
int main() {
C0 p, u, du;
gd.u_h() = gd.gh_on_gamma_h();
C0 unit(gd.u_h().total_node_no()*ndf, (double*)0);
unit = 1.0;
gd.u_h() = unit;
do {
mr.assembly();
p = ((C0)(mr.rhs())) / ((C0)(mr.lhs()));
if(!(u.rep_ptr())) { u = p; u = 1.0; }
double left = 0.0, right = 1.0, length = right-left;
do {
Matrix_Representation::Assembly_Switch = Matrix_Representation::RHS;
du = (left + 0.618 * length) * p;
gd.u_h() = u + du;
(C0)(mr.rhs()) = 0.0;
mr.assembly();
double residual_golden_right = norm((C0)(mr.rhs()));
du = (left + 0.382 * length)* p;
gd.u_h() = u + du;
(C0)(mr.rhs())=0.0;
mr.assembly();
double residual_golden_left = norm((C0)(mr.rhs()));
if(residual_golden_right < residual_golden_left) left = left + 0.382 * length;
else right = left+0.618*length;
length = right - left;
} while(length > 1.e-2);
cout << "bracket: (" << left << ", " << right << ")" << endl;
u += du;
cout << "residual norm: " << norm((C0)(mr.rhs())) <<
" search direction norm: " << norm(p) << endl << “solution: “ << gd.u_h() << endl;
Matrix_Representation::Assembly_Switch = Matrix_Representation::ALL;
(C0)(mr.lhs()) = 0.0;
(C0)(mr.rhs()) = 0.0;
} while((double)norm(p) > EPSILON);
cout << gd.u_h();
return 0;
}
line search
golden section
R u
e
( ) u
e
d
2
u
e
dx
2
-----------
du
e
dx
--------
¸ ,
¸ _
2
1 – + ≡
R u
ˆ
e
i
( ) ∂
u
ˆ
e
i

------------------ φ
e
i
d
2
u
e
dx
2
----------- u
e
d
2
φ
e
i
dx
2
----------- 2

e
i
dx
---------
du
e
dx
-------- + + =

2
R u
ˆ
e
( )
u
ˆ
e

2
-------------------- φ
e
d
2
φ
e
dx
2
----------- ⊗
d
2
φ
e
dx
2
----------- φ
e
+ ⊗ + =
2

e
dx
--------

e
dx
-------- ⊗
k
e
T
R u
ˆ
e
( ) ∂
u
ˆ
e

-----------------
R u
ˆ
e
( ) ∂
u
ˆ
e

----------------- + ⊗

e



2
R u
ˆ
e
( )
u
ˆ
e

2
--------------------R u
e
( ) dx
r
e
R u
ˆ
e
( ) ∂
u
ˆ
e

-----------------R u
e
( ) x d

e

– ≡
u
ˆ
k 1 +
u
ˆ
k
δu
ˆ
k
+ ≡
Listing 4•9 Solution of nonlinear ordinary differential equation using least squares formulation for finite
element (project: “nonlinear_least_squares_ode” in project workspace file “fe.dsw” under directory
Workbook of Applications in VectorSpace C++ Library 335
One Dimensional Problems
The nonlinear iterative algorithm with classical Newton’s method shows difficulty in getting convergence. A
quick fixed is to add line search algorithm, with golden section, on top of the classical Newton’s method, which
is implemented to tame the wild search path of the classical Newton’ method as introduced in Chapter 2 (see
page 125).
1 double left = 0.0, right = 1.0, length = right-left;
2 do {
3 Matrix_Representation::Assembly_Switch = Matrix_Representation::RHS;
4 du = (left + 0.618 * length) * p;
5 gd.u_h() = u + du;
6 (C0)(mr.rhs()) = 0.0;
7 mr.assembly();
8 double residual_golden_right = norm((C0)(mr.rhs()));
9 du = (left + 0.382 * length)* p;
10 gd.u_h() = u + du;
11 (C0)(mr.rhs())=0.0;
12 mr.assembly();
13 double residual_golden_left = norm((C0)(mr.rhs()));
14 if(residual_golden_right < residual_golden_left) left = left + 0.382 * length;
15 else right = left+0.618*length;
16 length = right - left;
17 } while(length > 1.e-2);
18 ...
19 Matrix_Representation::Assembly_Switch = Matrix_Representation::ALL;
In place of evaluating the objective functional value in Chapter 2, the finite element method is to minimized the
residuals of the problem. In the loop for the golden section line search, the assembly flag is set to only assemble
the right-hand-side vector (line 3). The norm of the right-hand-side vector is used as the criterion for the line
search minimization. At outer loop where Newton’s formula is used to compute the next search direction p, the
assembly flag is reset back to assembly both the left-hand-side matrix and the right-hand-side vector (line 19).
The results are shown in Figure 4•27.
0.2 0.4 0.6 0.8 1
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.2 0.4 0.6 0.8 1
1.1
1.2
1.3
1.4
Figure 4•27 Nodal solutions (open squares) comparing to the exact solutions (solid
curves) for the nonlinear least squares formulation.
u
x x
du/dx
Finite Element Method Primer
336 Workbook of Applications in VectorSpace C++ Library
Chapter 4
4.2.4 Transient Problems
The transient problem is introduced in Section 3.3.4. We consider the parabolic equation for heat conduction
Eq. 4•78
where C is the heat capacity matrix, K is the conductivity matrix, and f is heat source vector. The variable u is
the temperature and is the time derivative of temperature. And, the hyperbolic equation for structural dynam-
ics
Eq. 4•79
where M is the consistent mass matrix, K the stiffness matrix and f the force vector. The variable u is the dis-
placement and , the second time derivative of the displacement, gives the acceleration.
Parabolic Equation
From Eq. 3•191 of Chapter 3 (in page 253),
Eq. 4•80
Considering the initial-boundary value problem in page 253
subject to Eq. 4•81
The finite element formulation for C and K is
Eq. 4•82
θ is a scalar parameter and ∆t is the time step length. The Program Listing 4•10 implements Eq. 4•80 and Eq.
4•82. At the element level, the heat capacity matrix c
e
is the additional term to the static case as
mass &= ((H0)N)%((H0)N) | dv;
The protected member functions of the base class, “Element_Formulation::__lhs()” and
“Element_Formulation::__rhs()”, need to be overwritten in the derived class Parabolic_Equation as
1 C0& Parabolic_Equation::__lhs() {
2 the_lhs &= mass + theta_* dt_*stiff; //
3 return the_lhs;
4 }
Cu
·
Ku f + + 0 =
u
·
Mu
··
Ku f + + 0 =
u
··
C ∆tθK + ( )u
n 1 +
C ∆t 1 θ – ( )K – ( )u
n
f
ˆ
– =
u ∂
t ∂
-----

2
u
∂x
2
-------- – 0 0 x 1 < < , = u 0 t , ( ) 0
∂u
∂x
------ 1 t , ( ) , 0 and u x 0 , ( ) , 1 = = =
c
e
φ
e
φ
e
⊗ x d

e

and k
e
,
∂φ
e
∂x
--------
∂φ
e
∂x
-------- ⊗ x d

e

= =
C ∆tθK +
Workbook of Applications in VectorSpace C++ Library 337
One Dimensional Problems
#include "include\fe.h"
static const int node_no = 5; static const int element_no = 4; static const int spatial_dim_no = 1;
Omega_h::Omega_h(){
for(int i = 0; i < node_no; i++) { double v;v=((double)i)/((double)element_no);
Node* node = new Node(i, spatial_dim_no, &v); the_node_array.add(node); }
for(int i = 0; i < element_no; i++) { int ena[2]; ena[0] = i; ena[1] = ena[0]+1;
Omega_eh* elem = new Omega_eh(i, 0, 0, 2, ena); the_omega_eh_array.add(elem); } }
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) { __initialization(df, omega_h);
the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Neumann; }
static const int ndf = 1; static Omega_h oh; static gh_on_Gamma_h gh(ndf, oh);
static U_h uh(ndf, oh); static Global_Discretization gd(oh, gh, uh);
class Parabolic_Equation : public Element_Formulation { C0 mass, ul;
void __initialization(int, Global_Discretization&);
public:
Parabolic_Equation(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
Parabolic_Equation(int, Global_Discretization&);
C0& __lhs(); C0& __rhs(); };
void Parabolic_Equation::__initialization(int en, Global_Discretization& gd) {
ul &= gd.element_free_variable(en); }
Element_Formulation* Parabolic_Equation::make(int en, Global_Discretization& gd) {
return new Parabolic_Equation(en,gd); }
Parabolic_Equation::Parabolic_Equation(int en, Global_Discretization& gd) :
Element_Formulation(en, gd) { __initialization(en, gd);
Quadrature qp(spatial_dim_no, 2);
H1 Z(qp),
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int, int, Quadrature", 2, 1, qp);
N[0] = (1-Z)/2; N[1] = (1+Z)/2; H1 X = N*xl; H0 Nx = d(N)(0)/d(X); J dv(d(X));
stiff &= (Nx % Nx) | dv; mass &= ( ((H0)N)%((H0)N) ) | dv; }
Element_Formulation* Element_Formulation::type_list = 0;
static Element_Type_Register element_type_register_instance;
static Parabolic_Equation parabolic_equation_instance(element_type_register_instance);
static Matrix_Representation mr(gd);
static double theta_ = 0.5; static double dt_ = 0.05;
C0& Parabolic_Equation::__lhs() { the_lhs &= mass + theta_* dt_*stiff; return the_lhs; }
C0& Parabolic_Equation::__rhs() {
Element_Formulation::__rhs();
the_rhs += (mass - (1.0-theta_)*dt_*stiff)*ul;
return the_rhs; }
int main() {
for(int i = 0; i < node_no; i++) uh[i][0] = 1.0;
gd.u_h() = gd.gh_on_gamma_h();
mr.assembly();
C0 decomposed_LHS = !((C0)(mr.lhs()));
for(int i = 0; i < 28; i++) {
C0 u = decomposed_LHS*((C0)(mr.rhs())); gd.u_h() = u;
double iptr;
if(modf( ((double)(i+1))/4.0, &iptr)==0) {
cout << "time: " << (((double)(i+1))*dt_) << ", at (0.5, 1.0), u = (" <<
gd.u_h()[(node_no-1)/2][0] << ", " << gd.u_h()[node_no-1][0] << ")" << endl; }
if(i < 27) { (C0)(mr.rhs()) = 0.0; (C0)(mr.lhs()) = 0.0; mr.assembly(); }
}
return 0;
}
Definte discretizaed global domain
define nodes
define elements
define boundary conditions
instantiate fixed and free variables and
Global_Discretization
overwrite protected member functions
heat capacitance
conductivity
initial conditions
c
e
φ
e
φ
e
⊗ x d

e

=
k
e
∂φ
e
∂x
--------
∂φ
e
∂x
-------- ⊗ x d

e

=
C ∆tθK +
C ∆t 1 θ – ( )K – ( )u
n
f
ˆ

u
n 1 +
C ∆t 1 θ – ( )K – ( )u
n
f
ˆ

C ∆tθK + ( )
--------------------------------------------------------- =
Listing 4•10 Solution of hyperbolic equation using center difference scheme in time dimension (project:
“hyperbolic_equation” in project workspace file “fe.dsw” under directory “vs\ex\fe”).
Finite Element Method Primer
338 Workbook of Applications in VectorSpace C++ Library
Chapter 4
5 C0& Parabolic_Equation::__rhs() {
6 Element_Formulation::__rhs(); //
7 the_rhs += (mass - (1.0-theta_)*dt_*stiff)*ul; //
8 return the_rhs;
9 }
In the main() function the decomposition of the left-hand-side matrix is done only once, which is outside of the
time integration loop. The results of this program are shown in Program Listing 4•10.
f
ˆ

C ∆t 1 θ – ( )K – ( )u
n
f
ˆ

0 0.2 0.4 0.6 0.8 1
0.2
0.4
0.6
0.8
1
Figure 4•28Finite element solutions for the hyperbolic equation for heat conduction.
x
u
t
0
t
0.2
t
0.4
t
0.6
t
0.8
t
1.2
t
1.4
t
1.0
Workbook of Applications in VectorSpace C++ Library 339
One Dimensional Problems
Hyperbolic Equation
From Eq. 3•211 of Chapter 3 (see page 257), is defined as
Eq. 4•83
where and , the Newmark coefficients a
i
are
Eq. 4•84
Consider the initial boundary value problem in page 258,
boundary conditions
and initial conditions u(x, 0) = sin(πx)-πx(1-x), and Eq. 4•85
The finite element formulation for consistent mass matrix and stiffness matrix is
Eq. 4•86
The damping matrix c
e
is either in the form of m
e
times damping parameter or in the form of Raleigh damping as
a linear combination of m
e
and k
e
.
1
Again, for two-node element the Hermite cubics are required for the stiffness
matrix as in the irreducible formulation of beam bending problem. The Program Listing 4•11 implements the
hyperbolic equation. Now variables , , at t
n
and , , at t
n+1
need to be registered as
1 static U_h u_old(ndf, oh); static U_h du_old(ndf, oh); static U_h ddu_old(ndf, oh);
2 static U_h u_new(ndf, oh); static U_h du_new(ndf, oh); static U_h ddu_new(ndf, oh);
These variables are supplied to the element constructor by a private member function
Hyperbolic_Equation::__initialization(int, Global_Discretization&) as
1 void Hyperbolic_Equation::__initialization(int en, Global_Discretization& gd) {
2 Omega_h& oh = gd.omega_h();
1. p. 93 and p. 339 in K-J Bathe and E.L.Wilson, 1976, “Numerical methods in finite element analysis”, Prentice-Hall, inc.,
Englewood Cliffs, New Jersey.
K
ˆ
u
n 1 +
R
ˆ
n 1 + =
K
ˆ
K a
0
M a
1
C and R
ˆ
n 1 + , + + f
n 1 +
– a
0
u
n
a
2
u
·
n
a
3
u
··
n
+ + ( )M a
1
u
n
a
4
u
·
n
a
5
u
··
n
+ + ( )C + + = =
u
··
n 1 +
a
0
u
n 1 +
u
n
– ( ) a
2
u
·
n
– a
3
u
··
n
– = u
·
n 1 +
u
·
n
a
6
u
··
n
a
7
u
··
n 1 +
+ + =
a
0
1
β∆t
2
----------- a
1
,
γ
β∆t
--------- a
2
,
1
β∆t
--------- a
3
,
1

------ 1 a
4
, –
γ
β
--- 1 a
5
, –
∆t
2
-----
γ
β
--- 2 –
¸ ,
¸ _
a
6
, ∆t 1 γ – ( ) a
7
, γ∆t = = = = = = = =

2
u
∂t
2
--------

4
u
∂x
4
-------- 0 x 1 t 0 > , < < , – =
u 0 t , ( ) u 1 t , ( )
∂u 0 t , ( )
∂x
-------------------
∂u 1 t , ( )
∂x
------------------- 0 = = = =
∂u x 0 , ( )
∂t
-------------------- 0 =
m
e
φ
e
φ
e
⊗ x d

e

and k
e
,

2
φ
e
∂x
2
-----------

2
φ
e
∂x
2
----------- ⊗ x d

e

= =
u
n
u
·
n
u
··
n
u
n 1 +
u
·
n 1 +
u
··
n 1 +
Finite Element Method Primer
340 Workbook of Applications in VectorSpace C++ Library
Chapter 4
#include "include\fe.h"
static const int node_no = 5;
static const int element_no = node_no-1;
static const int spatial_dim_no = 1;
static const double L_ = 1.0;
static const double h_e = L_/((double)(element_no));
Omega_h::Omega_h() {
for(int i = 0; i < node_no; i++) {
double v = ((double)i)*h_e;
Node* node = new Node(i, spatial_dim_no, &v);
the_node_array.add(node);
}
for(int i = 0; i < element_no; i++) {
int ena[2]; ena[0] = i; ena[1] = ena[0]+1;
Omega_eh* elem = new Omega_eh(i, 0, 0, 2, ena);
the_omega_eh_array.add(elem);
}
}
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
__initialization(df, omega_h);
the_gh_array[node_order(0)](0) =
the_gh_array[node_order(0)](1) =
the_gh_array[node_order(node_no-1)](0) =
the_gh_array[node_order(node_no-1)](1) =
gh_on_Gamma_h::Dirichlet;
}
static const int ndf = 2;
static Omega_h oh;
static gh_on_Gamma_h gh(ndf, oh);
static U_h uh(ndf, oh);
static Global_Discretization gd(oh, gh, uh);
static U_h u_old(ndf, oh); static U_h du_old(ndf, oh); static U_h ddu_old(ndf, oh);
static U_h u_new(ndf, oh); static U_h du_new(ndf, oh); static U_h ddu_new(ndf, oh);
class Hyperbolic_Equation : public Element_Formulation {
C0 mass, ul, dul, ddul;
void __initialization(int, Global_Discretization&);
public:
Hyperbolic_Equation(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
Hyperbolic_Equation(int, Global_Discretization&);
C0& __lhs();
C0& __rhs();
};
void Hyperbolic_Equation::__initialization(int en, Global_Discretization& gd) {
Omega_h& oh = gd.omega_h();
gh_on_Gamma_h& gh = gd.gh_on_gamma_h();
Global_Discretization gd_u_old(oh, gh, u_old);
ul &= gd_u_old.element_free_variable(en);
Global_Discretization gd_du_old(oh, gh, du_old);
dul &= gd_du_old.element_free_variable(en);
Global_Discretization gd_ddu_old(oh,gh,ddu_old);
ddul &=gd_ddu_old.element_free_variable(en);
}
Element_Formulation* Hyperbolic_Equation::make(int en, Global_Discretization& gd) {
return new Hyperbolic_Equation(en,gd);
}
Definte discretizaed global domain
define nodes
define elements
define boundary conditions
instantiate fixed and free variables and
Global_Discretization
overwrite protected member functions
u
n
u
·
n
u
··
n
Workbook of Applications in VectorSpace C++ Library 341
One Dimensional Problems
Hyperbolic_Equation::Hyperbolic_Equation(int en, Global_Discretization& gd) :
Element_Formulation(en, gd) {
__initialization(en, gd);
double weight[3] = {1.0/3.0, 4.0/3.0, 1.0/3.0},
h_e = fabs( ((double)(xl[0] - xl[1])) );
Quadrature qp(weight, 0.0, h_e, 3);
J d_l(h_e/2.0);
H2 Z((double*)0, qp),
z = Z/h_e,
N = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE(
"int, int, Quadrature", 4/*nen x ndf*/, 1/*nsd*/, qp);
N[0] = 1.0-3.0*z.pow(2)+2.0*z.pow(3);
N[1] = -Z*(1.0-z).pow(2);
N[2] = 3.0*z.pow(2)-2.0*z.pow(3);
N[3] = -Z*(z.pow(2)-z);
H0 Nxx = INTEGRABLE_VECTOR("int, Quadrature", 4, qp);
for(int i = 0; i < 4; i++) Nxx[i] = dd(N)(i)[0][0];
stiff &= (Nxx % Nxx) | d_l; mass &= ( ((H0)N)%((H0)N) ) | d_l;
}
Element_Formulation* Element_Formulation::type_list = 0;
static Element_Type_Register element_type_register_instance;
static Hyperbolic_Equation hyperbolic_equation_instance(element_type_register_instance);
static Matrix_Representation mr(gd);
static const double gamma_ = 0.5;
static const double beta_ = 0.25;
static const double dt_ = 0.01;
static double a[8];
C0& Hyperbolic_Equation::__lhs() { the_lhs &= stiff + a[0]*mass; return the_lhs; }
C0& Hyperbolic_Equation::__rhs() { Element_Formulation::__rhs();
the_rhs += mass * (a[0]*ul+a[2]*dul+a[3]*ddul); return the_rhs; }
int main() {
for(int i = 0; i < node_no; i++) {
C1 x( ((double)i)*h_e ), w_0 = sin(PI*x)-PI*x*(1.0-x);
u_old[i][0] = ((C0)w_0); u_old[i][1] = -d(w_0);
for(int j = 0; j < ndf; j++) du_old[i][j] = ddu_old[i][j] = 0.0;
}
gd.u_h() = gd.gh_on_gamma_h();
a[0] = 1.0/(beta_*pow(dt_,2)); a[1] = gamma_/(beta_*dt_); a[2] = 1.0/(beta_*dt_);
a[3] = 1.0/(2.0*beta_)-1.0; a[4] = gamma_/beta_-1.0; a[5] = dt_/2.0*(gamma_/beta_-2.0);
a[6] = dt_*(1.0-gamma_); a[7] = gamma_*dt_;
mr.assembly(); C0 decomposed_LHS = !((C0)(mr.lhs()));
for(int i = 0; i < 28; i++) {
C0 u = decomposed_LHS*((C0)(mr.rhs()));
gd.u_h() = u;
u_new = ( (C0)(gd.u_h()) );
ddu_new = a[0]*(((C0)u_new)-((C0)u_old))-a[2]*((C0)du_old)-a[3]*((C0)ddu_old);
du_new = ((C0)du_old) + a[6]*((C0)ddu_old)+a[7]*((C0)ddu_new);
u_old = ((C0)u_new); du_old = ((C0)du_new); ddu_old = ((C0)ddu_new);
double iptr;
if(modf( ((double)(i+1))/2.0, &iptr)==0) { cout << "time: " << (((double)(i+1))*dt_)
<< ", u: " << u_new[(node_no-1)/2][0] << endl; }
if(i < 27) { (C0)(mr.rhs()) = 0.0; (C0)(mr.lhs()) = 0.0; mr.assembly(); }
}
return 0;
}
Hermite cubics
+

u(x, 0) = sin(πx)-πx(1-x),
and
m
e
φ
e
φ
e
⊗ x d

e

=
k
e

2
φ
e
∂x
2
-----------

2
φ
e
∂x
2
----------- ⊗ x d

e

=
K a
0
M a
1
C + +
f
n 1 +
– a
0
u
n
a
2
u
·
n
a
3
u
··
n
+ + ( )M +
a
1
u
n
a
4
u
·
n
a
5
u
··
n
+ + ( )C
∂u x 0 , ( )
∂t
-------------------- 0 =
a
0
1
β∆t
2
----------- a
1
,
γ
β∆t
--------- a
2
,
1
β∆t
--------- = = =
a
3
1

------ 1 a
4
, –
γ
β
--- 1 a
5
, –
∆t
2
-----
γ
β
--- 2 –
¸ ,
¸ _
= = =
a
6
∆t 1 γ – ( ) a
7
, γ∆t = =
u
··
n 1 +
a
0
u
n 1 +
u
n
– ( ) a
2
u
·
n
– a
3
u
··
n
– =
u
·
n 1 +
u
·
n
a
6
u
··
n
a
7
u
··
n 1 +
+ + =
Listing 4•11 Newmark scheme for hyperbolic equation using finite element method.
Finite Element Method Primer
342 Workbook of Applications in VectorSpace C++ Library
Chapter 4
3 gh_on_Gamma_h& gh = gd.gh_on_gamma_h();
4 Global_Discretization gd_u_old(oh, gh, u_old);
5 ul &= gd_u_old.element_free_variable(en);
6 Global_Discretization gd_du_old(oh, gh, du_old);
7 dul &= gd_du_old.element_free_variable(en);
8 Global_Discretization gd_ddu_old(oh,gh,ddu_old);
9 ddul &=gd_ddu_old.element_free_variable(en);
10 }
Basically, the time integration algorithm is to update variables , , at time t
n
to , , at
time t
n+1
. At the beginning of time t
n+1,
, , are given, and is solved from back-substitution of glo-
bal stiffness matrix and global residual vector. The velocity and acceleration and at time t
n+1
are
computed at the global level in the main() program, when the variable “u_new”, , is available, such as
1 ddu_new = a[0]*(((C0)u_new)-((C0)u_old))-a[2]*((C0)du_old)-a[3]*((C0)ddu_old);
2 du_new = ((C0)du_old) + a[6]*((C0)ddu_old)+a[7]*((C0)ddu_new);
This is implemented according to the formula for acceleration (line 1) and
velocity (line 2), respectively. The results of this computation are shown in Figure
4•29.
u
n
u
·
n
u
··
n
u
n 1 +
u
·
n 1 +
u
··
n 1 +
u
n
u
·
n
u
··
n
u
n 1 +
u
··
n 1 +
u
·
n 1 +
u
n 1 +
u
··
n 1 +
a
0
u
n 1 +
u
n
– ( ) a
2
u
·
n
– a
3
u
··
n
– =
u
·
n 1 +
u
·
n
a
6
u
··
n
a
7
u
··
n 1 +
+ + =
0.2 0.4 0.6 0.8 1
-0.2
-0.1
0.1
0.2
0.2 0.4 0.6 0.8 1
-0.2
-0.1
0.1
0.2
Figure 4•29 Beam vibration using finite element method with Newmark scheme to solve the
hyperbolic equation. The finite element solutions of downward deflection are piece-wise cubic
functions of nodal deflection “ ” and nodal negative slope “ -du/dx” (i.e., u = f( , ) for
two-node Hermite cubic element). Solutions of every four time steps are shown.
u
ˆ
ψ
ˆ
≡ u
ˆ
ψ
ˆ
t = 0.02
t = 0.16
t = 0.04
t = 0.06
t = 0.08
t = 0.10
t = 0.12
t = 0.14
t = 0.18
t = 0.20
t = 0.22
t = 0.24
t = 0.26
t = 0.28
u
u
x
x
initial condition t = 0
t = 0.0 to 0.14 t = 0.16 to 0.28
Workbook of Applications in VectorSpace C++ Library 343
One Dimensional Problems
4.2.5 The Mixed Formulation Revisited—Matrix Substructure Method
In the irreducible formulation for the beam bending problem (see page 306) the unwieldy piece-wise Hermit
cubic functions are used for the C
1
-continuity on the nodes since the second-order derivatives appeared in the
element stiffness matrix. The mixed formulation for the same problem (see page 312) reduces the element stiff-
ness matrix to have the first-order derivatives and only C
0
-continuity is required. However, this is achieved with
the expense of add nodal variables in addition to . Recall Eq. 4•44 for the element formulation
Eq. 4•87
Assign symbols for submatrices and subvectors in Eq. 4•87, we have
Eq. 4•88
where the stiffness matrix has the size of 4x4 and the solution and force vectors have the sizes of 4. Following the
finite element convention, we collect degree of freedoms w and M together for each node. At the global level, the
matrix form is
Eq. 4•89
For large-size problem, the stiffness matrix size could be critical for limited computer memory space , and even
more seriously for the computation time. One approach to reduce the number of degree of freedom in the global
matrix solution process is to separate the variables and in Eq. 4•89 at the global level. Rewriting the ele-
ment formulation of Eq. 4•89 in global submatrix form as
Eq. 4•90
where B is symmetric negative definite.
1

M
ˆ
w
ˆ
0

e
w
dx
----------

e
M
dx
---------- ⊗
¸ ,
¸ _
dx

e



e
M
dx
----------

e
w
dx
---------- ⊗
¸ ,
¸ _
dx

e


φ
e
M
φ
e
M

EI
----------------------
¸ ,
¸ _
dx

e


w
ˆ
e
M
ˆ
e
φ
e
w
fdx φ
e
w

h
+

e

φ
e
M
ψ
Γ
h
=
0 a
e
a
e
T
b
e
w
ˆ
e
M
ˆ
e
f
e
r
e
=
0 a
00
0 a
01
… … 0 a
0 n 1 – ( )
a
00
T
b
00
a
01
T
b
01
… … a
0 n 2 – ( )
T
b
0 n 1 – ( )
0 a
10
0 a
11
… … 0 a
1 n 1 – ( )
a
10
T
b
10
a
11
T
b
11
… … a
1 n 2 – ( )
T
b
1 n 1 – ( )
… … … … … … … …
… … … … … … … …
0 a
n 1 – ( )0
0 a
n 1 – ( )1
… … 0 a
n 1 – ( ) n 1 – ( )
a
n 1 – ( )0
T
b
n 1 – ( )0
a
n 1 – ( )1
T
b
n 1 – ( )1
… … a
n 1 – ( ) n 1 – ( )
T
b
n 1 – ( ) n 1 – ( )
w
ˆ
0
M
ˆ
0
w
ˆ
1
M
ˆ
1


w
ˆ
n 1 – ( )
M
ˆ
n 1 – ( )
f
0
r
0
f
1
r
1


f
n 1 – ( )
r
n 1 – ( )
=
w
ˆ
M
ˆ
0 A
A
T
B
w
ˆ
M
ˆ
f
r
=
Finite Element Method Primer
344 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Matrix Substructuring
We can solve the Eq. 4•90 with “matrix substructuring” (or static condensation). This is only possible
because of the special properties of the submatrices in Eq. 4•90 that (1) the diagonal submatrix B is symmetric
positive definite (or negative definite) which is invertible, and (2) the off-diagonal matrices are transpose of each
other. Therefore, pre-and post- multiplication of the symmetric positive definitive matrix B
-1
such as “AB
-1
A
T

is a similarity transformation which preserves the symmetric positive definitive property. Therefore, it can also
be inverted. Now, let’s proceed with the substructuring. From second equation of Eq. 4•90, we have
Eq. 4•91
Therefore,
Eq. 4•92
Note we have use the property that B is invertible. Observing that the first equation of Eq. 4•90 is , we
pre-multiply Eq. 4•92 with A, such that
Eq. 4•93
Now we can solve for as
Eq. 4•94
We have relied on the property that AB
-1
A
T
is invertible. With solved, can be recovered according to Eq.
4•92, if necessary. The solution using the substructuring technique has two major advantages. Firstly, only A and
B need to be stored in memory space that is only half of the memory space comparing to the entire left-hand-side
matrix in Eq. 4•90. Secondly, the matrix solver in substructuring deals with B
-1
and AB
-1
A
T
which are smaller
matrices than the left-hand-side matrix in Eq. 4•90. The cost for a matrix solver can be a function of cubic power
of size. For the present case, each of the inverse of B and the inverse of AB
-1
A
T
requires about one-eighth of
computation time comparing to that of the solution of the left-hand-side matrix in Eq. 4•90. That is only a quar-
ter of computation time is needed for the matrix solver using substructuring.
1
1. Note that the term f includes (1) the distributed load term, the term contains “f” in Eq. 4•87, (2) shear force
(V), the “nodal loading boundary condition” V
Γ
(treated as natural boundary condition specified corresponding to
“w”-dof), and (3) essential boundary condition of M
Γ
by subtracting “AM
Γ
” out of f. The term r includes (1)
negative slope (ψ), the “nodal loading boundary condition” ψ
Γ
(treated as natural boundary condition specified
corresponding to “M”-dof), and (2) the essential boundary conditions of {w
Γ
, M
Γ
} by subtracting “A
T
w
Γ
+BM
Γ

out of r .
1. Moreover, Eq. 4•89 has a lot of zero diagonals, which is not without trouble for the matrix solver. We either need to use
modified Cholesky decomposition with the diagonal pivoting or we need to ignore the symmetry and use LU decomposition
with complete pivoting.
A
T
w
ˆ
BM
ˆ
+ r =
M
ˆ
B
1 –
r A
T
w
ˆ
– ( ) =
AM
ˆ
f =
f AM
ˆ
AB
1 –
r A
T
w
ˆ
– ( ) AB
1 –
r AB
1 –
A
T
w
ˆ
– = = =
w
ˆ
w
ˆ
AB
1 –
A
T
( )
1 –
AB
1 –
r f – ( ) =
w
ˆ
M
ˆ
Workbook of Applications in VectorSpace C++ Library 345
One Dimensional Problems
Object-Oriented Modeling for Matrix Substructuring
With the understanding that we want to implement finite element in terms of Eq. 4•90, we may immediately
recognize that the “fe.lib” has no provision to define and to solve a problem described by Eq. 4•90. The object-
oriented modeling of the finite element method in fe.lib has four strong components, (1) discretized global
domain Ω
h
, (2) discretized variables u
h
, (3) element formulation “EF”, and (4) matrix representation “MR”.
Firstly, instead of one set of nodal variables combined together in the irreducible formulation as {
i
,
i
}
now we have two (separate) sets of variables {
i
} and {
i
}. The combination of Ω
h
and u
h
now yield two dif-
ferent Global_Discretizations with {Ω
h
,
h
} and {Ω
h
,
h
} as their constituents.
1 const int ndf = 1;
2 Omega_h oh; // Ω
h
3 gh_on_Gamma_h_i wgh(0, ndf, oh); //
4 U_h wh(ndf, oh); //
h
5 Global_Discretization wgd(oh, wgh, wh);
6 gh_on_Gamma_h_i mgh(1, ndf, oh); //
7 U_h mh(ndf, oh); //
h
8 Global_Discretization mgd(oh, mgh, mh);
The two sets of boundary conditions “gh_on_Gamm_h_i” corresponding to two set of variables {
i
} and {
i
}.
The new class “gh_on_Gamma_h_i” is derived from the”gh_on_Gamma_h” with a subscript index included.
The constructor of this class is defined as
1 gh_on_Gamma_h_i::gh_on_Gamma_h_i(int i, int df, Omega_h& omega_h) :
2 gh_on_Gamma_h() {
3 gh_on_Gamma_h::__initialization(df, omega_h);
4 if(i == 0) {
// w
Γ
(0) = 0; deflection essential boundary condition
5 the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet;
6 the_gh_array[node_order(0)][0] = 0.0;
// V
Γ
(L) = d/dx M(L) = d/dx (EI d
2
w/dx
2
) = 0; shear force natural boundary condition
7 the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Neumann;
8 the_gh_array[node_order(node_no-1)][0] = 0.0;
9 } else if(i == 1) {
// M
Γ
(L) = M_; bending moment essential boundary condition
10 the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Dirichlet;
11 the_gh_array[node_order(node_no-1)][0] = M_;
// -dw/dx(0) = ψ
Γ
(0) = 0; rotation natural boundary condition
12 the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Neumann;
13 the_gh_array[node_order(0)][0] = 0.0;
14 }
15 }
Secondly, for the definition of submatrix B we need the “Global_Discretization” with {Ω
h
,
h
}. The rows
and columns of submatrix B corresponding both to the -dof. However, for the definition of submatrix A, we
w
ˆ
M
ˆ
w
ˆ
M
ˆ
w
ˆ
M
ˆ
w Γ
g
and V Γ
h
∈ , ∈
w
ˆ
M Γ
g
and ψ Γ
h
∈ , ∈
M
ˆ
w
ˆ
M
ˆ
M
ˆ
M
ˆ
Finite Element Method Primer
346 Workbook of Applications in VectorSpace C++ Library
Chapter 4
need both “Global_Discretization” with {Ω
h
,
h
} and {Ω
h
,
h
}. Since the row of submatrix A corresponding
to the -dof, and columns of submatrix A corresponding to the -dof. Therefore, the definition of submatrix B
reference only to the single Global_Discretization of {Ω
h
,
h
}, which is the same as what is already available in
fe.lib. For the definition of submatrix A it needs to refer to a newly defined class of
“Global_Dsicretization_Couple” which is consists of “dual” Global_Discritization with both {Ω
h
,
h
} and
{Ω
h
,
h
}. We have the declaration of the “deflection-and-bending moment” coupled global discretization as
static Global_Discretization_Couple gdc(wgd, mgd);
Thirdly, in the element formulation “EF”, we not only need to define the diagonal submatrix B, but also need
to define the off-diagonal submatrix A. The newly defined class is the “Element_Formulation_Couple” to han-
dle this additional complexity. The user defined element formulation is derived from this
“Element_Formulation_Couple” instead of the “Element_Formulation” such as
1 class Beam_Mixed_Formulation : public Element_Formulation_Couple {
2 public:
3 Beam_Mixed_Formulation(Element_Type_Register a) : Element_Formulation_Couple(a) {}
// diagonal block formulation; submatrix B
4 Element_Formulation *make(int, Global_Discretization&);
5 Beam_Mixed_Formulation(int, Global_Discretization&);
// off-diagonal block formulation; submatrix A
6 Element_Formulation_Couple *make(int, Global_Discretization_Couple&);
7 Beam_Mixed_Formulation(int, Global_Discretization_Couple&);
8 };
9 Element_Formulation* Beam_Mixed_Formulation::make(int en, Global_Discretization& gd) {
10 return new Beam_Mixed_Formulation(en,gd); }
11 Element_Formulation_Couple* Beam_Mixed_Formulation::make(int en,
12 Global_Discretization_Couple& gdc) { return new Beam_Mixed_Formulation(en,gdc); }
For the diagonal submatrix B, the constructor of element formulation is
1 Beam_Mixed_Formulation::Beam_Mixed_Formulation(int en, Global_Discretization& gd) :
2 Element_Formulation_Couple(en, gd) {
3 Quadrature qp(spatial_dim_no, 2); // 1-dimension, 2 Gaussian integration points
4 H1 Z(qp),
5 N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE(
6 "int, int, Quadrature", 2/*nen*/, 1/*nsd*/, qp);
7 N[0] = (1-Z)/2; N[1] = (1+Z)/2;
8 H1 X = N*xl;
9 H0 Nx = d(N)(0)/d(X);
10 J d_l(d(X));
11 stiff &= -(1.0/E_/I_)* ( (((H0)N)*(~(H0)N)) | d_l ); // B =
12 }
For the off-diagonal submatrix A, the constructor of element formulation is
w
ˆ
M
M
ˆ
w
ˆ
M
ˆ
w
ˆ
M
ˆ
φ
e
M
φ
e
M

EI
----------------------
¸ ,
¸ _
dx

e


Workbook of Applications in VectorSpace C++ Library 347
One Dimensional Problems
1 Beam_Mixed_Formulation::Beam_Mixed_Formulation(int en, Global_Discretization_Couple& gdc)
2 : Element_Formulation_Couple(en, gdc) {
3 Quadrature qp(spatial_dim_no, 2); // 1-dimension, 2 integration points
4 H1 Z(qp),
5 N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE(
6 "int, int, Quadrature", 2/*nen*/, 1/*nsd*/, qp);
7 N[0] = (1-Z)/2; N[1] = (1+Z)/2;
8 H1 X = N*xl;
9 H0 Nx = d(N)(0)/d(X);
10 J d_l(d(X));
11 stiff &= -(Nx * (~Nx)) | d_l; // A =
12 force &= ( (((H0)N)*f_0) | d_l ); // f =
13 }
Finally, we recall the global submatrix form in Eq. 4•90
Eq. 4•95
The matrix representation, “MR”, for the diagonal submatrix B and its corresponding right-hand-side r is
declared as standard class of “Matrix_Representation”
Matrix_Representation mr(mgd);
This matrix representation instance “mr” can be called to assemble and instantiate the submatrix B and the sub-
vector r. They can be retrieved by
1 mr.assembly();
2 C0 B = ((C0)(mr.lhs())), // diagonal submatrix B
3 r = ((C0)(mr.rhs())); // r
The rows of submatrix A corresponding to “w”-dof, the principal discretization, and the columns of submatrix A
corresponding to “M”-dof, the subordinate discretization. The class “Matrix_Representation_Couple” is
declared instead as
Matrix_Representation_Couple mrc(gdc, 0, 0, &(mr.rhs()) );
The second argument of this constructor is reserved for instantiation sparse matrix, the third and the fourth argu-
ments of this constructor referencing to right-hand-side vectors corresponding to the principal and the subordi-
nate discretization of submatrix A. In the above example, the principal right-hand-side is supplied with a “0”, the
null pointer. In this case, the principal right-hand-side vector f will be instantiated. When the argument is not
null, such as the subordinate right-hand-side is reference to “mr.rhs()” in this case. The subordinate right-hand-

e
w
dx
----------

e
M
dx
---------- ⊗
¸ ,
¸ _
dx

e


φ
e
w
fdx

e

0 A
A
T
B
w
ˆ
M
ˆ
f
r
=
Finite Element Method Primer
348 Workbook of Applications in VectorSpace C++ Library
Chapter 4
side will not be instantiated but will be referring to “mr.rhs()”, which has already been instantiated. Now we can
solve Eq. 4•90 with the matrix substructuring such as
1 int main() {
2 mrc.assembly();
3 C0 f = ((C0)(mrc.rhs())),
4 A = ((C0)(mrc.lhs()));
5 mr.assembly();
6 C0 B = ((C0)(mr.lhs())),
7 r = ((C0)(mr.rhs()));
8 C0 B_inv = B.inverse(),
9 w = (A*B_inv*r - f)/(A*B_inv*(~A)), //
10 M = B_inv*(r-(~A)*w); //
11 wh = w; wh = wgd.gh_on_gamma_h();
13 mh = M; mh = mgd.gh_on_gamma_h();
14 cout << "deflection:" << endl << wh << endl << "bending moment:" << endl << mh;
15 return 0;
16 }
The complete listing of the substructure mixed formulation is in Program Listing 4•12. The cases for nodal
loading and distributed loading, discussed in the mixed formulation of Section 4.2.2, can be turn on by setting
macro definitions “__TEST_NODAL_LOAD” and “__TEST_DISTRIBUTED_LOAD” . The results of the
present computation are completely identical to those of the previous section on mixed formulation..
The nonlinear and transient problems bring only marginal changes to the “fe.lib”. We certainly can create
new classes of “Nonlinear_Element_Formulation” and “Transient_Element_Formulation” for a user defined ele-
ment to derived from. This is similar to the class “Element_Formulation_Couple” in the present example is cre-
ated for user to derived a user defined element formulation from it. We can even create a multiple inheritance (an
advanced but controversial C++ feature) of class Nonlinear_Element_Formulation and class
Transient_Element_Formulation to capture both the nonlinear and the transient capabilities. The object-oriented
programming provides the basic mechanisms for a smooth code evolution of “fe.lib” to be extended to vastly
different area of problems. However, the problem of “mixed formulation with separate variables” brings the
greatest impact of change to fe.lib. We need to change all four strong components of the “fe.lib” to implement
this problem. With mechanisms of the object-oriented programming, we are not only able to reuse the code in
“fe.lib” by deriving from it, but also are able to keep the simplicity of the “fe.lib” intact. After the “fe.lib” has
been modified to deal with the new problem, the beginner of the fe.lib still only need to learn the unscrambled
basic set of “fe.lib” without to confront all kinds of more advanced problems in finite element at once. For For-
tran/C programmers who are already familiar with a couple of existing full-fledged finite element programs, this
advantage of using object-oriented programming to accommodate vastly different problems would be most
immediately apparent.
w
ˆ
AB
1 –
A
T
( )
1 –
AB
1 –
r f – ( ) =
M
ˆ
B
1 –
r A
T
w
ˆ
– ( ) =
Workbook of Applications in VectorSpace C++ Library 349
One Dimensional Problems
#include "include\fe.h"
#include "include\omega_h_n.h"
Matrix_Representation_Couple::assembly_switch
Matrix_Representation_Couple::Assembly_Switch = Matrix_Representation_Couple::ALL;
static const int node_no = 5;
static const int element_no = 4;
static const int spatial_dim_no = 1;
static const double L_ = 1.0;
static const double h_e = L_/((double)(element_no));
static const double E_ = 1.0;
static const double I_ = 1.0;
static const double f_0 = 1.0;
static const double M_ = 1.0;
Omega_h::Omega_h() {
for(int i = 0; i < node_no; i++) {
double v = ((double)i)*h_e;
Node* node = new Node(i, spatial_dim_no, &v);
the_node_array.add(node);
}
for(int i = 0; i < element_no; i++) {
int ena[2];
ena[0] = i;
ena[1] = ena[0]+1;
Omega_eh* elem = new Omega_eh(i, 0, 0, 2, ena);
the_omega_eh_array.add(elem);
}
}
gh_on_Gamma_h_i::gh_on_Gamma_h_i(int i, int df, Omega_h& omega_h) : gh_on_Gamma_h() {
gh_on_Gamma_h::__initialization(df, omega_h);
if(i == 0) {
the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet;
} else if(i == 1) {
the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(node_no-1)][0] = M_;
}
}
static const int ndf = 1;
static Omega_h oh;
static gh_on_Gamma_h_i wgh(0, ndf, oh);
static U_h wh(ndf, oh);
static Global_Discretization wgd(oh, wgh, wh);
static gh_on_Gamma_h_i mgh(1, ndf, oh);
static U_h mh(ndf, oh);
static Global_Discretization mgd(oh, mgh, mh);
static Global_Discretization_Couple gdc(wgd, mgd);
class Beam_Mixed_Formulation : public Element_Formulation_Couple {
public:
Beam_Mixed_Formulation(Element_Type_Register a) : Element_Formulation_Couple(a) {}
Element_Formulation *make(int, Global_Discretization&);
Beam_Mixed_Formulation(int, Global_Discretization&);
Element_Formulation_Couple *make(int, Global_Discretization_Couple&);
Beam_Mixed_Formulation(int, Global_Discretization_Couple&);
};
Element_Formulation* Beam_Mixed_Formulation::make(int en, Global_Discretization& gd) {
return new Beam_Mixed_Formulation(en,gd);
}
initialize static member of class
“Matrix_Representation_Couple”
Definte discretizaed global domain
define nodes
define elements
define boundary conditions
instantiate fixed and free variables and
Global_Discretization
{Ω
h
,
h
}
{Ω
h
,
h
}
Global_Discretization_Couple
w
ˆ
M
ˆ
Finite Element Method Primer
350 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Beam_Mixed_Formulation::Beam_Mixed_Formulation(int en, Global_Discretization& gd) :
Element_Formulation_Couple(en, gd) {
Quadrature qp(spatial_dim_no, 2);
H1 Z(qp),
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE(
"int, int, Quadrature", 2/*nen*/, 1/*nsd*/, qp);
N[0] = (1-Z)/2;
N[1] = (1+Z)/2;
H1 X = N*xl;
H0 Nx = d(N)(0)/d(X);
J d_l(d(X));
stiff &= -(1.0/E_/I_)* ( (((H0)N)*(~(H0)N)) | d_l );
}
Element_Formulation_Couple* Beam_Mixed_Formulation::make(int en,
Global_Discretization_Couple& gdc) {
return new Beam_Mixed_Formulation(en,gdc);
}
Beam_Mixed_Formulation::Beam_Mixed_Formulation(int en, Global_Discretization_Couple&
gdc) : Element_Formulation_Couple(en, gdc) {
Quadrature qp(spatial_dim_no, 2);
H1 Z(qp),
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE(
"int, int, Quadrature",2,1,qp);
N[0] = (1-Z)/2;
N[1] = (1+Z)/2;
H1 X = N*xl;
H0 Nx = d(N)(0)/d(X);
J d_l(d(X));
stiff &= -(Nx * (~Nx)) | d_l;
force &= ( (((H0)N)*f_0) | d_l );
}
Element_Formulation* Element_Formulation::type_list = 0;
static Element_Type_Register element_type_register_instance;
static Beam_Mixed_Formulation
beam_mixed_formulation_instance(element_type_register_instance);
static Matrix_Representation mr(mgd);
static Matrix_Representation_Couple mrc(gdc, 0, 0, &(mr.rhs()), &mr);
int main() {
mrc.assembly();
C0 f = ((C0)(mrc.rhs())),
A = ((C0)(mrc.lhs()));
mr.assembly();
C0 B = ((C0)(mr.lhs())),
r = ((C0)(mr.rhs()));
C0 B_inv = B.inverse(),
w = (A*B_inv*r - f)/(A*B_inv*(~A)),
M = B_inv*(r-(~A)*w);
wh = w;
wh = wgd.gh_on_gamma_h();
mh = M;
mh= mgd.gh_on_gamma_h();
cout << "deflection:" << endl << wh<< endl << "bending moment:" << endl << mh;
return 0;
}
B =
A =
f =
φ
e
M
φ
e
M

EI
----------------------
¸ ,
¸ _
dx

e



e
w
dx
----------

e
M
dx
---------- ⊗
¸ ,
¸ _
dx

e


φ
e
w
fdx

e

w
ˆ
AB
1 –
A
T
( )
1 –
AB
1 –
r f – ( ) =
M
ˆ
B
1 –
r A
T
w
ˆ
– ( ) =
Listing 4•12 Substructure solution for the mixed formulation of the beam bending problem.
Workbook of Applications in VectorSpace C++ Library 351
Two Dimensional Problems
4.3 Two Dimensional Problems
We went through various 1-D proto-type problems in finite element method. However, we may argue that it is
somewhat un-necessary to use finite element method for the 1-D problems. We can solve these problems with the
classical variational method as in Chapter 3. However, for more complicated geometry with the dimension
greater than or equal 2-D, the finite element method offers a systematic treatment of the complicated geometry
where the use of the finite element method becomes essential.
4.3.1 Heat Conduction
Basic Physics and Element Formulation
For heat conduction problem the divergence of heat flux of a body is equal to the heat generated from the
source contained within the body as
Eq. 4•96
where q is the heat flux and f is the heat source. This is subject to Dirichlet and Neumann boundary conditions
, and , Eq. 4•97
respectively. We use “u” for temperature and n as the outward unit surface normal. The Fourier law assumes
that the heat flux can be related to temperature gradient as
Eq. 4•98
where κ is the thermal diffusivity. The weighted residual statement of Eq. 4•96 with the Fourier law gives
Eq. 4•99
Integration by parts and applying divergence theorem of Gauss to transform the volume integral into a boundary
integral gives
Eq. 4•100
Since the “w” is homogeneous at Γ
g
, the boundaries with Dirchlet boundary conditions, the second term of the
boundary integral becomes
Eq. 4•101
q ∇• f =
u g on Γ
g
= q n • – h on Γ
h
=
q κ∇u – =
w q f – ∇• ( ) Ω d


w κ u ∇
2
– f – ( ) Ω d


0 = =
∇w κ∇u ( ) Ω w κ – ∇u ( ) n • Γ wf Ω d


– d
Γ

+ d


0 =
wq n Γ d •
Γ

wh Γ d
Γ
h

– =
Finite Element Method Primer
352 Workbook of Applications in VectorSpace C++ Library
Chapter 4
the element stiffness matrix, under finite element approximation, is
Eq. 4•102
and, the element force vector is
Eq. 4•103
The second term is the Neumann boundary conditions, which is most easily specified in the problem
definition as equivalent nodal load, and the third term accounts for the Dirichlet boundary condi-
tions. Again, the default behaviors of “fe.lib” will deal with these two terms automatically.
For an isoparametric bilinear 4-nodes element, the bilinear shape functions are taken for both the variable
interpolation, , and the coordinate transformation rule, , that is
Eq. 4•104
index “a” indicates element node number, and (ξ
a
, η
a
) , for a = 0, 1, 2, 3 are four nodal coordinates {(-1, -1), (1,
-1), (1, 1), (-1, 1)} of the referential element. The variable interpolation becomes
Eq. 4•105
where is the nodal variables, and the coordinate transformation rule becomes
Eq. 4•106
where is the element nodal coordinates. The integration in Eq. 4•102 and first term of Eq. 4•103 gives
, and Eq. 4•107
The Gaussian quadrature requires the integration domain to be transformed from the physical element domain
“Ω
e
” to the referential element domain “Ω
e
” with the Jacobian of the coordinate transformation as “J
” (i.e., the determinant of the Jacobian matrix), where the Jacobian matrix of the coordinate trans-
formation rule, “ ”, is computed from the definition of the coordinate transformation rule in Eq. 4•106.
The derivatives of the variables are taken from Eq. 4•105 as
k
e
ab
a φ
e
a
φ
e
b
, ( ) ∇φ
e
a
κ∇φ
e
b
( )dx

e

= =
f
e
a
φ
e
a
f , ( ) φ
e
a
h , ( )
Γ
h
a φ
e
a
φ
e
b
, ( )u
e
b
– + =
φ
e
a
h , ( )
Γ
h
a φ
e
a
φ
e
b
, ( )u
e
b

u
e
h
φ
e
a
u
ˆ
e
a
≡ x φ
e
a
x
e
a

φ
e
a
N
a
ξ η , ( ) ≡
1
4
--- 1 ξ
a
ξ + ( ) 1 η
a
η + ( ) =
u
e
h
ξ η , ( )
N
a
ξ η , ( )u
ˆ
e
a

u
ˆ
e
a
x
e
h
N
a
ξ η , ( )x
e
a

x
e
a
k
e
∇N κ∇N ⊗ ( )dx

e

∇N κ∇N ⊗ ( )det
∂x
∂ξ
------
¸ ,
¸ _

Ωe

= = f
e
Nf ( )dx

e

Nf ( )det
∂x
∂ξ
------
¸ ,
¸ _

Ωe

= =
d ≡ et ∂x ∂ξ ⁄ ( )
∂x ∂ξ ⁄
Workbook of Applications in VectorSpace C++ Library 353
Two Dimensional Problems
Eq. 4•108
The derivative of shape functions with respect to natural coordinates , is computed from the definition of
the shape functions in Eq. 4•104. The term is computed from the inverse of the derivative of the coordi-
nate transformation rule from Eq. 4•106 as
Eq. 4•109
That is, Eq. 4•108 gives the formula to compute the derivatives of shape functions matrix (nen dof = 4 2) for
the element stiffness matrix in Eq. 4•107
Eq. 4•110
An Example with Bilinear 4-Node Element
We now consider an example of a 3 3 unit square insulated from the two sides with the top boundary and the
bottom boundary set at 30
o
C and 0
o
C, respectively. The thermal diffusivity is assumed to be isotropic with κ = 1
(see Figure 4•30). Combinding Eq. 4•96 and Eq. 4•98, we have
Eq. 4•111
Since there is no heat source in the square area “f = 0”, and due to symmetry of the boundary conditions no tem-
perature gradient can be generated in x-direction, Eq. 4•111 reduces to
∇u
e
h
ξ η , ( ) ∇N
a
ξ η , ( ) ( )
T
u
ˆ
e
a
∇N
0
∇N
1
∇N
2
∇N
3
u
ˆ
e
0
u
ˆ
e
1
u
ˆ
e
2
u
ˆ
e
3
∂N
0
∂x
---------
∂N
1
∂x
---------
∂N
2
∂x
---------
∂N
3
∂x
---------
∂N
0
∂y
---------
∂N
1
∂y
---------
∂N
2
∂y
---------
∂N
3
∂y
---------
u
ˆ
e
0
u
ˆ
e
1
u
ˆ
e
2
u
ˆ
e
3
= = =
∂ξ
∂x
------
∂η
∂x
------
∂ξ
∂y
------
∂η
∂y
------
∂N
0
∂ξ
---------
∂N
1
∂ξ
---------
∂N
2
∂ξ
---------
∂N
3
∂ξ
---------
∂N
0
∂η
---------
∂N
1
∂η
---------
∂N
2
∂η
---------
∂N
3
∂η
---------
u
ˆ
e
0
u
ˆ
e
1
u
ˆ
e
2
u
ˆ
e
3
∂N
a
∂ξ
---------
¸ ,
¸ _
∂ξ
∂x
------
¸ ,
¸ _
T
u
ˆ
e
a
= =
∂N ∂ξ ⁄
∂ξ ∂x ⁄
∂ξ ∂x ⁄ ∂x ∂ξ ⁄ ( )
1 –
=
× ×
∇N
∂N
∂ξ
-------
¸ ,
¸ _
∂x
∂ξ
------
¸ ,
¸ _
1 –
=
×
κ u ∇ – ( ) ∇• κ u ∇
2
– f = =
Finite Element Method Primer
354 Workbook of Applications in VectorSpace C++ Library
Chapter 4
constant = = 10 Eq. 4•112
That is the temperature gradient in y-direction is 10 (
o
C per unit length). In other words, the nodal solutions at
the row next to the bottom is u = 10
o
C, and the row next to the top is u = 20
o
C. The Program Listing 4•13
implements element formulation for the stiffness matrix and force vector in Eq. 4•107 for this simple problem.
The nodes and elements can be generated as
1 int row_node_no = 4,
2 row_element_no = row_node_no - 1;
3 double v[2];
4 for(int i = 0; i < row_node_no; i++)
5 for(int j = 0; j < row_node_no; j++) {
6 int nn = i*row_node_no+j;
7 v[0] = (double)j; v[1] = (double)i;
8 Node* node = new Node(nn, 2, v);
9 the_node_array.add(node);
10 }
11 int ena[4];
12 for(int i = 0; i < row_element_no; i++)
13 for(int j = 0; j < row_element_no; j++) {
14 int nn = i*row_node_no+j;
15 ena[0] = nn; ena[1] = ena[0]+1; ena[3] = nn + row_node_no; ena[2] = ena[3]+1;
16 int en = i*row_element_no+j;
17 Omega_eh* elem = new Omega_eh(en, 0, 0, 4, ena);
18 the_omega_eh_array.add(elem);
19 }
Figure 4•30 Conduction in a square insulated from two sides.
u
bottom
= 0
o
C
u
top
= 30
o
C
q • n = 0 q • n = 0
d
2
u
y
2
d
-------- 0
du
y d
------ ⇒ = =
u
t op
u
bot tom
– ( )
3
---------------------------------------
Workbook of Applications in VectorSpace C++ Library 355
Two Dimensional Problems
#include "include\fe.h"
Omega_h::Omega_h() {
int row_node_no = 4, row_element_no = row_node_no - 1;
for(int i = 0; i < row_node_no; i++)
for(int j = 0; j < row_node_no; j++) {
int nn = i*row_node_no+j; double v[2]; v[0] = (double)j; v[1] = (double)i;
Node* node = new Node(nn, 2, v); the_node_array.add(node);
}
for(int i = 0; i < row_element_no; i++)
for(int j = 0; j < row_element_no; j++) {
int nn = i*row_node_no+j, en = i*row_element_no+j;
int ena[4]; ena[0] = nn; ena[1] = ena[0]+1; ena[3] = nn + row_node_no; ena[2] = ena[3]+1;
Omega_eh* elem = new Omega_eh(en, 0, 0, 4, ena); the_omega_eh_array.add(elem);
}
}
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
__initialization(df, omega_h);
int row_node_no = 4, first_top_node_no = row_node_no*(row_node_no-1);
for(int i = 0; i < row_node_no; i++) {
the_gh_array[node_order(i)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(first_top_node_no+i)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(first_top_node_no+i)][0] = 30.0;
}
}
class HeatQ4 : public Element_Formulation { public:
HeatQ4(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
HeatQ4(int, Global_Discretization&);
};
Element_Formulation* HeatQ4::make(int en, Global_Discretization& gd) {
return new HeatQ4(en,gd);
}
HeatQ4::HeatQ4(int en, Global_Discretization& gd) : Element_Formulation(en, gd) {
Quadrature qp(2, 4);
H1 Z(2, (double*)0, qp), Zai, Eta,
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int, int, Quadrature", 4, 2, qp);
Zai &= Z[0]; Eta &= Z[1];
N[0] = (1-Zai)*(1-Eta)/4; N[1] = (1+Zai)*(1-Eta)/4;
N[2] = (1+Zai)*(1+Eta)/4; N[3] = (1-Zai)*(1+Eta)/4;
H1 X = N*xl; H0 Nx = d(N) * d(X).inverse(); J dv(d(X).det()); double k = 1.0;
stiff &= (Nx * k * (~Nx)) | dv;
}
Element_Formulation* Element_Formulation::type_list = 0;
Element_Type_Register element_type_register_instance;
static HeatQ4 heatq4_instance(element_type_register_instance);
void output(Global_Discretization&);
int main() {
int ndf = 1; Omega_h oh; gh_on_Gamma_h gh(ndf, oh); U_h uh(ndf, oh);
Global_Discretization gd(oh, gh, uh); Matrix_Representation mr(gd);
mr.assembly();
C0 u = ((C0)(mr.rhs())) / ((C0)(mr.lhs()));
gd.u_h() = u; gd.u_h() = gd.gh_on_gamma_h();
cout << gd.u_h();
return 0;
}
define nodes
define elements
define B.C.
top boundary u = 0
o
C
bottom boundary u = 30
o
C
define element
assembly
matrix solver
update free and fixed dof
output
N
a
ξ η , ( )
1
4
--- 1 ξ
a
ξ + ( ) 1 η
a
η + ( ) =
∇N
∂N
∂ξ
-------
¸ ,
¸ _
∂x
∂ξ
------
¸ ,
¸ _
1 –
=
k
e
∇N κ∇N ⊗ ( )det
∂x
∂ξ
------
¸ ,
¸ _

Ωe

=
Listing 4•13 Two-dimensional heat conduction problem (project workspace file “fe.dsw”, project
“2d_heat_conduction”.
Finite Element Method Primer
356 Workbook of Applications in VectorSpace C++ Library
Chapter 4
This code generates 16 nodes and 9 bilinear 4-node elements in the constructor “Omega_h::Omega_h()”. After
node and element are created by their own constructors (i.e., “Node:Node(int, int, double*)” and
“Omega_eh::Omega_eh(int, int, int, int, int*)”), we use the member functions “Node::add(Node*)” and
“Omega_eh::add(Omega_eh*)” to add to the “database” the information on nodes and elements, respectively.
We observe that defining the nodes and the elements for a two dimensional problem may become a very compli-
cated task. We will discussed this issue later with a simple 2-D tool—“block()” function that has already been
introduced in Chapter 3 (see page 192) to enhance the capability to handle increasingly complicated geometry.
At the heart of the code is the element constructor “HeatQ4::HeatQ4()” which implements a 4-nodes bilinear
quadrilateral element
1 HeatQ4::HeatQ4(int en, Global_Discretization& gd) : Element_Formulation(en, gd) {
2 Quadrature qp(2, 4);
3 H1 Z(2, (double*)0, qp), Zai, Eta,
4 N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int, int, Quadrature", 4, 2, qp);
5 Zai &= Z[0]; Eta &= Z[1];
6 N[0] = (1-Zai)*(1-Eta)/4; N[1] = (1+Zai)*(1-Eta)/4;
7 N[2] = (1+Zai)*(1+Eta)/4; N[3] = (1-Zai)*(1+Eta)/4;
8 H1 X = N*xl;
9 H0 Nx = d(N) * d(X).inverse();
10 J dv(d(X).det());
11 double k = 1.0;
12 stiff &= (Nx * k * (~Nx)) | dv;
13 }
We use a 2-D 2 2 Gaussian quadrature for all integrable objects (line 2). In line 6 and 7, the shape functions
“N” is defined according to Eq. 4•104. The coordinate transformation rule in line 8 is from Eq. 4•106. The deriv-
ative of shape function are calculated according to Eq. 4•109 and Eq. 4•110. Line 10 on “the Jacobian” and line
12 on stiffness matrix is the first part of the Eq. 4•107. The rest of the code is not very different from that of a 1-
D problem.
A 2-D Geometrical Tool — “block()”
Even with the above extremely simple problem, the increasing difficulty in specifying geometry is exposed.
We use a few examples to demonstrate the tool “block()” function that facilitates the definition of 2-D geometry.
The first example constructs a set of nodes and elements with a single “block()” function call as (see Figure
4•31)
1 double coord[4][2] = {{0.0, 0.0}, {3.0, 0.0}, {3.0, 3.0}, {0.0, 3.0}};
2 int control_node_flag[4] = {TRUE, TRUE, TRUE, TRUE};
3 block(this, 4, 4, 4, control_node_flag, coord[0]);
The first integer argument specifies in “block()” the number of nodes generated row-wise, which is “4”. The sec-
ond integer argument specifies the number of nodes generated column-wise. The following integer is the number
of control nodes. In this example, the four control nodes are located at node numbers “0”, “3”, “15”, and “12”
×
Workbook of Applications in VectorSpace C++ Library 357
Two Dimensional Problems
ordered counter-clockwise starting from the lower-left corner. The components in the int array of the
“control_node_flag” are all set as TRUE (=1). This is followed by the pointer to double array “coord[0]”. Notice
that in the semantics of C language (“pointer arithmatics”), the expression of the symbol “coord” with “[]” means
casting the double** to double*, while the index “0” means with an off-set of zero from the first memory address
of the double*.
An example with two “block()” function calls has the potential of being more adaptive to deal with compli-
cated geometry (see Figure 4•32)
1 double coord1[4][2] = {{0.0, 0.0}, {3.0, 0.0}, {3.0, 3.0}, {0.0, 3.0}},
2 coord2[4][2] = {{3.0, 0.0}, {6.0, 0.0}, {6.0, 3.0}, {3.0, 3.0}};
3 int control_node_flag[4] = {1, 1, 1, 1};
4 block(this, 4, 4, 4, control_node_flag, coord1[0], 0, 0, 3, 3);
5 block(this, 4, 4, 4, control_node_flag, coord2[0], 3, 3, 3, 3);
In this example, the coordinates of the control nodes are given as rectangles for simplicity. The first int argument
after the coordinates of type double* is the first node number generated, the next int argument is the first element
generated. The last two int arguments are “row-wise node number skip” and “row-wise element number skip”.
For example, in line 5 the second block definition has both its first node and first element numbered as “3”. The
row-wise node number and element number both skip “3”. Therefore, the first node number of the second row is
“10” and the first element number of the second row is “9”. When we define the first block in line 4 the nodes
numbered “3”, “10”, “17” and “24” has been defined. On line 5, when the “block()” function is called again,
these four nodes will be defined again. In “fe.lib”, the “block()” function use “Omega_h::set()” instead of
“Omega_h::add()”, in which the database integrity is accomplished by checking the uniqueness of the node
number. Using the terminology of relational database, the node number is the key of the database tabulae in this
case. If a node number exist, it will not be added to the database again.
A third example shows a cylinder consists of eight blocks (see Figure 4•33) which is even much more chal-
lenging. The code for generating these eight blocks is
1 const double PI = 3.141592653509,
2 c4 = cos(PI/4.0), s4 = sin(PI/4.0),
Figure 4•31 16 nodes and 9 elements generated by a single “block()” function call.
0 1 2 3
4
5 6
7
8
9 10
11
12 13 14 15
0 1
2
3 4 5
6 7 8
Finite Element Method Primer
358 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Figure 4•32 A contiguous block generated by two “block()” function calls.
0 1 2 3
7
8 9
10
11
14
15
0 1
2
6 7 8
4 5 6 3
10
12
13
16
17
18 19
20
21
22 23 24
25 26
27
3 4
5
9 10
11
24
17
11
12 13 14
15
16 17
0 1 2
7
8 9
10
14
15
0 1
2
6 7 8
16
21
22 23 24
17
12 13 14
11
4 5 6 3
10
12
13
17 18 19
20
25 26
27
3 4
5
9 10
11
11
15
16 17
common
nodes
Figure 4•33 A cylinder consists of eight blocks. Open circles in the left-hand-side are
control nodes. Tie nodes 164-132, 131-99, 98-66, 65-33, and 32-0 are shown in the right-
hand-side.
132
133
99
100
66
67
33
34
0
1 31
32
64
65
97
98
130
131
163
164
r
i
= 0.5
r
o
= 1
u
i
=100
o
C
u
o
= 0
o
C
tie nodes
Workbook of Applications in VectorSpace C++ Library 359
Two Dimensional Problems
3 c8 = cos(PI/8.0), s8 = sin(PI/8.0),
4 c83 = cos(3.0*PI/8.0), s83 = sin(3.0*PI/8.0),
5 r1 = 0.5, r2 = 1.0;
6 double coord1[7][2] = {{0.0, r1},{c4*r1, s4*r1},{c4, s4},{0.0, r2},
7 {c83*r1, s83*r1},{0.0, 0.0},{c83*r2, s83*r2}},
8 coord2[7][2] = {{c4*r1, s4*r1},{r1, 0.0},{r2, 0.0},{c4, s4},
9 {c8*r1, s8*r1},{0.0, 0.0},{c8*r2, s8*r2}},
10 coord3[7][2] = {{r1, 0.0},{c4*r1, -s4*r1},{c4, -s4},{r2, 0.0},
11 {c8*r1, -s8*r1},{0.0, 0.0},{c8*r2, -s8*r2}},
12 coord4[7][2] = {{c4*r1, -s4*r1},{0.0, -r1},{0.0, -r2},{c4, -s4},
13 {c83*r1, -s83*r1},{0.0, 0.0},{c83*r2, -s83*r2}},
14 coord5[7][2] = {{0.0, -r1},{-c4*r1, -s4*r1},{-c4, -s4},{0.0, -r2},
15 {-c83*r1, -s83*r1},{0.0, 0.0},{-c83*r2, -s83*r2}},
16 coord6[7][2] = {{-c4*r1, -s4*r1},{-r1, 0.0},{-r2, 0.0},{-c4, -s4},
17 {-c8*r1, -s8*r1},{0.0, 0.0},{-c8*r2, -s8*r2}},
18 coord7[7][2] = {{-r1, 0.0},{-c4*r1, s4*r1},{-c4, s4},{-r2, 0.0},
19 {-c8*r1, s8*r1},{0.0, 0.0},{-c8*r2, s8*r2}},
20 coord8[7][2] = {{-c4*r1, s4*r1},{0.0, r1},{0.0, r2},{-c4, s4},
21 {-c83*r1, s83*r1},{0.0, 0.0},{-c83*r2, s83*r2}};
22 int flag[7] = {1, 1, 1, 1, 1, 0, 1};
23 block(this, 5, 5, 7, flag, coord1[0], 0, 0, 28, 28);
24 block(this, 5, 5, 7, flag, coord2[0], 4, 4, 28, 28);
25 block(this, 5, 5, 7, flag, coord3[0], 8, 8, 28, 28);
26 block(this, 5, 5, 7, flag, coord4[0], 12, 12, 28, 28);
27 block(this, 5, 5, 7, flag, coord5[0], 16, 16, 28, 28);
28 block(this, 5, 5, 7, flag, coord6[0], 20, 20, 28, 28);
29 block(this, 5, 5, 7, flag, coord7[0], 24, 24, 28, 28);
30 block(this, 5, 5, 7, flag, coord8[0], 28, 28, 28, 28);
Five tie nodes “164-132”, “131-99”, “98-66”, “65-33”, and “32-0” (see right-hand-side of Eq. 4•33) are gener-
ated when the “tail” of the eighth block comes back to meet the “head” of the first block. The tie nodes are gen-
erated when different node number with same coordinates occurs. In fe.lib the nodes that are generated later is
“tied” to the nodes that are generated earlier. In this example nodes “0”, “33”, “66”, “99”, and “132” are gener-
ated when the first “block()” function call is made. When the eighth “block()” function call is made later, nodes
“32”, “65”, “98”, “131”, and “164” will be generated. The tie nodes are formed when the coordinates are found
to be the same as that of any node generated previously.
For heat conduction problem, if the boundary condition is symmetrical with respect to the center axis, it can
well be written with axisymmetrical formulation and solve as an one dimension problem such as in the subsec-
tion under the title of “Cylindrical Coordinates For Axisymmetrical Problem” on page 302. For the present case
of the hollow cylinder made of one material, the Eq. 4•111 expressed in cylindrical coordinates is
1
1. p. 189 in Carslaw, H.S., and J.C. Jaeger, 1959, “Conduction of heat in solids”, 2nd ed. Oxford University Press, Oxford,
UK.
Finite Element Method Primer
360 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Eq. 4•113
The general solution is u = A+B ln r. The constants A and B are determined by imposing the boundary condi-
tions. For example, if at inner side of the cylinder of r
i
the temperature is kept at u
i
, and at outer side of the cylin-
der of r
o
the temperature is kept at u
o
, we have the solution as
Eq. 4•114
The finite element computation can be turned on using the same project “2d_heat_conduction” in project
workspace “fe.dsw” by setting macro definition “__TEST_CYLINDER” at compile time. The finite element
solution in the radial direction is compared to the analytical solution of Eq. 4•114 and shown in Figure 4•34.For
an additional exercise for function “block()”, we proceed with the fourth example of using three blocks to
approximate a quarter of a circle. In Chapter 3 on page 195, we approximate a quarter of a circle with three
“block()” function calls. In that case we do not have provision of repeated definitions of nodes. In the present
case, we try to minimize the number of the tie nodes by the following code
1 const double PI = 3.141592653509, c = cos(PI/4.0), s = sin(PI/4.0), c2 = c/2, s2 = s/2;
2 double coord1[4][2] = {{0.0,0.0},{0.5, 0.0},{c2, s2}, {0.0, 0.5}},
3 coord2[6][2] = {{0.5,0.0},{1.0,0.0},{c, s},{c2, s2}, {0.0,0.0},{cos(PI/8.0),sin(PI/8.0)}},
4 coord3[7][2] = {{0.0, 0.5},{c2, s2},{c, s},{0.0,1.0},
5 {0.0, 0.0},{0.0, 0.0}, {cos(3.0*PI/8.0), sin(3.0*PI/8.0)}};
6 int flag1[4] = {1, 1, 1, 1}, flag2[6] = {1, 1, 1, 1, 0, 1}, flag3[7] = {1, 1, 1, 1, 0, 0, 1};
7 block(this, 5, 5, 4, flag1, coord1[0], 0, 0, 4, 4);
8 block(this, 5, 5, 6, flag2, coord2[0], 4, 4, 4, 4);
9 block(this, 5, 5, 7, flag3, coord3[0], 45, 32, 0, 0);
d
dr
----- r
du
dr
------
¸ ,
¸ _
0 =
u
exact
u
i
ln
r
o
r
----
¸ ,
¸ _
u
o
ln
r
r
i
---
¸ ,
¸ _
+
ln
r
o
r
i
----
¸ ,
¸ _
----------------------------------------------- =
Figure 4•34 Finite element nodal solutions in the radial direction comparing to the
analytical solution in Eq. 4•114 for the heat conduction of the cylinder.
0.5 0.6 0.7 0.8 0.9
20
40
60
80
100
o
C
r
Workbook of Applications in VectorSpace C++ Library 361
Two Dimensional Problems
The numbering of the elements and nodes for the first two blocks are similar to that of the second example. After
the third block has been generated, 9 tie-nodes will be generated including “45-36”, “46-37”, “47-38”, “48-39”,
“49-40”, “54-41”, “59-42”, “64-43”, and “69-44”.
Lagrange 9-nodes Element for Heat Conduction
The element formulation “HeatQ4” implemented the bilinear 4-node element for heat conduction. We intro-
duce a Lagrangian 9-node element “HeatQ9” as follows
1 class HeatQ9 : public Element_Formulation {
2 public:
3 HeatQ9(Element_Type_Register a) : Element_Formulation(a) {}
4 Element_Formulation *make(int, Global_Discretization&);
5 HeatQ9(int, Global_Discretization&);
6 };
7 Element_Formulation* HeatQ9::make(int en, Global_Discretization& gd) {
8 return new HeatQ9(en,gd);
9 }
10 HeatQ9::HeatQ9(int en, Global_Discretization& gd) : Element_Formulation(en, gd) {
11 Quadrature qp(2, 9); // 2-d 3 3 Gaussain quadrature
12 H1 Z(2, (double*)0, qp),
13 N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE(
Figure 4•35 Three block function calls to approximate a quarter of a circle. The right-hand-
side shows the element numbering scheme and the left-hand-side shows the node numbering
scheme.
0 1 2 3 4 5 6 7 8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
0 5
6
7
8
9
10
11
12
13
14
15
16
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
1 2 3 4
17
45
46
47
element numbering
node numbering
×
Finite Element Method Primer
362 Workbook of Applications in VectorSpace C++ Library
Chapter 4
14 "int, int, Quadrature", 9/*nen*/, 2/*nsd*/, qp),
15 Zai, Eta;
16 Zai &= Z[0]; Eta &= Z[1];
17 N[0] = (1-Zai)*(1-Eta)/4; N[1] = (1+Zai)*(1-Eta)/4; // 4-9 node shape functions
18 N[2] = (1+Zai)*(1+Eta)/4; N[3] = (1-Zai)*(1+Eta)/4;
19 N[8] = (1-Zai.pow(2))*(1-Eta.pow(2));
20 N[0] -= N[8]/4; N[1] -= N[8]/4; N[2] -= N[8]/4; N[3] -= N[8]/4;
21 N[4] = ((1-Zai.pow(2))*(1-Eta)-N[8])/2; N[5] = ((1-Eta.pow(2))*(1+Zai)-N[8])/2;
22 N[6] = ((1-Zai.pow(2))*(1+Eta)-N[8])/2; N[7] = ((1-Eta.pow(2))*(1-Zai)-N[8])/2;
23 N[0] -= (N[4]+N[7])/2; N[1] -= (N[4]+N[5])/2;
24 N[2] -= (N[5]+N[6])/2; N[3] -= (N[6]+N[7])/2;
25 H1 X = N*xl;
26 H0 Nx = d(N) * d(X).inverse();
27 J dv(d(X).det());
28 double k_ = 1.0;
29 stiff &= (Nx * k_ * (~Nx)) | dv; // {9 2}*{2 9}={9 9}
30 }
31 Element_Formulation* Element_Formulation::type_list = 0;
32 Element_Type_Register element_type_register_instance;
33 static HeatQ9 heatq9_instance(element_type_register_instance); // element type # 1
34 static HeatQ4 heatq4_instance(element_type_register_instance); // element type # 0
Lines 17-24 are shape function definition for Lagragian 4-to-9-node element that we have already used in Chap-
ter 3. Lines 33, and 34 register the element formulations. The last element formulation register has the element
type number “0”. This number increases backwards to element(s) registered earlier. We can also use the
“block()” function call to define Lagrangian 9-node element as (see Figure 4•36)
1 EP::element_pattern EP::ep = EP::LAGRANGIAN_9_NODES;
2 Omega_h::Omega_h() {
...
3 block(this, 5, 5, 7, flag, coord1[0], 0, 0, 28, 14, 1);
4 block(this, 5, 5, 7, flag, coord2[0], 4, 2, 28, 14, 1);
5 block(this, 5, 5, 7, flag, coord3[0], 8, 4, 28, 14, 1);
6 block(this, 5, 5, 7, flag, coord4[0], 12, 6, 28, 14, 1);
7 block(this, 5, 5, 7, flag, coord5[0], 16, 8, 28, 14, 1);
8 block(this, 5, 5, 7, flag, coord6[0], 20, 10, 28, 14, 1);
9 block(this, 5, 5, 7, flag, coord7[0], 24, 12, 28, 14, 1);
10 block(this, 5, 5, 7, flag, coord8[0], 28, 14, 28, 14, 1);
11 }
Line 1 specified the elements generated are Lagragian 9-nodes elements. The last integer argument in line 3 to
line 10 indicate the element type number is 1, which corresponding to the “HeatQ9” element that we just regis-
tered. The computation of the Lagragian 9-node elements can be activated by setting macro definition
“__TEST_QUADRATIC_CYLINDER” for the same project “2d_heat_conduction” in the project workspace
file “fe.dsw”.
× × ×
Workbook of Applications in VectorSpace C++ Library 363
Two Dimensional Problems
Post-Processing—Heat Flux on Gauss Points
After the solutions on temperature distribution is obtained, heat flux can be computed from Fourier law of
heat conduction of Eq. 4•98; i.e.,
This step is often referred to as post-processing in finite element method. The derivatives of shape function,
, on Gaussian integration points are available at the constructor of class “Element_Formulation”. The
gradients of temperature distribution are approximated by
Eq. 4•115
Therefore,
Eq. 4•116
Therefore, after the solutions of nodal values, , are obtained, we can loop over each element to calculate the
heat flux on its Gaussian integration points, such as,
Figure 4•36 9-node Lagrangian quadrilateral elements generated by eight “block()” function
calls.
q κ∇u – =
∇N
a
ξ η , ( )
∇u
e
h
ξ η , ( )
∇N
a
ξ η , ( )u
ˆ
e
a

q
e
h
κ ∇N
a
ξ η , ( )u
ˆ
e
a
( ) – =
u
ˆ
e
a
Finite Element Method Primer
364 Workbook of Applications in VectorSpace C++ Library
Chapter 4
1 HeatQ4::HeatQ4(int en, Global_Discretization& gd) : Element_Formulation(en, gd) {
2 ...
3 if(Matrix_Representation::Assembly_Switch == Matrix_Representation::FLUX) {
4 H0 flux = INTEGRABLE_VECTOR("int, Quadrature", 2, qp);
5 flux = 0.0;
6 for(int i = 0; i < nen; i++) {
7 flux[0] += - k * Nx[i][0]*(ul[i]+gl[i]); //
8 flux[1] += - k * Nx[i][1]*(ul[i]+gl[i]);
9 }
10 int nqp = qp.no_of_quadrature_point(); cout.flush();
11 for(int i = 0; i < nqp; i++) {
12 cout << setw(9) << en
13 << setw(14) << ((H0)X[0]).quadrature_point_value(i)
14 << setw(14) << ((H0)X[1]).quadrature_point_value(i)
15 << setw(14) << (flux[0].quadrature_point_value(i))
16 << setw(14) << (flux[1].quadrature_point_value(i)) << endl;
17 }
18 } else stiff &= ...
19 }
20 int main() {
21 ...
22 Matrix_Representation::Assembly_Switch = Matrix_Representation::FLUX;
23 cout << "Heat flux on gauss integration points: " << endl;
24 cout.setf(ios::left,ios::adjustfield);
25 cout << setw(9) << " elem #, " << setw(14) << "x-coor.," << setw(14) << "y-coor.,"
26 << setw(14) << " q_x, " << setw(14) << " q_y, " << setw(14) << endl;
27 mr.assembly(FALSE);
28 }
Line 27 in the main() program is to call “Matrix_Representation::assembly()” with an argument “FALSE” to
indicate that the nodal loading on the right-hand-side is not to be performed. This function invokes element for-
mulation with a flag in class “Matrix_Representation” set to “Matrix_Representation::Assembly_Switch =
Matrix_Representation::FLUX”. The real computation is done at lines 7-8, where these two lines simply imple-
mented the Fourier law of heat conduction. The rest of lines is just the run-of-the-mill C++ output formatting.
The information on the coordinates of the Gauss points and their corresponding heat flux values are reported ele-
ment-by-element.
Post-Processing—Heat Flux Nodal Projection Method
Since the solutions of finite element computation are the temperatures on nodes, we may also interested in
having the heat flux to be reported on nodes. However, nodal heat flux, , requires much more elaborated post-
processing. The heat flux on an element can be interpolated from the nodal heat flux as
Eq. 4•117
q κ∇u – =
q
ˆ
e
a
q
e
h
N
a
ξ η , ( )q
ˆ
e
a

Workbook of Applications in VectorSpace C++ Library 365
Two Dimensional Problems
Since the shape function is an integrable object, its value is actually evaluated and stored only at the Gauss inte-
gration points. Now we can define error as the difference of of Eq. 4•117 with of Eq. 4•116. This error is
then distributed over the element domain by making a weighted-residual statement (as Eq. 3•105 of Chapter 3 on
page 352) with Galerkin weighting that w = N
a

Eq. 4•118
Substituting Eq. 4•117 and Eq. 4•116 into Eq. 4•118, we have
Eq. 4•119
We identify, in Eq. 4•119, the consistent mass matrix (with unit density), M, as
Eq. 4•120
The nodal heat flux, , can be solved from Eq. 4•119. This nodal solution procedure is described as smoothing
or projection in finite element.
1
An approximation on Eq. 4•120 which alleviates the need for matrix solver is to
define lumped mass matrix as
Eq. 4•121
This is the row-sum method among many other ways of defining a lumped mass matrix.
2
An alternative thinking on Eq. 4•118 of Galerkin weighting of the weighted-residual statement is that we can
write least-squares approximation of error as
Eq. 4•122
1. p. 346 in Zienkiewicz, O.C., and R.L. Taylor, 1989, “The finite element method: basic formulation and linear problems”,
4the ed., vol. 1, McGraw-Hill, London, UK,
see also p. 226 in Hughes, T. J.R., “The finite element method: linear static and dynamic finite element analysis”, Prentice-
Hall, Inc., Englewood Cliffs, New Jersey.
2. see appendix 8 in Zienkiewicz, O.C., and R.L. Taylor, 1989, “The finite element method: basic formulation and linear
problems”, 4the ed., vol. 1, McGraw-Hill, London, UK.
q
e
h
q
e
h
N
a
q
e
h
q
e
h
– ( ) Ω d


0 =
N
a
N
b
Ω d


¸ ,

¸ _
q
ˆ
e
b
N
a
κ – ∇N
b
u
ˆ
e
b
( ) ( ) Ω d


=
M N
a
N
b
Ω d



q
ˆ
e
a
M
L
N
a
N
b
Ω d


b

a b = ,
0 a b ≠ , ¹
¹
'
¹
¹

q
e
h
q
e
h
– ( )
2
Ω d


0 =
Finite Element Method Primer
366 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Minimization by taking derivative with respect to the nodal heat flux, , and using its interpolation relation of
Eq. 4•117, gives back to Eq. 4•118. Therefore, the nodal flux can be considered as obtained through least squares
approximation too.
Eq. 4•119 can be solved with a full-scale finite element method, direct or iterative. However, as post-process-
ing procedure, it will be more desirable to have a simplified approximation that can be performed element-by-
element without even to assemble the global mass matrix, or to invoke matrix solver to solve for Eq. 4•119, such
as,
1 HeatQ4::HeatQ4(int en, Global_Discretization& gd) : Element_Formulation(en, gd) {
2 ...
3 if(Matrix_Representation::Assembly_Switch == Matrix_Representation::NODAL_FLUX) {
4 int flux_no = 2;
5 the_element_nodal_value &= C0(nen*flux_no, (double*)0);
6 C0 projected_nodal_flux = SUBVECTOR("int, C0&", flux_no, the_element_nodal_value);
7 H0 flux = INTEGRABLE_VECTOR("int, Quadrature", flux_no, qp);
8 flux = 0.0;
9 for(int i = 0; i < nen; i++) {
10 flux[0] += - k * Nx[i][0]*(ul[i]+gl[i]);
11 flux[1] += - k * Nx[i][1]*(ul[i]+gl[i]);
12 }
13 for(int i = 0; i < nen; i++) {
14 C0 lumped_mass(0.0);
15 for(int k = 0; k < nen; k++)
16 lumped_mass += (((H0)N[i])*((H0)N[k])) | dv;
17 projected_nodal_flux(i) = ( ((H0)N[i])*flux | dv ) / lumped_mass;
18 }
19 } else stiff &= (Nx * k * (~Nx)) | dv;
20 }
21 int main() {
22 ...
23 Matrix_Representation::Assembly_Switch = Matrix_Representation::NODAL_FLUX;
24 mr.assembly(FALSE);
25 cout << "nodal heat flux:" << endl;
26 for(int i = 0; i < oh.total_node_no(); i++) {
27 int node_no = oh.node_array()[i].node_no();
28 cout << "{ " << node_no << "| "
29 << (mr.global_nodal_value()[i][0]) << ", "
30 << (mr.global_nodal_value()[i][1]) << "}" << endl;
31 }
32 ...
33 }
q
ˆ
e
a
Workbook of Applications in VectorSpace C++ Library 367
Two Dimensional Problems
Lines 14-16 are the row-sum lumped mass matrix (in Eq. 4•121). Line 17 is the element-by-element solution of
approximated Eq. 4•119. In the main() program “NODAL_FLUX” switch is set (line 23) and assembly() invokes
the nodal projection procedure through Element_Formulation. Nodal values are shared by various number of ele-
ments. The assembly() function keeps and internal count on how many evaluations are performed on a particular
node, and it will compute an average nodal value from these nodal values for the node. The computation is done
with macro definitions “__TEST_CYLINDER” and “__TEST_FLUX”. The results of nodal heat flux are shown
in Figure 4•37 The projected nodal heat flux values are obviously less accurate than the temperature solutions
shown in Figure 4•37a. The projected nodal heat fluxes on the boundaries are significantly less accurate than
those in the interior. The reason can be easily deduce by studying Figure 4•37b, since the nodal heat fluxes (open
squares) are just least squares fit of a set of piece-wise line segments of the Gauss point heat fluxes (open cir-
cles).
0.5 0.6 0.7 0.8 0.9
160
180
200
220
240
260
280
Figure 4•37 (a) Nodal heat flux shown in vectors, (b) projected radial heat flux on nodes are
shown in open squares. Heat flux on Gauss points are shown in open circles. The solid curve is the
analytical solution q
r
= du/dr = 100/(r ln 2), which is obtained from differentiation with respect to
r on Eq. 4•114.
r
q
(a) (b)
Finite Element Method Primer
368 Workbook of Applications in VectorSpace C++ Library
Chapter 4
4.3.2 Potential Flow
Basic Physics and Element Formulation
We consider incompressible, inviscid fluid which gives the potential flow. The conditions of incompressible
(a solenoidal field) and irrotational (a toroial- free field) give
, Eq. 4•123
respectively. In 2-D, Eq. 4•123 reduces to the continuity equation
, Eq. 4•124
and an equation with zero vorticity component perpendicular to the x-y plane
Eq. 4•125
From the continuity equation Eq. 4•124, it follows that u dy - v dx is an total derivative, defined as
dψ = u dy - v dx Eq. 4•126
where ψ is a scalar function, and
Eq. 4•127
Substituting Eq. 4•127 back to Eq. 4•124 gives the identity of cross derivatives of ψ to be equal. This is the con-
dition that ψ to be a potential function in calculus. Integration of Eq. 4•126 along an arbitrary path, as shown in
Figure 4•38a, gives the volume flux across the path. Along a stream line the volume flux across it is zero by def-
inition. That is along a streamline ψ is constant. Therefore, the scalar function ψ is known as the stream func-
tion.
Substituting Eq. 4•127 into the condition of irrotationality, Eq. 4•125, gives
Eq. 4•128
We identify that is the Laplace equation.
Similarly starting from Eq. 4•125 of condition of irrotationality, curl v = 0 at all point of the fluid. According
to Stokes’s theorem circulation along any closed loop is zero, as
div u u ∇• ≡ 0 = and curl u u ∇× ≡ , 0 =
∂u
∂x
------
∂v
∂y
------ + 0 =
∂u
∂y
------
∂v
∂x
------ – 0 =
u
∂ψ
∂y
------- and v ,
∂ψ
∂x
------- – = =
div grad ψ ( ) ψ ∇ ( ) ψ ∇
2
≡ ∇• ≡

2
u
∂x
2
--------

2
v
∂y
2
-------- + 0 = =
ψ ∇
2
0 =
Workbook of Applications in VectorSpace C++ Library 369
Two Dimensional Problems
Eq. 4•129
From Figure 4•38b, we have two different integration paths, C
1
and C
2
, along any two points form a closed cir-
cle.
Eq. 4•130
Therefore, any two paths of integration give the same result; i.e., the integration depends only on end-points.
Therefore, we can define a potential function φ, i.e.,
Eq. 4•131
φ is known as the velocity potential, and the components of velocity as
Eq. 4•132
Again, substituting Eq. 4•132 back to Eq. 4•125 of condition of irrotationality, we have the cross derivatives of φ
which is identical to assert the exact differential nature of φ. Substituting Eq. 4•132 into the continuity equation
of Eq. 4•124, we have another Laplace equation that
Eq. 4•133
Furthermore, from Eq. 4•127 and Eq. 4•132, we have
Figure 4•38 (a) The volume flux across an arbitrary integration path is equal to u dy - v dx.
If the integration path coincides with the streamline, the volume flux across the integration
path should become zero by definition. (b) The circulation of a loop is zero for irrotational
flow. Therefore, a potential function φ can be defined which only depends on position.
x
y
u
v
dy
dx
volume flux across path
= u dy - v dx
(a)
C
1
C
2
(b)
A
B
u x d •
C

°
0 =
u x d •
C
1

u x d •
C
2

+ 0 or u x d •
C
1

, u x d •
C
2

– = =
dφ x ( ) u – dx • or φ x ( ) ∇ , u x ( ) – = =
u
∂φ
∂x
------ – and v ,
∂φ
∂y
------ – = =
φ ∇
2
– 0 =
Finite Element Method Primer
370 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Eq. 4•134
This relation ensures that the gradients of stream function and velocity potential are orthogonal to each other,
since
Eq. 4•135
The gradients are the normals to the equipotential lines of φ and the streamlines of ψ. Therefore, the “contours”
of φ and ψ are orthogonal to each others.
An example of finite element problem
1
(a confined flow around a cylinder is shown in Figure 4•39) in both
stream function—ψ formulation and velocity potential—φ formulation are solved using VectorSpace C++
Library and “fe.lib” in the followings.
Stream Function—ψ Formulation
Recall Eq. 4•127,
At the right-boundary Γ
AE
(Figure 4•39b), since u = ∂ψ/d∂, we can integrate ψ as
ψ(y) - ψ
0
= U
0
y. Eq. 4•136
At the bottom-boundary Γ
AB
we choose the arbitrary reference value of ψ
0
= 0. Therefore, along the left-bound-
ary Γ
AE
, Eq. 4•136 simplified to ψ(y) = U
0
y. The streamline at boundary Γ
BC
follows from the boundary Γ
AB
which has ψ =ψ
0
(= 0). On the top-boundary Γ
ED
, y = 2, we have ψ(2) = 2U
0
. Notice that the corner E is shared
by the boundaries Γ
AE
and Γ
ED
. At the right-boundary Γ
DC
the horizontal velocity, u, is unknown, but the verti-
cal velocity v = 0; i.e., v = −∂ψ/∂x = 0.
The Program Listing 4•14 implements the Eq. 4•128 with the above boundary conditions. The only differ-
ence to the 2-D heat conduction problem is the post-processing of the derivative information.
1 if(Matrix_Representation::Assembly_Switch == Matrix_Representation::NODAL_FLUX) {
2 int velocity_no = 2;
3 the_element_nodal_value &= C0(nen*velocity_no, (double*)0);
4 C0 projected_nodal_velocity = SUBVECTOR("int, C0&", velocity_no, the_element_nodal_value);
5 H0 Velocity = INTEGRABLE_VECTOR("int, Quadrature", velocity_no, qp);
6 Velocity = 0.0;
7 for(int i = 0; i < nen; i++) {
1. p. 360-365 in Reddy, J.N., “An introduction to the finite element method”, 2nd ed., McGraw-Hill, Inc., New York.
∂φ
∂x
------ –
∂ψ
∂y
------- = and
∂φ
∂y
------ – ,
∂ψ
∂x
------- – =
φ ∇ ψ ∇ •
∂φ
∂x
------
∂ψ
∂x
-------
∂φ
∂y
------
∂ψ
∂y
------- + 0 = =
u
∂ψ
∂y
------- and v ,
∂ψ
∂x
------- – = =
Workbook of Applications in VectorSpace C++ Library 371
Two Dimensional Problems
8 Velocity[0] += Nx[i][1]*(ul[i]+gl[i]);
9 Velocity[1] += - Nx[i][0]*(ul[i]+gl[i]);
10 }
11 for(int i = 0; i < nen; i++) {
12 C0 lumped_mass(0.0);
13 for(int k = 0; k < nen; k++)
14 lumped_mass += (((H0)N[i])*((H0)N[k])) | dv;
15 projected_nodal_velocity(i) = ( ((H0)N[i])*Velocity | dv ) / lumped_mass;
16 }
17 } else stiff &= (Nx * (~Nx)) | dv;
From Eq. 4•127, the velocity is interpolated at the element formulation level as
Figure 4•39(a) A confined flow around a circular cylinder. Only the upper left quadrant is
model due to symmetries of geometry, boundary conditions, and PDE. (b) stream
function B.C., and (c) velocity potential B.C.
U
0
8
4
(a)
(b) stream function B.C. (c) velocity potential B.C.
ψ = y U
0
ψ · 0
ψ · 0
ψ · 2U
0
∂ψ/∂x = 0
-∂φ/∂x = U
0
∂φ/∂y = 0
φ · 0
∂φ/∂n = 0
∂φ/∂y = 0
A B
C
D E
A
B
C
D
E
Finite Element Method Primer
372 Workbook of Applications in VectorSpace C++ Library
Chapter 4
#include "include\fe.h"
EP::element_pattern EP::ep = EP::QUADRILATERALS_4_NODES;
Omega_h::Omega_h() { const double PI = 3.141592653509, c = cos(PI/4.0), s = sin(PI/4.0),
c1 = cos(PI/8.0), s1 = sin(PI/8.0), c2 = cos(3.0*PI/8.0), s2 = sin(3.0*PI/8.0);
double coord0[4][2] = {{0.0, 0.0}, {3.0, 0.0}, {1.0, 2.0}, {0.0, 2.0}},
coord1[5][2] = {{3.0, 0.0}, {4.0-c, s}, {3.0, 2.0}, {1.0, 2.0}, {4.0-c1, s1}},
coord2[5][2] = {{4.0-c, s}, {4.0, 1.0}, {4.0, 2.0}, {3.0, 2.0}, {4.0-c2, s2}};
int control_node_flag[5] = {TRUE, TRUE, TRUE, TRUE, TRUE};
block(this, 5, 5, 4, control_node_flag, coord0[0], 0, 0, 8, 8);
block(this, 5, 5, 5, control_node_flag, coord1[0], 4, 4, 8, 8);
block(this, 5, 5, 5, control_node_flag, coord2[0], 8, 8, 8, 8); }
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
__initialization(df, omega_h); const double U0 = 1.0; const double h_y = 0.5;
for(int i = 0; i <= 12; i++) the_gh_array[node_order(i)](0) = gh_on_Gamma_h::Dirichlet;
for(int i = 52; i <= 64; i++) { the_gh_array[node_order(i)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(i)][0] = 2.0*U0; }
for(int i = 1; i <= 4; i++) { the_gh_array[node_order(i*13)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(i*13)][0] = (((double)i)*h_y)*U0; } }
class Irrotational_Flow_Q4 : public Element_Formulation { public:
Irrotational_Flow_Q4(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
Irrotational_Flow_Q4(int, Global_Discretization&); };
Element_Formulation* Irrotational_Flow_Q4::make(int en, Global_Discretization& gd) {
return new Irrotational_Flow_Q4(en,gd); }
Irrotational_Flow_Q4::Irrotational_Flow_Q4(int en, Global_Discretization& gd) :
Element_Formulation(en, gd) { Quadrature qp(2, 4);
H1 Z(2, (double*)0, qp), Zai, Eta,
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 4, 2, qp);
Zai &= Z[0]; Eta &= Z[1]; N[0] = (1-Zai)*(1-Eta)/4; N[1] = (1+Zai)*(1-Eta)/4;
N[2] = (1+Zai)*(1+Eta)/4; N[3] = (1-Zai)*(1+Eta)/4;
H1 X = N*xl; H0 Nx = d(N) * d(X).inverse(); J dv(d(X).det());
if(Matrix_Representation::Assembly_Switch == Matrix_Representation::NODAL_FLUX) {
int v_no = 2; the_element_nodal_value &= C0(nen*velocity_no, (double*)0);
C0 projected_nodal_velocity = SUBVECTOR("int, C0&", v_no, the_element_nodal_value);
H0 Velocity = INTEGRABLE_VECTOR("int, Quadrature", v_no, qp); Velocity = 0.0;
for(int i = 0; i < nen; i++) { Velocity[0] += Nx[i][1]*(ul[i]+gl[i]);
Velocity[1] += - Nx[i][0]*(ul[i]+gl[i]); }
for(int i = 0; i < nen; i++) { C0 lumped_mass(0.0);
for(int k = 0; k < nen; k++) lumped_mass += (((H0)N[i])*((H0)N[k])) | dv;
projected_nodal_velocity(i) = ( ((H0)N[i])*Velocity | dv ) / lumped_mass; }
} else stiff &= (Nx * (~Nx)) | dv; }
Element_Formulation* Element_Formulation::type_list = 0;
Element_Type_Register element_type_register_instance;
static Irrotational_Flow_Q4 flowq4_instance(element_type_register_instance);
int main() { int ndf = 1; Omega_h oh; gh_on_Gamma_h gh(ndf, oh); U_h uh(ndf, oh);
Global_Discretization gd(oh, gh, uh); Matrix_Representation mr(gd);
mr.assembly(); C0 u = ((C0)(mr.rhs())) / ((C0)(mr.lhs()));
gd.u_h() = u; gd.u_h() = gd.gh_on_gamma_h(); cout << gd.u_h();
Matrix_Representation::Assembly_Switch = Matrix_Representation::NODAL_FLUX;
mr.assembly(FALSE); cout << "nodal velocity:" << endl;
for(int i = 0; i < uh.total_node_no(); i++)
cout << "{ " << oh.node_array()[i].node_no() << "| " <<
(mr.global_nodal_value()[i]) << "}" << endl;
return 0;
}
define nodes and elements
define B.C.
define element formulation
assembly and matrix solver
update free and fixed dof
post-processing for nodal velocity
N
a
ξ η , ( )
1
4
--- 1 ξ
a
ξ + ( ) 1 η
a
η + ( ) =
u
e
h
∂N
a
∂y
---------ψ
ˆ
a
∂N
a
∂x
---------ψ
ˆ
a
– ,
T
=
u
ˆ
e
M
L
( )
1 –
Nu
e
h
( ) Ω d


=
k
e
∇N ∇N ⊗ ( )det
∂x
∂ξ
------
¸ ,
¸ _

Ωe

=
Listing 4•14 Stream function formulation potential flow problem(project “fe.ide”, project “potential_flow”
with macro definition “__TEST_STREAM_FUNCTION” set).
Workbook of Applications in VectorSpace C++ Library 373
Two Dimensional Problems
Eq. 4•137
The least squares nodal projection can be calculated accordingly as
Eq. 4•138
where M
L
is the lumped mass matrix. The results of this computation with element discretization, streamlines,
and nodal velocity vectors are shown in Figure 4•40.
Velocity Potential—φ Formulation
The velocity potential formulation has the boundary conditions shown in Figure 4•39(c). Recall Eq. 4•132
Eq. 4•139
At the left-boundary Γ
AE
of Figure 4•39c, from u = - ∂φ/∂x, we have ∂φ/∂x = - U
0
. At the top and bottom-bound-
aries Γ
AB
and Γ
ED
we have ∂φ/∂y = 0. On the cylinder surface Γ
BC
, ∂φ/∂n = 0, where n is its outward normal. At
the left-boundary Γ
CD
a reference value of φ is set to zero.
The code is implemented in the same project file without the macro definition
“__TEST_STREAM_FUNCTION” set at compile time. The results of this computation with element discretiza-
tion, velocity equipotential lines, and nodal velocity vectors are shown in Figure 4•41.
Inspecting Figure 4•40 and Figure 4•41, we see that the contours lines of the stream function ψ and velocity
potential φ is orthogonal to each others at every point. This is consistent with the orthogonality condition proved
u
e
h
∂N
a
∂y
---------ψ
ˆ
a
∂N
a
∂x
---------ψ
ˆ
a
– ,
T
=
u
ˆ
e
M
L
( )
1 –
Nu
e
h
( ) Ω d


=
0.25
0.5
0.75
1.0
1.25
1.5
1.75
2.0
0.0
ψ=
Figure 4•40 Finite element discretization (open circles are nodes), streamlines (ψ = 0-2.0 at 0.25
intervals), and nodal velocity vectors shown as arrows.
u
∂φ
∂x
------ – and v ,
∂φ
∂y
------ – = =
Finite Element Method Primer
374 Workbook of Applications in VectorSpace C++ Library
Chapter 4
in Eq. 4•135. The contours of stream function ψ and velocity potential φ make a smoothed mesh. Actually, this
is a popular method to generate a finite element mesh automatically.
1
1. p.99-106 in George, P.L., 1991, “Automatic mesh generation: application to finite element methods”, John Wiley & Sons,
Masson, Paris, France.
0.0
0.5
1.0
1.5
2.0
2.5 3.0 3.5
4.0 4.5 5.0
0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5
5.0 φ ·
Figure 4•41 Finite element discretization (open circles are nodes), velocity equi-potential lines
(φ = 0-5.0 at 0.5 intervals), and nodal velocity vectors shown as arrows.
Workbook of Applications in VectorSpace C++ Library 375
Two Dimensional Problems
4.3.3 Plane Elasticity
We introduce three commonly used formulations for plane elasticity (1) the coordinate-free tensorial formula-
tion, (2) the indicial notation formulation, and (3) the B-matrix (strain-displacement Matrix) formulation. We begin
from Cauchy’s equation of equilibrium which is the continuum version of Newton’s second law of motion. In
static state, the summation of surface force (= divergence of the stress tensor; i.e., div σ ) and external
force (f) equals zero. We expressed this balance of forces in both the coordinate free tensorial notation and the
indicial notation as
Eq. 4•140
This is subject to displacement boundary conditions and traction boundary conditions
Eq. 4•141
where t is the traction and n is the outward unit surface normal. The weighted-residual statement of the Eq. 4•140
is
Eq. 4•142
Integration by parts and then applying the divergence theorem of Gauss, we have
: , or
Eq. 4•143
where the gradient operator, “grad”, and its relation to divergence operator, “div”, are
and , Eq. 4•144
respectively. The trace operator, “tr”, is the summation of all diagonal entries. The operator “:”, in Eq. 4•143, is
the double contraction. Considering the variation of “w” is chosen to be homogeneous at Γ
g
, the second term of
the boundary integral, in Eq. 4•143, can be restricted to Γ
h
as
: , or
Eq. 4•145
We first develop in tensorial notation for its clarity in physical meaning. The Cauchy stress tensor, σ, in Eq.
4•145 can be decomposed as
Eq. 4•146
σ ∇• ≡
div σ f + 0 or σ
i j j ,
f
i
+ , 0 = =
u g on Γ
g
and t , σ n • t
i
σ
ij
n
j
h on Γ
h
= = = = =
w div σ f + ( )


0 or w
i
σ
i j j ,
f
i
+ ( )


, 0 = =
grad w ( )


– σdΩ w σ n • ( )dΓ wfdΩ 0 =


+
Γ

+
w
i j ,
σ
i j
dΩ w
i
σ
ij
n
j
dΓ w
i
f
i
dΩ 0 =


+
Γ

+



grad w ∇w ∇ w ⊗ w
i j ,
= = = div w ∇ w • tr grad w ( ) w
i i ,
= = =
grad w ( )


– σdΩ whdΓ
h
Γ
h

wfdΩ 0 =


+ +
w
i j ,
σ
ij
dΩ w
i
h
i

h
w
i
f
i
dΩ 0 =


+
Γ
h

+



σ p I – τ + =
Finite Element Method Primer
376 Workbook of Applications in VectorSpace C++ Library
Chapter 4
where p is the pressure, I is the unit tensor, and τ is the deviatoric stress tensor. For isotropic material, the con-
stitutive equations are
, and Eq. 4•147
where λ and µ are the Lamé constants. µ is often denoted as G for the shear modulus. The operator def u is
defined as the symmetric part of grad u; i.e.,
Eq. 4•148
where the superscript “s” denotes the symmetrical part of grad ( ), and ε is the (infiniteismal) strain tensor,
and the skew-symmetric part of grad u is defined as
Eq. 4•149
def u and rot u are orthogonal to each other. From Eq. 4•148 and Eq. 4•149, we have the additative decomposi-
tion of grad u as
Eq. 4•150
Recall the first term in Eq. 4•145, and substituting the constitutive equations Eq. 4•146 and Eq. 4•147
: : Eq. 4•151
Note that,
grad w : I = tr(grad w) = div w Eq. 4•152
The last identity is from the second part of the Eq. 4•144. With the Eq. 4•150 and the orthogonal relation of def
u and rot u, we can verify that
grad w : (2µ def u) = (def u + rot u) : (2µ def u) = 2 µ (def u : def u) Eq. 4•153
where the double contraction of def u can be written as
def u : def u = tr((def u)
T
def u) Eq. 4•154
With Eq. 4•152 and Eq. 4•153, the Eq. 4•151 becomes
: : Eq. 4•155
With the element shape function defined, e.g., as Eq. 4•104, the element stiffness matrix is
p λ – div u = τ 2µ def u =
def u ∇
s
u
1
2
--- grad u grad u ( )
T
+ ( ) ε ≡ ≡ ≡
∇ ≡
rot u
1
2
--- grad u grad u ( )
T
– ( ) ≡
grad u def u rot u + =
grad w ( )


σdΩ grad w ( )


= λIdiv u 2µ def u + ( )dΩ
grad w ( )


σdΩ λ div w • div u ( ) 2µ def w ( + [


= def u ( )]dΩ
Workbook of Applications in VectorSpace C++ Library 377
Two Dimensional Problems
where indices {a, b} in superscripts and subscripts are the element node numbers.
In the indicial notation, we have the infinitesimal strain tensor ε
ij
(u) = def u = u
(i,j)
(with the parenthesis in
the subscript denotes the symmetric part), and the generalized Hooke’s law as
σ
ij
= c
ijkl
ε
kl
Eq. 4•157
c
ijkl
is the elastic coefficients. For isotropic material, it is well-known that
c
ijkl
= λ δ
ij
δ
kl
+µ (δ
ik
δ
jl
+ δ
il
δ
jk
) Eq. 4•158
where δ
ij
is the Kronecker delta (δ
ij
= 1 if i = j, otherwise δ
ij
=0). The equivalence of Eq. 4•155 is
Eq. 4•159
The last identity is due to the minor symmetry of c
ijkl
. The element stiffness matrix for the indicial notation for-
mulation is
Eq. 4•160
where the indices {i, j} are the degree of freedom numbers (0 i, j < ndf, where ndf is the “number degree of
freedoms” which equals to the nsd the “number of spatial dimension” in the present case; i.e., 0 k < nsd), and
the indices {a, b} are element node numbers (0 a, b < nen, where nen is the “element node number”). The rela-
tion of indices {p, q} and {i, a, j, b} are defined as
p = ndf (a-1) + i, and q = ndf (b-1)+j Eq. 4•163
In the engineering convention, the strain tensor, ε, and stress tensor, σ, are flatten out as vectors (e.g., in 2-D)
Eq. 4•164
I. Coordinate-Free Tensorial Forumlation:
: Eq. 4•156 k
e
a φ
e
a
φ
e
b
, ( ) λ div N
a
• div N
b
( ) 2µ def N
a
( + [


= = def N
b
)]dΩ
w
i j ,
σ
i j
dΩ


w
i j , ( )
c
i jkl
u
k l , ( )
dΩ


w
i j ,
c
i jkl
u
k l ,
dΩ


= =
k
e
pq
k
e
i aj b
N
a k ,
λ δ
ik
δ
jl
( ) µ δ
i j
δ
kl
δ
il
δ
kj
+ ( ) + [ ] N
b l ,
dΩ


= =
II. Indicial Notation Forumlation:
Eq. 4•161
k
e
i ajb
λ N
a i ,
N
b j ,
dΩ


µ δ
i j
N
a k ,
N
b k ,
dΩ


N
a j ,
N
b i ,
dΩ


+
¸ ,

¸ _
+ =



ε
ε
x
ε
y
γ
xy
∂u
∂x
------
∂v
∂y
------
∂u
∂y
------
∂v
∂x
------ +

∂x
------ 0
0

∂y
------

∂y
------

∂x
------
u
v
and σ ,
σ
x
σ
y
τ
xy
= = = =
Finite Element Method Primer
378 Workbook of Applications in VectorSpace C++ Library
Chapter 4
The constitutive equation is
σ = D ε Eq. 4•165
In plane strain case, we can show that the fourth-order tensor D becomes a matrix as
Eq. 4•166
in plane stress case, D can be defined by replacing λ by λ, according to
Eq. 4•167
In engineering applications, the Young’s modulus, E, and Poisson’s ratio, ν, are often given instead of the Lamé
constants. They can be related as
Eq. 4•168
rewritten Eq. 4•105 for a = 0, 1, ..., (nen - 1), and i = 0, ..., (ndf - 1)
Eq. 4•169
where e
i
is the Euclidean basis vector. We can write
ε( ) = B
a
e
i
Eq. 4•170
Eq. 4•171
The element stiffness matrix of the B-matrix (strain-displacement matrix) formulation is
Eq. 4•172
D
λ 2µ + λ 0
λ λ 2µ + 0
0 0 µ
=
λ
2λµ
λ 2µ +
---------------- =
λ
νE
1 ν + ( ) 1 2ν – ( )
-------------------------------------- and µ ,
E
2 1 ν + ( )
-------------------- = =
u
e
h
ξ η , ( )
N
a
ξ η , ( )u
ˆ
e
ai
e
i
no sum on i ( ) ,

u
e
h
u
ˆ
e
ai
B
a
∂N
a
∂x
--------- 0
0
∂N
a
∂y
---------
∂N
a
∂y
---------
∂N
a
∂x
---------
= and B B
0
B
1
B
2
… B
n 1 –
= ,
k
e
ε δu ( )
T
σ u ( )dΩ


ε δu ( )
T
Dε u ( )dΩ


= =
III. B-matrix Formulation:
Eq. 4•173 k
e
pq
k
e
i ajb
ε δu ( )
T
Dε u ( )dΩ


e
i
T
B
a
T
DB
b
dΩe
j


= = =
Workbook of Applications in VectorSpace C++ Library 379
Two Dimensional Problems
In Eq. 4•173, the relation of indices {p, q} and {i, a, j, b} are defined in Eq. 4•163.
Consider an example of a cantilever beam
1
with Young’s modulus E = 30 x10
6
psi, ν = 0.25 subject to a
uniformly distributed shear stress τ = 150 psi at the end (see Figure 4•42). The shear stress at the end is τ
y
= -150
psi. For boundaries of a 4-node quadrilateral element, we use trapezoidal rule to compute the nodal load, because
the element boundary is linear. In the trapezoidal rule (Eq. 3•1 of Chapter 3 on page 166), the weighting for the
end-points of a line segment is {0.5, 0.5}. To element “0”, we add -75 psi to nodes “0”, and “5”, and for the ele-
ment “4”, we also add -75 psi to nodes “5”, and “10”. Adding the nodal loading on the two element together, this
yields nodal load specification of -75, -150, and -75 psi to nodes “0”, “5”, and “10”, respectively. Similarly, for a
9-nodes Lagrangian element, the boundary is quadratic, we use Simpson’s rule with weightings of {1/3, 4/3, 1/3}
to compute the three nodes on the boundary. This yields -50, -200, and -50 psi on nodes “0”, “5”, and “10”,
respectively. The analytical solution on the tip deflection is
Eq. 4•175
With the given parameters, this value is “-0.51875”.
We proceed to implement this problem in C++ with the aid of VectorSpace C++ Library and “fe.lib”. In most
finite element text, the B-matrix formulation is the carnonical formula provided. Therefore, we discuss the
implementation of the three formulations in reverse order.
1. p. 473 in Reddy, J.N. 1993, “ An introduction to the finite element method”, 2nd ed., McGraw-Hill, Inc., New York.
×
τ
y
· 150 psi
10 in.
2 in.
f
y,10
= -75
f
y,5
= -150
f
y,0
= -75
0 1 2 3
4
5 6 7 8 9
10 11 12 13 14
0 1 2 3
4 5 6 7
u
x,14
= 0
u
x,9
= 0, and
u
y,9
= 0
u
x,4
= 0
f
y,10
= -50
f
y,5
= -200
f
y,0
= -50
0 1 2 3
4
5
6 7 8
9
10 11 12 13 14
0
1
u
x,14
= 0
u
x,9
= 0, and
u
y,9
= 0
u
x,4
= 0
Figure 4•42 Discretization of eight 4-nodes quadrilateral elements or two 9-nodes
Lagrangian elements for a cantilever beam.
v
PL
3
3EI
--------- 1
3 1 ν + ( )
L
2
-------------------- + – =
Finite Element Method Primer
380 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Implementations for B-Matrix Formulation:
The Program Listing 4•15 implements Eq. 4•173. The Element_Formulation of “ElasticQ4” is
1 static const double a_ = E_ / (1-pow(v_,2)); // plane stress D matrix
2 static const double Dv[3][3] = {{a_, a_*v_, 0.0 },
4 {a_*v_, a_, 0.0 },
5 {0.0, 0.0, a_*(1-v_)/2.0} };
6 C0 D = MATRIX("int, int, const double*", 3, 3, Dv[0]);
7 ElasticQ4::ElasticQ4(int en, Global_Discretization& gd) : Element_Formulation(en, gd) {
8 Quadrature qp(2, 4); // 2-dimension, 2x2 integration points
9 H1 Z(2, (double*)0, qp), // Natrual Coordinates
10 N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE(
11 "int, int, Quadrature", 4/*nen*/, 2/*nsd*/, qp),
12 Zai, Eta;
13 Zai &= Z[0]; Eta &= Z[1];
14 N[0] = (1-Zai)*(1-Eta)/4; N[1] = (1+Zai)*(1-Eta)/4;
15 N[2] = (1+Zai)*(1+Eta)/4; N[3] = (1-Zai)*(1+Eta)/4;
16 H1 X = N*xl;
17 H0 Nx = d(N) * d(X).inverse();
18 J dv(d(X).det());
19 H0 w_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, nsd, Nx),
20 wx, wy, B;
21 wx &= w_x[0][0]; wy &= w_x[0][1]; // aliase submatrices; 1x2
22 B &= (~wx || C0(0.0)) & // dim B = {3x8}, where dim wx[i] = {1x4}
23 (C0(0.0) || ~wy ) &
24 (~wy || ~wx );
25 stiff &= ((~B) * (D * B)) | dv; // {8x3}*{3x3}*{3x8}={8x8}
26 }
Line 17 is the computation of the derivatives of the shape function “Nx” (see Figure 4•43). The “Nx” is then par-
titioned into submatrix “w_x”. The regular increment submatrices wx &= w_x[0][0] and wy &= w_x[0][1] are
Figure 4•43 Construction of B-matrix using one-by-one concatenation operation.
N
0,x
N
1,x
N
1,y
N
0,y
N
2,x
N
2,y
N
3,y
N
3,x
w_x
N
0,x
N
1,x
N
2,x
N
3,x
N
0,y
N
1,y
N
2,y
N
3,y
wx &= wy &=
N
0,x
N
1,x
N
2,x
N
3,x
N
0,y N
1,y
N
2,y
N
3,y
N
0,x
N
0,y
N
1,y
N
2,x
N
2,y
N
3,x
N
3,y
0
0 0
0
0
0
0
0
N
1,x
node
number
spatial dim.
B &= (~wx || C0(0.0)) &
(C0(0.0) || ~wy ) &
(~wy || ~wx );
w_x[0][0] w_x[0][1]
aliase submatrices B-matrix lay-out
Workbook of Applications in VectorSpace C++ Library 381
Two Dimensional Problems
#include "include\fe.h"
static const double L_ = 10.0; static const double c_ = 1.0; static const double h_e_ = L_/2.0;
static const double E_ = 30.0e6; static const double v_ = 0.25;
static const double lambda_ = v_*E_/((1+v_)*(1-2*v_));
static const double mu_ = E_/(2*(1+v_));
static const double lambda_bar = 2*lambda_*mu_/(lambda_+2*mu_);
EP::element_pattern EP::ep = EP::QUADRILATERALS_4_NODES;
Omega_h::Omega_h() {
double x[4][2] = {{0.0, 0.0}, {10.0, 0.0}, {10.0, 2.0}, {0.0, 2.0}}; int flag[4] = {1, 1, 1, 1};
block(this, 3, 5, 4, flag, x[0]);
}
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) { __initialization(df, omega_h);
the_gh_array[node_order(4)](0) = the_gh_array[node_order(9)](0) =
the_gh_array[node_order(9)](0)=the_gh_array[node_order(14)](0)=gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(0)](1) = the_gh_array[node_order(5)](1) =
the_gh_array[node_order(10)](1) = gh_on_Gamma_h::Neumann;
the_gh_array[node_order(0)][1] = the_gh_array[node_order(10)][1] = -75.0;
the_gh_array[node_order(5)][1] = -150.0;
}
class ElasticQ4 : public Element_Formulation { public:
ElasticQ4(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
ElasticQ4(int, Global_Discretization&);
};
Element_Formulation* ElasticQ4::make(int en, Global_Discretization& gd) {
return new ElasticQ4(en,gd);
}
static const double a_ = E_ / (1-pow(v_,2));
static const double Dv[3][3] = {{a_, a_*v_, 0.0}, {a_*v_, a_, 0.0 }, {0.0, 0.0, a_*(1-v_)/2.0} };
C0 D = MATRIX("int, int, const double*", 3, 3, Dv[0]);
ElasticQ4::ElasticQ4(int en, Global_Discretization& gd) : Element_Formulation(en, gd) {
Quadrature qp(2, 4);
H1 Z(2, (double*)0, qp), Zai, Eta,
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 4, 2, qp);
Zai &= Z[0]; Eta &= Z[1];
N[0] = (1-Zai)*(1-Eta)/4; N[1] = (1+Zai)*(1-Eta)/4;
N[2] = (1+Zai)*(1+Eta)/4; N[3] = (1-Zai)*(1+Eta)/4;
H1 X = N*xl;
H0 w_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, nsd, Nx), wx, wy, B;
wx &= w_x[0][0]; wy &= w_x[0][1];
B &= (~wx || C0(0.0)) &
(C0(0.0) || ~wy ) &
(~wy || ~wx );
stiff &= ((~B) * (D * B)) | dv;
}
Element_Formulation* Element_Formulation::type_list = 0;
Element_Type_Register element_type_register_instance;
static ElasticQ4 elasticq4_instance(element_type_register_instance);
int main() { int ndf = 2; Omega_h oh; gh_on_Gamma_h gh(ndf, oh); U_h uh(ndf, oh);
Global_Discretization gd(oh, gh, uh); Matrix_Representation mr(gd); mr.assembly();
C0 u = ((C0)(mr.rhs())) / ((C0)(mr.lhs()));
gd.u_h() = u; gd.u_h() = gd.gh_on_gamma_h();
cout << gd.u_h();
return 0;
}
Young’s modulus and Poisson ratio
plane stress λ modification
generate nodes and elements
B.C.
u
4
= u
9
= v
9
= u
14
= 0
τ
y0
= τ
y10
= -75, τ
y5
= -150
N
a
ξ η , ( )
1
4
--- 1 ξ
a
ξ + ( ) 1 η
a
η + ( ) =
∇N
∂N
∂ξ
-------
¸ ,
¸ _
∂x
∂ξ
------
¸ ,
¸ _
1 –
=
B
a
∂N
a
∂x
--------- 0
0
∂N
a
∂y
---------
∂N
a
∂y
---------
∂N
a
∂x
---------
=
k
e
e
i
T
B
a
T
DB
b
dΩe
j


=
Listing 4•15 Plane elastiticity (project workspace file “fe.dsw”, project “2d_beam” with Macro definition
“__TEST_B_MATRIX_CONCATENATE_EXPRESSION_SUBMATRIX” set at compile time).
Finite Element Method Primer
382 Workbook of Applications in VectorSpace C++ Library
Chapter 4
also shown. The B-matrix, according to Eq. 4•171, is defined with one-by-one column-wise concatenation oper-
ation “H0::operator || (const H0&)”. When the argument of the concatenation operation is of type C0, it will be
promote to H0 type object before concatenation occurred. Line 25 is the element stiffness matrix definition of
the Eq. 4•173.
A complete parallel algorithm, without the use of the one-by-one concatenation operation, results in C++
statements closer to linear algebraic expression with basis
1 H0 w_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, nsd, Nx),
2 wx, wy;
3 wx &= w_x[0][0]; wy &= w_x[0][1];
4 H0 zero = ~wx; zero = 0.0;
5 C0 e3(3), e(ndf), E(nen),
6 U = (e3%e)*(~E);
7 H0 B =+((~wx) * U[0][0] + zero * U[0][1] +
8 zero * U[1][0] + (~wy) * U[1][1] +
9 (~wy) * U[2][0] + (~wx) * U[2][1]) ;
10 stiff &= ((~B) * (D * B)) | dv;
Line 4 takes the size and type of the transpose of “wx”, then re-assigns its values to zero. Line 7 uses unary pos-
itive operator “+” to convert a Integrable_Nominal_Submatrix (of object type H0) into a plain Integrable_Matrix
(also of object type H0). We note that the expression “U[2][1]” can be written as “(e3[2] % e[1]) * (~E)” without
having to define the additional symbol “U = (e3%e)*(~E)”. One needs to set both macro definitions of
“__TEST_B_MATRIX_CONCATENATE_EXPRESSION_SUBMATRIX” and “__TEST_BASIS” for this
implementation at compile time
The semantics in the construction of B-matrix in the above is a bottom-up process. We first define the com-
ponents of the B-matrix than built the B-matrix with these pre-constructed components. The semantics of the
program code can be constructed in a reversed order; i.e., top-down process. We may want to construct the B-
matrix first, giving its size and initialized with default values (“0.0”). Then, we can assign each components of
the B-matrix with its intended values.
1 H0 w_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, nsd, Nx),
2 B = INTEGRABLE_MATRIX("int, int, Quadrature", 3, nsd*nen, qp),
3 epsilon = INTEGRABLE_SUBMATRIX("int, int, H0&", 3, nsd, B),
4 wx, wy; // aliases of w_x components
5 wx &= w_x[0][0]; wy &= w_x[0][1];
6 epsilon[0][0] = ~wx; epsilon[0][1] = 0.0; // ε
x
= ∂u/∂x
7 epsilon[1][0] = 0.0; epsilon[1][1] = ~wy; // ε
y
= ∂v/∂y
8 epsilon[2][0] = ~wy; epsilon[2][1] = ~wx; // γ
xy
= ∂u/∂y + ∂v/∂x
9 stiff &= ((~B) * (D * B)) | dv;
The B-matrix is constructed first, then, its components {ε
x
, ε
y
, γ
xy
}
T
are assign according to the definition in the
first part of Eq. 4•164 and Eq. 4•170, where the strain “epsilon” is a submatrix referring to “B” matrix. For this
implementation the same project “2d_beam” in project workspace file “fe.dsw” can be used with only the macro
definition “__TEST_B_MATRIX_CONCATENATE_EXPRESSION_TOP_DOWN” set.
Workbook of Applications in VectorSpace C++ Library 383
Two Dimensional Problems
The above two methods of programming depend heavily on the submatrix facility in the VectorSpace C++
Library. This dependency can be removed, if we flatten the submatrix into plain matrix with concatenation oper-
ations as (by setting only macro definition “__TEST_B_MATRIX_CONCATENATE_EXPRESSION” in the
same project)
1 H0 Nx0, Nx1, Nx2, Nx3; // aliases
2 Nx0 &= Nx[0]; Nx1 &= Nx[1]; Nx2 &= Nx[2]; Nx3 &= Nx[3];
3 H0 B = (Nx0[0] | C0(0.0) | Nx1[0] | C0(0.0) | Nx2[0] | C0(0.0) | Nx3[0] | C0(0.0) ) &
4 (C0(0.0) | Nx0[1] | C0(0.0) | Nx1[1] | C0(0.0) | Nx2[1] | C0(0.0) | Nx3[1] ) &
5 (Nx0[1] | Nx0[0] | Nx1[1] | Nx1[0] | Nx2[1] | Nx2[0] | Nx3[1] | Nx3[0] );
6 stiff &= ((~B) * (D * B)) | dv;
We see that this implementation takes direct image of the right-hand-side block in the Figure 4•43. In the above
code, no submatrix facility is used only the concatenate operator “|” is used to built the B-matrix from ground-up.
Comparing the bottom-up with the top-down algorithms, the only difference is the semantics. In the last algo-
rithm, we have flatten out the submatrix into simple matrix. In doing so, we can avoid using the requirement of
submatrix features supported by the VectorSpace C++ Library. We may want to optimize the rapid-proto-typing
code by eliminating the features supported in VectorSpace C++ Library step-by-step, such that the overhead
caused by the use of VectorSpace C++ Library can be alleviated.
A even more Fortran-like equivalent implementation is as the followings
1
(set the macro definition to noth-
ing)
1 H0 k(8, 8, (double*)0, qp), DB(3, nsd, (double*)0, qp), B1, B2;
2 for(int b = 0; b < nen; b++) {
3 B1 &= Nx[b][0]; B2 &= Nx[b][1];
4 DB[0][0] = Dv[0][0]*B1; DB[0][1] = Dv[0][1]*B2; // D*B takes care of zeros
5 DB[1][0] = Dv[0][1]*B1; DB[1][1] = Dv[1][1]*B2;
6 DB[2][0] = Dv[2][2]*B2; DB[2][1] = Dv[2][2]*B1;
7 for(int a = 0; a <= b; a++) {
8 B1 &= Nx[a][0];
9 B2 &= Nx[a][1];
9 k[2*a ][2*b ] = B1*DB[0][0] + B2*DB[2][0]; // B
T
* DB takes care of zeros
10 k[2*a ][2*b+1] = B1*DB[0][1] + B2*DB[2][1];
11 k[2*a+1 ][2*b ] = B2*DB[1][0] + B1*DB[2][0];
12 k[2*a+1 ][2*b+1] = B2*DB[1][1] + B1*DB[2][1];
13 }
14 }
15 for(int b = 0; b < nen; b++) // determined by minor symmetry
16 for(int a = b+1; a < nen; a++) {
17 k[2*a ][2*b ] = k[2*b ][2*a ];
18 k[2*a ][2*b+1 ] = k[2*b+1 ][2*a ];
1. p. 153 in Thomas J.R. Hughes, 1987, “ The finite element method: Linear and dynamic finite element analysis.”, Prentice-
Hall, Englewood Cliffs, New Jersey.
Finite Element Method Primer
384 Workbook of Applications in VectorSpace C++ Library
Chapter 4
19 k[2*a+1][2*b ] = k[2*b ][2*a+1 ];
20 k[2*a+1][2*b+1 ] = k[2*b+1 ][2*a+1 ];
21 }
22 stiff &= k | dv;
In lines 2-14, provision is taken to eliminate the multiplication with “0” components in B
T
DB. Only the “nodal
submatrices”—k
e
ab
in the diagonal and upper triangular matrix of k
e
is computed. The lower triangular part
matrix is then determined by symmetry with k
e
ab
= (k
e
ba
)
T
(lines 15-21). We recognize that this is the idiom of
using the low-level language expression with indices in accessing the submatrices of the matrix k
e
as “k[ndf a +
i][ndf b + j]”. By this way, we may avoid using the submatrix facility in VectorSpace C++ Library entirely. Cer-
tainly the optimized low-level code is much longer, less readable, and harder to maintain for programmers.
Nonetheless, this last version can be easily optimized even more aggressively in plain C language without using
the VectorSpace C++ Library at all. The last step is to have an numerical integration at the most outer loop where
we evaluate all values at Gaussian quadrature points and multiply these values with their corresponding weights.
Implementations for Indicial Notation Formulation:
Recall Eq. 4•161
The integrand of the nodal submatrices k
ab
(ndf ndf submatrices) has the first term(the volumetric part) as
Eq. 4•176
Note that λ may replace λ for the plane stress case in Eq. 4•167. The rest of the integrands of Eq. 4•161 is its
deviatoric part
=
= Eq. 4•177
Eq. 4•176 and Eq. 4•177 are implemented as (by setting, at compile time, the macro definition of
“__TEST_INDICIAL_NOTATION_FORMULATION” )
k
e
i ajb
λ N
a i ,
N
b j ,
dΩ


µ δ
i j
N
a k ,
N
b k ,
dΩ


N
a j ,
N
b i ,
dΩ


+
¸ ,

¸ _
+ =
×
λ N
a i ,
N
b j ,
( ) λ
∂N
a
∂x
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂y
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂x
---------
∂N
b
∂y
---------
¸ ,
¸ _
∂N
a
∂y
---------
∂N
b
∂y
---------
¸ ,
¸ _
=
µ δ
i j
N
a k ,
N
b k ,
( ) N
a j ,
N
b i ,
( ) + ( )
µ
∂N
a
∂x
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂y
---------
∂N
b
∂y
---------
¸ ,
¸ _
+ 0
0
∂N
a
∂x
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂y
---------
∂N
b
∂y
---------
¸ ,
¸ _
+
µ
∂N
a
∂x
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂y
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂x
---------
∂N
b
∂y
---------
¸ ,
¸ _
∂N
a
∂y
---------
∂N
b
∂y
---------
¸ ,
¸ _
+
µ
2
∂N
a
∂x
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂y
---------
∂N
b
∂y
---------
¸ ,
¸ _
+
∂N
a
∂y
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂x
---------
∂N
b
∂y
---------
¸ ,
¸ _
∂N
a
∂x
---------
∂N
b
∂x
---------
¸ ,
¸ _
2
∂N
a
∂y
---------
∂N
b
∂y
---------
¸ ,
¸ _
+
Workbook of Applications in VectorSpace C++ Library 385
Two Dimensional Problems
1 C0 e(ndf), E(nen), U = (e%e)*(E%E);
2 H0 w_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, nsd, nx), wx, wy;
3 wx &= w_x[0][0]; wy &= w_x[0][1]; //
4 C0 stiff_vol = lambda_bar* //
5 ( //
6 +( wx*~wx*U[0][0]+wx*~wy*U[0][1]+ //
7 wy*~wx*U[1][0]+wy*~wy*U[1][1] ) //
8 | d_v);
11 C0 stiff_dev = mu_*
12 ( //
13 +( (2*wx*~wx+wy*~wy)*((e[0]%e[0])*(E%E))+//
14 (wy*~wx) *((e[0]%e[1])*(E%E))+ //
15 (wx*~wy) *((e[1]%e[0])*(E%E))+ //
16 (wx*~wx+2*wy*~wy)*((e[1]%e[1])*(E%E))//
17 )
18 | dv);
19 stiff &= stiff_vol + stiff_dev;
Line 4-8 implements the integrand of the volumetric element stiffness by Eq. 4•176 and line 11-18 implements
the integrand of the deviatoric element stiffness by Eq. 4•177. Note that the unary positive operator in front of
both line 6 and line 13 are conversion operation to convert an Integrable_Nominal_Submatrix (of object type
H0) into an Integrable_Matrix (of type H0). An Integrable_Submatrix version of this implementation will be
1 H0 vol = INTEGRABLE_MATRIX("int, int, Quadrature", nsd*nen, nsd*nen, qp),
2 vol_sub = INTEGRABLE_SUBMATRIX("int, int, H0&", nsd, nsd, vol);
3 vol_sub[0][0] = wx*~wx; vol_sub[0][1] = wx*~wy;
4 vol_sub[1][0] = wy*~wx; vol_sub[1][1] = wy*~wy;
5 C0 stiff_vol = lambda_bar * (vol | dv);
6 H0 dev = INTEGRABLE_MATRIX("int, int, Quadrature", nsd*nen, nsd*nen, qp),
7 dev_sub = INTEGRABLE_SUBMATRIX("int, int, H0&", nsd, nsd, dev);
8 dev_sub[0][0] = 2*wx*~wx+wy*~wy; dev_sub[0][1] = wy*~wx;
9 dev_sub[1][0] = wx*~wy; dev_sub[1][1] = 2*wy*~wy+wx*~wx;
10 C0 stiff_dev = mu_ * (dev | dv);
11 stiff &= stiff_vol + stiff_dev;
The same implementation with one-by-one concatenation operations “||” and “&&” will be
1 C0 stiff_dev = mu_ *( ( ((2*Wx*~Wx+Wy*~Wy) || (Wy*~Wx) ) &&
2 ((Wx*~Wy) || (2*Wy*~Wy+Wx*~Wx))
3 ) | dv);
4 C0 stiff_vol = lambda_bar *( ( ((Wx*~Wx) || (Wx*~Wy)) &&
5 ((Wy*~Wx) || (Wy*~Wy))
6 ) | dv);
7 stiff &= stiff_vol + stiff_dev;
λ
∂N
a
∂x
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂y
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂x
---------
∂N
b
∂y
---------
¸ ,
¸ _
∂N
a
∂y
---------
∂N
b
∂y
---------
¸ ,
¸ _
µ
2
∂N
a
∂x
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂y
---------
∂N
b
∂y
---------
¸ ,
¸ _
+
∂N
a
∂y
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂x
---------
∂N
b
∂y
---------
¸ ,
¸ _
∂N
a
∂x
---------
∂N
b
∂x
---------
¸ ,
¸ _
2
∂N
a
∂y
---------
∂N
b
∂y
---------
¸ ,
¸ _
+
Finite Element Method Primer
386 Workbook of Applications in VectorSpace C++ Library
Chapter 4
The flatten-out coding using concatenation operations “|” and “&” is not recommended, since the 8 8 stiff-
ness matrix is just too much for either write it all out, or be read easily. An aggressively optimized counterpart
using less VectorSpace C++ library features is shown in the followings
1
1 double c1 = lambda_bar + mu_, c2 = mu_, c3 = lambda_bar; // λ replaces λ for plane stress
2 H0 k(nen*ndf, nen*ndf, (double*)0, qp);
3 for(int b = 0; b < nen; b++) // upper triangular nodal submatrices +
4 for(int j = 0; j < ndf; j++) // diagonal nodal submatrices only
5 for(int a = 0; a <= b; a++) //
6 for(int i = 0; i < ndf; i++) //
7 if( (a != b) || (a == b && i <= j) ) // k
e(temp)
=
8 k[i+a*ndf][j+b*ndf] = Nx[a][i]*Nx[b][j]; //
9 for(int i = 1; i < nen*ndf; i++) //
10 for(int j = 0; j < i; j++) //
11 k[i][j] = k[j][i]; // get lower triangualr part by symmetry
12 C0 K = k | dv; // k
e
13 for(int b = 0; b < nen; b++) //only the upper triangular of nodal submatrices—k
e
ab
14 for(int a = 0; a <= b; a++) {
15 C0 temp = 0.0;
16 for(int k = 0; k < ndf; k++)
17 temp += K[k+a*ndf][k+b*ndf];
18 for(int j = 0; j < ndf; j++)
19 for(int i = 0; i <= j; i++) {
20 if(i == j) // diagonal components of nodal submatrices—k
e
iaib
21 K[i+a*ndf][i+b*ndf] = c1*K[i+a*ndf][i+b*ndf]+ c2*temp;
22 else if(a == b) // off-diagonal components of diagonal nodal submat-
23 K[i+a*ndf][j+a*ndf] *= c1; // rices—k
e
iaja
(those in upper triangular of k
e
; i<j)
24 else { // off-diagonals components
25 double Kij = K[i+a*ndf][j+b*ndf], // of off-diagonal nodal submatrices—k
e
iajb
(a b)
26 Kji = K[j+a*ndf][i+b*ndf]; //
27 K[i+a*ndf][j+b*ndf] = c3*Kij + c2*Kji;//
28 K[j+a*ndf][i+b*ndf] = c3*Kji + c2*Kij;//
29 } //
30 }
31 }
32 for(int i = 1; i < nen*ndf; i++) // get lower triangular part of k
e
by symmetry
33 for(int j = 0; j < i; j++) //
34 K[i][j] = K[j][i];
35 stiff &= K;
1. p. 155 in Thomas J.R. Hughes, 1987, “ The finite element method: Linear and dynamic finite element analysis.”, Prentice-
Hall, Englewood Cliffs, New Jersey.
×
∂N
a
∂x
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂y
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂x
---------
∂N
b
∂y
---------
¸ ,
¸ _
∂N
a
∂y
---------
∂N
b
∂y
---------
¸ ,
¸ _
dΩ



λ
∂N
a
∂x
---------
∂N
b
∂y
---------
¸ ,
¸ _
µ
∂N
a
∂y
---------
∂N
b
∂x
---------
¸ ,
¸ _
+
¸ ,
¸ _
dΩ


λ
∂N
a
∂y
---------
∂N
b
∂x
---------
¸ ,
¸ _
µ
∂N
a
∂x
---------
∂N
b
∂y
---------
¸ ,
¸ _
+
¸ ,
¸ _
dΩ


Workbook of Applications in VectorSpace C++ Library 387
Two Dimensional Problems
All integration operations are done with between lines 3-12. Data of the derivatives of shape function are stored
in matrix k
e
temporarily as
k
e
ab

(temporary)
= Eq. 4•178
Then, k
e
is overwritten by the rest of the codes. Lines 13-34 will have no integrable objects involved. In both of
these two parts, the symmetry consideration is taken, and only the components of the diagonal nodal submatrix
and upper-triangular nodal submatrices belonging to the upper triangular part of k
e
are calculated, to reduce the
number of calculation. Firstly, line 17 calculates the following quantity and store in the variable “temp”
Eq. 4•179
Lines 20-21 gets diagonal components of the nodal submatrices according to
Eq. 4•180
where the null symbol “ ” denotes the corresponding components in the matrix are not calculated. Lines 22-24,
and 25-30 get the off-diagonal components of nodal submatrices
Eq. 4•181
Special care is taken in lines 22-23, when the nodal submatrices are diagonal nodal submatrices. In the case the
node number index is “a”, we have N
a,x
N
a,y
= N
a,y
N
a,x
. That is the off-diagonal components in the diagonals
nodal submatrices in Eq. 4•181 is reduced to
Eq. 4•182
For these diagonal nodal submatrices the off-diagonal components calculation is therefore further simplified to
lines 22-23. Notice that components in lower-left corner of Eq. 4•182 are not calculated, because these compo-
∂N
a
∂x
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂y
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂x
---------
∂N
b
∂y
---------
¸ ,
¸ _
∂N
a
∂y
---------
∂N
b
∂y
---------
¸ ,
¸ _
dΩ


∂N
a
∂x
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂y
---------
∂N
b
∂y
---------
¸ ,
¸ _
+ dΩ


λ 2µ + ( )
∂N
a
∂x
---------
∂N
b
∂x
---------
¸ ,
¸ _
µ
∂N
a
∂y
---------
∂N
b
∂y
---------
¸ ,
¸ _
+ ∅
∅ λ 2µ + ( )
∂N
a
∂y
---------
∂N
b
∂y
---------
¸ ,
¸ _
µ
∂N
a
∂x
---------
∂N
b
∂x
---------
¸ ,
¸ _
+
dΩ



∅ λ
∂N
a
∂x
---------
∂N
b
∂y
---------
¸ ,
¸ _
µ
∂N
a
∂y
---------
∂N
b
∂x
---------
¸ ,
¸ _
+
λ
∂N
a
∂y
---------
∂N
b
∂x
---------
¸ ,
¸ _
µ
∂N
a
∂x
---------
∂N
b
∂y
---------
¸ ,
¸ _
+ ∅
dΩ


λ µ + ( )

∂N
a
∂x
---------
∂N
a
∂y
---------
∅ ∅
dΩ


Finite Element Method Primer
388 Workbook of Applications in VectorSpace C++ Library
Chapter 4
nents belong to the lower triangular part of k
e
, and they can be obtained by symmetry as in lines 32-34. Lines 24-
29 take care of the rest by Eq. 4•181; i.e., the off-diagonal components in off-diagonal nodal submatrices, they
all lie in the upper triangular part of k
e
. This implementation is probably the most efficient of all. However, the
code is quite abstruse without study its comments and explanations carefully. Lots of programming merits are all
compromised in the name of efficiency.
Implementation for Coordinate-Free Tensorial Formulation:
Now we turn away from the goal of optimization for efficiency completely to the goal of obtaining a most
physically and mathematically comprehensive implementation. For research scientists and engineers, it is most
likely to have a formula available that is derived from physical principles such as the development of elasticity
in the beginning of this section. The finite element formula may not be available. VectorSpace C++ Library
together with object-oriented features in C++ language may serve as the rapid proto-typing tools. A high-level
code can be quickly implemented with VectorSpace C++ Library because it provides capability of making com-
puter code very close to its mathematical counterparts. If it turns out further optimization is necessary for either
saving computation time or memory space, the numerical results of the high-level prototype code can be used to
debug the optimized code which is often quite un-readable and error-prone.
First we recall Eq. 4•155 for , we have the inner product defined by a symmetrical bilinear form
: Eq. 4•183
The inner product gives a scalar. The implementation for the coordinate free tensorial formulation will be based
on Eq. 4•156 which is
: Eq. 4•184
where , and superscripts and subscripts {a, b} are the element node numbers. The element variables,
e.g., in 2-D elasticity for bilinear 4-nodes element, are arranged in the order of u = {u
0
, v
0
, u
1
, v
1
, u
2
, v
2
, u
3
, v
3
}
T
.
The variable vector u has the size of (ndf nen) = 2 4 =8. Therefore, we identify that the finite element space—
V
h
(Ω
e
) has its inner product operation producing an element stiffness matrix, k
e
,

of size (ndf nen) (ndf nen)
= 8 8. We also observed that the differential operators “div”, “def”, and the double contraction “:” on the finite
element space, V
h
(Ω
e
), all need to be defined. The closest thing to the finite element space, V
h
(Ω
e
), in Vector-
Space C++ Library is the type H1 which is an integrable type differentiable up to the first order. However, H1 is
certainly not a finite element space. The inner product of objects defined by H1 will not generate a
(ndf nen) (ndf nen) element stiffness matrix, neither does it has the knowledge of “div”, “def” or “:” opera-
tors. We may implement a customized, not intended for code reuse, class “H1_h” in ad hoc manner for the finite
element space—V
h
(Ω
e
) as
1 class H0_h; // forward declaration
2 class H1_h { // finite element space—V
h
(Ω
e
), where
V v H
1
∈ { } ≡
a v v , ( ) λdiv v • div v 2µ def v ( ) + [


= def v)]dΩ
k
e
a N
a
N
b
, ( ) λ div N
a
div • N
b
( ) 2µ def N
a
( + [


= = def N
b
)]dΩ
N
a
V
h

× ×
× × ×
×
× × ×
V v H
1
∈ { } ≡
Workbook of Applications in VectorSpace C++ Library 389
Two Dimensional Problems
3 H1 n, x;
4 public:
5 H1_h(H1&, H1&);
6 H0_h div_();
7 H0_h grad_();
8 H0_h grad_t_();
9 H0_h def_();
10 };
11 class H0_h : public H0 { // return type for the differential operators div, grad, def
12 public:
13 H0_h(const H0& a) : H0(a) {}
14 H0 operator ^(const H0_h&); // double contraction “:”
15 };
16 H0_h div(H1_h& n) { return n.div_(); }
17 H0_h grad(H1_h& n) { return n.grad_(); }
18 H0_h grad_t(H1_h& n) { return n.grad_t_(); }
19 H0_h def(H1_h& n) { return n.def_(); }
20 H1_h::H1_h(H1& N, H1& X) { n = N; x = X; }
The differential operators “div” and “def” are applied to the finite element space—V
h
(Ω
e
) which can be imple-
mented as an abstract data type “H1_h”. The return values of these differential operators are of yet another
abstract data type “H0_h”. In the terminology of object-oriented analysis, H0_h “IS-A” H0 type. The “IS-A”
relationship between H0_h and H0 is manifested by the definition of class H0_h as publicly derived from class H0
(line 11). We can view class “H0_h” as an extension of class H0 to define the double contraction operation “:”.
The double contraction operator is defined as a public member binary operator “H0_h::operator ^ (const
H0_h&)” (line 14). We emphasize that with the public derived relationship, class H0_h inherits all the public
interfaces and implementations of class H0. Moreover, we design to have H1_h used in the element formulation
as close to the mathematical expression as possible. Lines 16-20 are auxiliary free functions defined to provide
better expressiveness, such that, we may write in element formulation as simple as
1 H1_h N_(N, X);
2 C0 K_vol = lambda_bar*(((~div(N_))*div(N_)) | dv), //
3 K_dev = (2*mu_) * ( (def(N_) ^ def(N_)) | dv); // :
4 stiff &= K_vol + K_dev;
which is almost an exact translation of high-flown mathematical expression of Eq. 4•184. The constructor of
class H1_h take two arguments of type H1. The first argument is the shape functions—“N”, and the second argu-
ment is the physical coordinates— “X”. The derivatives of the shape function can be computed from these two
objects as
H0 Nx = d(N) * d(X).inverse();
These two objects have been defined earlier in the element formulation. Now we get to the definition of the
divergence operator “div” according to Eq. 4•144
λ div N
a
div • N
b
( )dΩ


2µ def N
a
(


def N
b
)dΩ
Finite Element Method Primer
390 Workbook of Applications in VectorSpace C++ Library
Chapter 4
div u = u
i,i
= Eq. 4•185
Or in the form of the nodal subvector (row-wise) for the finite element space—V
h
(Ω
e
) as
Eq. 4•186
of size 1 8. Eq. 4•186 can be implemented as
1 H0_h H1_h::div_() {
2 H0 Nx = n.d() * x.d().inverse();
3 H0 w_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, 2, Nx);
4 H0 wx = (+w_x[0][0]), wy = (+w_x[0][1]);
5 C0 u = BASIS("int", 2), E = BASIS("int", 4);
6 H0 ret_val = wx(0)*(u[0]*E) + wy(0)*(u[1]*E); // Eq. 4•186
7 return ~(+ret_val);
8 }
This divergence operation will return an Integrable_Matrix of size 1 8. Therefore, the inner product,
“ ”, not with respect to node number, will return an element stiffness matrix object (an
Integrable_Matrix of type H0) of size 8 8. The gradient operator “grad” is defined (also in Eq. 4•144)
Eq. 4•187
Notice that we arrange “u”, “v” in row-wise order to be compatible with the order of the variable vector in ele-
ment formulation. This special ordering makes the gradient tensor in Eq. 4•187 as the transpose of the ordinary
mathematical definition on grad u. The nodal submatrices of the return value of “grad” operator are
Eq. 4•188
Eq. 4•188, for “grad” operator on V
h
, should return a 2 8 Integrable_Matrix, and it is implemented as
1 H0_h H1_h::grad_() {
2 H0 Nx = n.d() * x.d().inverse();
3 H0 w_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, 2, Nx), wx, wy;
∂u
∂x
------
∂v
∂y
------ +
∂N
a
∂x
---------
∂N
a
∂y
---------
×
×
div div •
×
grad u ∇ u ⊗ u
i j ,
∂u
∂x
------
∂v
∂x
------
∂u
∂y
------
∂v
∂y
------
= = =
∂N
a
∂x
---------
∂N
a
∂x
---------
∂N
a
∂y
---------
∂N
a
∂y
---------
×
Workbook of Applications in VectorSpace C++ Library 391
Two Dimensional Problems
4 wx &= ~(+w_x[0][0]); wy &= ~(+w_x[0][1]);
5 C0 eu = BASIS("int", 4),
6 e = BASIS("int", 2),
7 E1 = BASIS("int", 1),
8 E2 = BASIS("int", 4),
9 a = (e%eu)*(E1%E2);
10 H0 ret_val = wx*a[0][0] + wx*a[0][3] + // Eq. 4•188
11 wy*a[1][0] + wy*a[1][3];
12 return ret_val;
13 }
The operator “grad
T
” is defined independently from “grad” for the finite element space—V
h
(Ω
e
), which can not
be obtained by the transpose of the resulting matrix of “grad”. This is because that the transpose operation on
grad is with respect to its spatial derivatives only not with respect to element node number index—a. Both differ-
ential operators “grad” and “grad
T
” have return value, with the size of 2 8, of type H0_h which is derive from
Integrable_Matrix of type H0. The operator grad
T
has its nodal submatrices
Eq. 4•189
which is implemented as
1 H0_h H1_h::grad_t_() {
2 H0 Nx = n.d() * x.d().inverse();
3 H0 w_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, 2, Nx), wx, wy;
4 wx &= ~(+w_x[0][0]); wy &= ~(+w_x[0][1]);
5 C0 eu = BASIS("int", 4),
6 e = BASIS("int", 2),
7 E1 = BASIS("int", 1),
8 E2 = BASIS("int", 4),
9 a = (e%eu)*(E1%E2);
10 H0 ret_val = wx*a[0][0] + wy*a[0][2] + //Eq. 4•189
11 wx*a[1][1] + wy*a[1][3];
12 return ret_val;
13 }
The operator “def”, for the finite element space—V
h
(Ω
e
), is defined according to Eq. 4•148
Eq. 4•190
With both “grad” and “grad
T
” already defined, “def” can be implemented simply as
×
∂N
a
∂x
---------
∂N
a
∂y
---------
∂N
a
∂x
---------
∂N
a
∂y
---------
def u
1
2
--- grad u grad u ( )
T
+ ( ) ≡
Finite Element Method Primer
392 Workbook of Applications in VectorSpace C++ Library
Chapter 4
1 H0_h H1_h::def_() {
2 H0 ret_val = (+(grad_t(*this) + grad(*this))/2); // Eq. 4•190
3 return ret_val;
4 }
The differential operator def also return a 2 8 H0_h type object. The double contraction is defined in Eq. 4•154
def u : def u = tr((def u)
T
def u) Eq. 4•191
The implementation of the binary operator “^” as double contraction operator is completely ad hoc. Under the
discretion of the programmer, it has assumed that the two operands of the binary operator are the return values of
the def operator. The return value has the size of 8 8. This is evident from the left-hand-side of Eq. 4•191.
1 H0 H0_h::operator^(const H0_h& a) {
2 H0 ret_val(8, 8, (double*)0, a.quadrature_point());
3 H0 ret_sub = INTEGRABLE_SUBMATRIX("int, int, H0&", 2, 2, ret_val);
4 H0 def_w = INTEGRABLE_SUBMATRIX("int, int, H0&", 2, 4, a);
5 for(int a = 0; a < 4; a++)
6 for(int b = 0; b < 4; b++) {
7 H0 def_wa = +def_w(0,a), def_wb = +def_w(0,b);
8 H0 def_def = (~def_wa)*def_wb; // (def u)
T
a
(def u)
b
9 H0 dds = INTEGRABLE_SUBMATRIX("int, int, H0&", 2, 2, def_def);
10 ret_sub(a,b) = +(dds(0,0)+dds(1,1)); // trace of “(def u)
T
a
(def u)
b

11 }
12 return ret_val;
13 }
Line 3 is the nodal submatrices that we calculated according to Eq. 4•191, and upon which we loop over all
nodes. This implementation can be activated by setting, at compile time, the macro definition
“__TEST_COORDINATE_FREE_TENSORIAL_FORMULATION” for the same project “2d_beam” in project
workspace file “fe.dsw”.
The extension of H1 class in VectorSpace C++ Library to finite element space—V
h
(Ω
e
) as H1_h class in the
above is an example of the so-call programming by specification in the object-oriented method.
×
×
Workbook of Applications in VectorSpace C++ Library 393
Two Dimensional Problems
Post-Processing—Nodal Reactions
The reaction on each node can be computed after the displacement is known, according to “K
ij
u
j
”. The actual
computation is done at the constructor of class “ElasticQ4”, and is invoked in the main() program as the follow-
ings.
1 ElasticQ4::ElasticQ4(int en, Global_Discretization& gd) : Element_Formulation(en, gd) {
2 ...
3 if(Matrix_Representation::Assembly_Switch == Matrix_Representation::REACTION) {
4 stiff &= K | dv;
5 the_element_nodal_value &= stiff * (ul+gl);
6 } else stiff &=K | dv;
7 }
8 int main() {
9 ...
10 Matrix_Representation::Assembly_Switch = Matrix_Representation::REACTION;
11 mr.assembly(FALSE);
12 cout << "Reaction:" << endl << (mr.global_nodal_value()) << endl;
13 }
The class “Matrix_Representation” has the member function “assembly()” which maps
“the_element_nodal_value” to the “mr.global_nodal_value()” used in the “main()” function. The reaction is not
computed in the present example of project “beam_2d”. The next project “patch_test”, in the next section, will
compute this quantity.
Post-Processing—Stresses on Gauss Points
After the displacement solution is obtained, stresses can be computed from stress-strain relation, e.g., in B-
matrix form of Eq. 4•173, the stress is
Eq. 4•192
After the nodal displacements, , are obtained, we can loop over each element to calculate the stresses on each
Gaussian integration point as,
1 HeatQ4::HeatQ4(int en, Global_Discretization& gd) : Element_Formulation(en, gd) {
2 ...
3 if(Matrix_Representation::Assembly_Switch == Matrix_Representation::STRESS) {
4 H0 Sigma = INTEGRABLE_VECTOR("int, Quadrature", 3, qp);
5 Sigma = 0.0;
6 for(int i = 0; i < nen; i++) {
7 B1 &= Nx[i][0]; B2 &= Nx[i][1];
8 DB[0][0] = Dv[0][0]*B1; DB[0][1] = Dv[0][1]*B2;
9 DB[1][0] = Dv[0][1]*B1; DB[1][1] = Dv[1][1]*B2;
10 DB[2][0] = Dv[2][2]*B2; DB[2][1] = Dv[2][2]*B1;
σ
e
h
DBu
ˆ
e
a
=
u
ˆ
e
a
Finite Element Method Primer
394 Workbook of Applications in VectorSpace C++ Library
Chapter 4
11 Sigma += DB(0)*(ul[i*ndf]+gl[i*ndf]) + DB(1)*(ul[i*ndf+1]+gl[i*ndf+1]); //
12 }
13 int nqp = qp.no_of_quadrature_point();
14 for(int i = 0; i < nqp; i++) {
15 cout << setw(9) << en
16 << setw(14) << ((H0)X[0]).quadrature_point_value(i)
18 << setw(14) << ((H0)X[1]).quadrature_point_value(i)
19 << setw(14) << (Sigma[0].quadrature_point_value(i))
20 << setw(14) << (Sigma[1].quadrature_point_value(i))
21 << setw(14) << (Sigma[2].quadrature_point_value(i)) << endl;
22 }
23 } else stiff &= ...
24 }
25 int main() {
26 ...
27 Matrix_Representation::Assembly_Switch = Matrix_Representation::STRESS;
28 cout << << "gauss point stresses: " << endl;
29 cout.setf(ios::left,ios::adjustfield);
30 cout << setw(9) << " elem #, " << setw(14) << "x-coor.," << setw(14) << "y-coor.,"
31 << setw(14) << "sigma-11," << setw(14) << "sigma-22," << setw(14) << "sigma-12" << endl;
32 mr.assembly(FALSE);
33 }
Post-Processing—Stress Nodal Projection Method
Stress projection for nodal stress, , is similar to the heat flux projection on node , the element stresses
are interpolated from the nodal stresses as
Eq. 4•193
The weighted-residual statement with Galerkin weighting that w = N
a

Eq. 4•194
Substituting Eq. 4•192 and Eq. 4•117 into Eq. 4•118, we have
Eq. 4•195
The nodal stresses can be solved for from Eq. 4•195. Following the same procedure for the heat flux projec-
tion on node, in the previous section, Eq. 4•195 can be approximated similarly for the stress nodal projection by
implementing the following codes.
σ
e
h
DBu
ˆ
e
a
=
σ
ˆ
e
a
q
ˆ
e
a
σ
e
h
N
a
ξ η , ( )σ
ˆ
e
a

N
a
σ
e
h
σ
e
h
– ( ) Ω d


0 =
N
a
N
b
Ω d


¸ ,

¸ _
σ
ˆ
e
b
N
a
DBu
ˆ
e
a
( ) ( ) Ω d


=
σ
ˆ
e
a
Workbook of Applications in VectorSpace C++ Library 395
Two Dimensional Problems
1 ElasticQ4::ElasticQ4(int en, Global_Discretization& gd) : Element_Formulation(en, gd) {
2 ...
3 if(Matrix_Representation::Assembly_Switch == Matrix_Representation::NODAL_STRESS) {
4 int stress_no = (ndf+1)*ndf/2;
5 the_element_nodal_value &= C0(nen*stress_no, (double*)0);
6 C0 projected_nodal_stress = SUBVECTOR("int, C0&", stress_no, the_element_nodal_value);
7 H0 Sigma = INTEGRABLE_VECTOR("int, Quadrature", 3, qp);
8 Sigma = 0.0;
9 for(int i = 0; i < nen; i++) {
10 B1 &= Nx[i][0]; B2 &= Nx[i][1];
11 DB[0][0] = Dv[0][0]*B1; DB[0][1] = Dv[0][1]*B2;
12 DB[1][0] = Dv[0][1]*B1; DB[1][1] = Dv[1][1]*B2;
13 DB[2][0] = Dv[2][2]*B2; DB[2][1] = Dv[2][2]*B1;
14 Sigma += DB(0)*(ul[i*ndf]+gl[i*ndf]) + DB(1)*(ul[i*ndf+1]+gl[i*ndf+1]);
15 }
16 for(int i = 0; i < nen; i++) {
17 C0 lumped_mass = ((H0)N[i]) | dv;
18 projected_nodal_stress(i) = ( ((H0)N[i])*Sigma | dv ) / lumped_mass;
19 }
20 } else stiff &= K | dv;
21 }
22 int main() {
23 ...
24 Matrix_Representation::Assembly_Switch = Matrix_Representation::NODAL_STRESS;
25 mr.assembly(FALSE);
26 cout << "nodal stresses: " << endl;
27 for(int i = 0; i < oh.total_node_no(); i++) {
28 int node_no = oh.node_array()[i].node_no();
29 cout << "{ " << node_no << "| "
30 << (mr.global_nodal_value()[i][0]) << ", "
31 << (mr.global_nodal_value()[i][1]) << ", "
32 << (mr.global_nodal_value()[i][2]) << "}" << endl;
33 }
34 ...
35 }
The computation of strains on Gaussian integration points and nodes is similar to the computation of stresses. In
place of Eq. 4•192 for stresses, we have strains computed according to . The flag
“Matrix_Representation::Assembly_Switch” is now set to “Matrix_Representation::STRAIN” and
“Matrix_Representation::NODAL_STRAIN” for Gauss point stresses and nodal stresses, respectively. The
results of relative magnitudes of displacements, nodal stresses and nodal strains of the 4-node quadrilateral ele-
ment are shown in Figure 4•44.
We introduce the notorious pathology of the finite element method by demonstrating (1) shear locking and
(2) dilatational locking for the bilinear four-node element in plane elasticity.
ε
e
h
Bu
ˆ
e
a
=
Finite Element Method Primer
396 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Figure 4•44Displacement (arrows), nodal stresses (crossed-hairs, solid line for compression, dashed
line for tension), and nodal strain (ellipsoidals) of the beam bending problem. The magnitudes of these
three quantities have all been re-scaled.
Workbook of Applications in VectorSpace C++ Library 397
Two Dimensional Problems
Shear Locking of Bilinear 4-Node Element
The bilinear 4-node element has shape functions as
Eq. 4•196
We considered a special case of a rectangle (Eq. 4•45a), for simplicity, under applied bending moment as shown
in Figure 4•45. Therefore, the finite element space is spanned by the bases of P = {1, ξ, η, ξη}. Since referential
coordinates ξ- and η- axes of the rectangle is assumed to coincide with the physical coordinates x- and y- axes,
the finite element space is also spanned by {1, x, y, xy}. The solution to the displacement field u = [u, v]
T
for the
bending problem, in plane stress, is
1
Eq. 4•197
This analytical solution is shown in Figure 4•45b with υ = 0 for simplicity. The horizontal displacement compo-
nent, u = xy, will be represented correctly by the bilinear four-node element, since the basis “xy” is included. The
quadratic terms, x
2
and y
2
, in the solution of vertical displacement “v” will not be captured by the element. These
quadratic forms of solution will be “substituting” or “aliasing” to the linear combination of bases in P. For the
bilinear four-node element the shape functions Eq. 4•196 can be expressed in its generic form as “ N
a
= PC
-1
”.
2
Therefore, from Eq. 4•196, we have
Eq. 4•198
1. p.218 in MacNeal, R.H., 1994, “Finite elements: their design and performance”, Marcel Dekker, Inc., New York.
2. p. 116 in Zienkiewicz, O.C. and R.L. Taylor, 1989, “The finite element method: basic formulation and linear problems”,
vol. 1, McGraw-Hill book company, UK.
N
a
ξ η , ( )
1
4
--- 1 ξ
a
ξ + ( ) 1 η
a
η + ( ) =
u
u
v
xy
1
2
---x
2

υ
2
---y
2

= =
Figure 4•45 Rectangular element shear locking analysis.
η
ξ
1
Λ
(a)
(b) (c)
key-stoning; u = xy, v = const.
in-plane bending
u
e
h
ξ η , ( )
N
a
ξ η , ( )u
ˆ
e
a
≡ P ξ η , ( )C
1 –
u
ˆ
e
a
= where C
1 –
,
1
4
---
1 1 1 1
1 – 1 1 1 –
1 – 1 – 1 1
1 1 – 1 1 –
=
Finite Element Method Primer
398 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Let’s exam the “aliasing” of a quadratic solution u = ξ
2
into a bilinear four-node element. The corresponding
nodal values and discretized variable are
, Eq. 4•199
and,
Eq. 4•200
That is we have the alias of . By symmetry of the element we can also obtain the alias of . The
vertical displacement solution in the bending problem in Eq. 4•197 will then be aliased, considering the aspect
ratio “Λ” in the transformation of natural to physical coordinates in a rectangular element, into
Eq. 4•201
With vertical displacement “v” as constant through out the element domain, the deformation becomes a “key-
stoning” or “x-hourglass” mode (see Figure 4•45c, where the constant “v” is set to zero for comparing to the
original configuration). That is the lower-order element, such as the bilinear 4-node element, exhibits locking
phenomenon, when a boundary value problem corresponding to a higher-order solution is imposed.
The analytical strain, derived from Eq. 4•197, corresponding to the bending problem is
Eq. 4•202
where u and v are solutions in Eq. 4•197. The bilinear 4-node element under the same bending condition
responds with the solution in Eq. 4•201, and we have the corresponding strains as
u
ˆ
e
a
u
e
h
u
ˆ
e
a
ξ
a
( )
2
ξ
0
2
ξ
1
2
ξ
2
2
ξ
3
2
1
1
1
1
= = =
u
e
h
PC
1 –
u
ˆ
e
a
1 ξ η ξη
1
4
---
1 1 1 1
1 – 1 1 1 –
1 – 1 – 1 1
1 1 – 1 1 – ¸ ,




¸ _
1
1
1
1
1 = = =
ξ
2
1 ⇒ η
2
1 ⇒
u xy = and v
Λ
2
2
------ –
ν
2
--- – cons t tan = = ,
ε
x
ε
y
γ
xy
∂u
∂x
------
∂v
∂y
------
∂v
∂x
------
∂u
∂y
------ +
y
νy –
0
= =
Workbook of Applications in VectorSpace C++ Library 399
Two Dimensional Problems
Eq. 4•203
Comparing Eq. 4•202 and Eq. 4•203, both ε
y
and γ
xy
are in error. With Poisson’s ratio in the range of ν = [0, 0.5],
γ
xy
will be more serious than ε
y
. The source of error is the interpolating failure of the bilinear four node element
which leads to the aliasing of x
2
and y
2
terms in Eq. 4•197 into constants in Eq. 4•201. A partial solution to this
locking problem is to evaluate γ
xy
at ξ = 0, and η = 0. That is one Gauss point integration of in-plane shear strain
at the center of the element, and 2 2 integration for the remaining direct strain components ε
x
and ε
y
. A more
satisfactory treatment is to add back both x
2
and y
2
to the set of shape functions which is the subject of “non-con-
forming element” in page 502 of Chapter 5. We introduce the treatment by selective reduced integration on in-
plane shear strain γ
xy
(at ξ = 0, η = 0) in the followings.
Eq. 4•176 and Eq. 4•177 are re-written as
Eq. 4•204
and
= Eq. 4•205
Notice that the positions in the stiffness matrix corresponding to variables u and v and their variations u’ and v’
as
Eq. 4•206
The components in Eq. 4•204 and the first term in Eq. 4•205 only involve the direct strains ε
x
(=u,
x
) and ε
y
(=v,
y
).
These terms are evaluated with 2 2 points Gauss integration (the full-integration). The components in the sec-
ond term of Eq. 4•205 involve the in-plane shear strain γ
xy
(=u,
y
+v,
x
), and these are to be evaluated at the center
of the element where ξ = 0, η = 0. This term is applied with 1-point Gauss integration (the reduced integration.)
In retrospect, had we apply 1-point integration to all terms, spurious modes (x-hourglass and y-hourglass)
will arise. That is the two hourglass modes become eigenvectors for the stiffness matrix that is evaluated at the
center of the element. This is evident from Figure 4•45c. The cross-hairs which parallel to the ξ, η axes are dis-
ε
x
ε
y
γ
xy
y
0
x
=
×
λ N
a i ,
N
b j ,
( ) λ
∂N
a
∂x
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂x
---------
∂N
b
∂y
---------
¸ ,
¸ _
∂N
a
∂y
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂y
---------
∂N
b
∂y
---------
¸ ,
¸ _
=
µ δ
ij
N
a k ,
N
b k ,
( ) N
a j ,
N
b i ,
( ) + ( )
µ
2
∂N
a
∂x
---------
∂N
b
∂x
---------
¸ ,
¸ _
0
0 2
∂N
a
∂y
---------
∂N
b
∂y
---------
¸ ,
¸ _
µ
∂N
a
∂x
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂y
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂x
---------
∂N
b
∂y
---------
¸ ,
¸ _
∂N
a
∂y
---------
∂N
b
∂y
---------
¸ ,
¸ _
+
u’u ( ) u’v ( )
v’u ( ) v’v ( )
×
Finite Element Method Primer
400 Workbook of Applications in VectorSpace C++ Library
Chapter 4
torted at 2 2 Gauss integration points, while it is totally undisturbed at the center of the hourglass deformation
mode. That is the hourglass modes give zero energy if 1-point Gauss integration is used. An alternative view is
reveal by the rank of the element stiffness matrix. The bilinear four-node element has 4(nen) 2(ndf) = 8 d.o.f. If
the three rigid body modes have been properly constrained for the problem, we are left with 8-3 = 5 d.o.f. The
rank of the stiffness matrix is provided by number of integration points (1) times the number of stress-strain rela-
tions (3); i.e., 1 3 = 3. Therefore, the rank deficiency for the 1-point integration element stiffness matrix is 5-3
= 2, which corresponding to the x-hourglass and y-hourglass modes. Therefore, in the selective reduced integra-
tion, the 2 2 integration on the terms involving the direct strains ε
x
and ε
y
provides a finite stiffness for the x-
hourglass and y-hourglass modes to prevent them from becoming spurious. The selective reduced integration on
the offending in-plane shear term is implemented in Program Listing 4•16 (project: “invariance_formulation” in
project workspace file “fe.dsw”).
However, the selective reduced integration for curing the in-plane shear locking has a side effect. For an iso-
parametric element such as the bilinear 4-node element, we expect spatial isotropy; i.e., the element is invariant
with respect to rotation since a complete order of polynomial has been used; i.e., the so-called completeness
requirement. This is true only if the element stiffness matrix is fully integrated. When the selective reduced inte-
gration is applied to the second term in Eq. 4•205 that involves in-plane shear strain γ
xy
, the spatial isotropy
will be lost. Therefore the orientation of an element does matter.
A first-order approximation can be proposed to correct the frame dependent problem for the shear term.
1
The
idea is the shear term presented in Eq. 4•205 is not symmetrical. We can symmetrize the two off-diagonal terms
by choosing a local preferred coordinate system x’ as shown in Figure 4•46. The origin is at the centroid of the
element (computed as the intersections of two opposing mid-side line segments). The x’ and y’ axes are to make
angles with ξ and η axes in natural coordinates such that
Eq. 4•207
This approximation is possible to make the shear term nearly invariant if we deal only with element shapes that
are very close to a square element. At the limit of infinitesimal coordinate transformation, Eq. 4•207 is to assume
the “spin” at the centroid vanishes, which is adopted in the “co-rotational” formulation in finite element method.
The invariance formulation, discussed in the above, can be activated by setting macro definition
“__TEST_HUGHES” at compile time.
Unfortunately, for an arbitrary element shape, the mapping from the reference element (in ξ, η) to physical
element (in x, y) is unlikely to be infinitesimal as can be approximated in Eq. 4•207. For an arbitrary element
shape, we can decomposed the shape distortion into eigenvectors as rectangular, parallelogram, and trapezoid
shapes (see Figure 4•48b). There is no practical invariance formulation that can remove the shape sensitivity if
the trapezoid component for a particular element shape is strong.
2
In a finite element program, which often
implemented with sparse matrix technique, the node-ordering can be changed, for example, in order to minimize
the bandwidth of the global stiffness matrix. Sudden change of the node-ordering can therefore inadversarily
change the value of the global stiffness matrix dramatically. A practical fixed to remedy the frame dependent in-
1. see project in p. 261-262 from Hughes, T.J.R., 1987, “ The finite element method: linear static and dynamic finite element
analysis”, Prentice-Hall, Inc., Englewood Cliffs, New Jersey.
2. see p.241-248 in MacNeal, R.H., 1994, “Finite elements: their design and performance”, Marcel Dekker, Inc., New York.
×
×
×
×
∂ξ
∂y
------ y d
∂η
∂x
------ x d =
Workbook of Applications in VectorSpace C++ Library 401
Two Dimensional Problems
#include "include\fe.h"
static const double L_ = 10.0; static const double c_ = 1.0; static const double h_e_ = L_/4.0;
static const double E_ = 30.0e6; static const double v_ = 0.25;
static const double lambda_ = v_*E_/((1+v_)*(1-2*v_));
static const double mu_ = E_/(2*(1+v_));
static const double lambda_bar = 2*lambda_*mu_/(lambda_+2*mu_);
static const double K_ = lambda_bar+2.0/3.0*mu_;
static const double e_ = 0.0;
Omega_h::Omega_h() {
Node *node; double v[2]; int ena[4]; Omega_eh *elem;
v[0] = 0.0; v[1] = 0.0; node = new Node(0, 2, v); node_array().add(node);
v[0] = h_e_-e_; node = new Node(1, 2, v); node_array().add(node);
v[0] = 2.0*h_e_-2.0*e_; node = new Node(2, 2, v); node_array().add(node);
v[0] = 3.0*h_e_-e_; node = new Node(3, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(4, 2, v); node_array().add(node);
v[0] = 0.0; v[1] = 1.0*c_; node = new Node(5, 2, v); node_array().add(node);
v[0] = 1.0*h_e_; node = new Node(6, 2, v); node_array().add(node);
v[0] = 2.0*h_e_; node = new Node(7, 2, v); node_array().add(node);
v[0] = 3.0*h_e_; node = new Node(8, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(9, 2, v); node_array().add(node);
v[0] = 0.0; v[1] = 2.0*c_; node = new Node(10, 2, v); node_array().add(node);
v[0] = h_e_+e_; node = new Node(11, 2, v); node_array().add(node);
v[0] = 2.0*h_e_+2.0*e_; node = new Node(12, 2, v); node_array().add(node);
v[0] = 3.0*h_e_+e_; node = new Node(13, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(14, 2, v); node_array().add(node);
ena[0] = 0; ena[1] = 1; ena[2] = 6; ena[3] = 5;
elem = new Omega_eh(0, 0, 0, 4, ena); omega_eh_array().add(elem);
ena[0] = 1; ena[1] = 2; ena[2] = 7; ena[3] = 6;
elem = new Omega_eh(1, 0, 0, 4, ena); omega_eh_array().add(elem);
ena[0] = 2; ena[1] = 3; ena[2] = 8; ena[3] = 7;
elem = new Omega_eh(2, 0, 0, 4, ena); omega_eh_array().add(elem);
ena[0] = 3; ena[1] = 4; ena[2] = 9; ena[3] = 8;
elem = new Omega_eh(3, 0, 0, 4, ena); omega_eh_array().add(elem);
ena[0] = 5; ena[1] = 6; ena[2] = 11; ena[3] = 10;
elem = new Omega_eh(4, 0, 0, 4, ena); omega_eh_array().add(elem);
ena[0] = 6; ena[1] = 7; ena[2] = 12; ena[3] = 11;
elem = new Omega_eh(5, 0, 0, 4, ena); omega_eh_array().add(elem);
ena[0] = 7; ena[1] = 8; ena[2] = 13; ena[3] = 12;
elem = new Omega_eh(6, 0, 0, 4, ena); omega_eh_array().add(elem);
ena[0] = 8; ena[1] = 9; ena[2] = 14; ena[3] = 13;
elem = new Omega_eh(7, 0, 0, 4, ena); omega_eh_array().add(elem);
}
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
__initialization(df, omega_h); int row_node_no = 5, col_node_no = 3;
the_gh_array[node_order(4)](0) = the_gh_array[node_order(14)](0) =
the_gh_array[node_order(4)](1) = the_gh_array[node_order(9)](1) =
gh_on_Gamma_h::Dirichlet;
for(int i = 0; i < col_node_no; i++) {
the_gh_array[node_order(i*row_node_no)](1) = gh_on_Gamma_h::Neumann;
if(i == 0 || i == (col_node_no-1)) the_gh_array[node_order(i*row_node_no)][1] = -75.0;
else the_gh_array[node_order(i*row_node_no)][1] = -150.0;
}
}
class Elastic_Invariant_Formulation_Q4 : public Element_Formulation { public:
Elastic_Invariant_Formulation_Q4(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
Elastic_Invariant_Formulation_Q4(int, Global_Discretization&);
};
Young’s modulus and Poisson ratio
plane stress λ modification
define nodes
define elements
B.C.
u
4
= u
9
= v
9
= u
14
= 0
τ
y0
= τ
y10
= -75, τ
y5
= -150
Finite Element Method Primer
402 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Element_Formulation* Elastic_Invariant_Formulation_Q4::make(int en,
Global_Discretization& gd) { return new Elastic_Invariant_Formulation_Q4(en,gd); }
Elastic_Invariant_Formulation_Q4::Elastic_Invariant_Formulation_Q4(
int en, Global_Discretization& gd) : Element_Formulation(en, gd) {
Quadrature qp(2, 4);
H1 Z(2, (double*)0, qp), Zai, Eta,
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 4, 2, qp);
Zai &= Z[0]; Eta &= Z[1];
N[0] = (1-Zai)*(1-Eta)/4; N[1] = (1+Zai)*(1-Eta)/4;
N[2] = (1+Zai)*(1+Eta)/4; N[3] = (1-Zai)*(1+Eta)/4;
H1 X = N*xl; H0 Nx = d(N) * d(X).inverse(); J dV(d(X).det());
H0 W_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, nsd, Nx), Wx, Wy;
Wx &= W_x[0][0]; Wy &= W_x[0][1];
C0 e = BASIS("int", ndf), E = BASIS("int", nen),
u = e*E, U = (e%e)*(E%E);
C0 stiff_vol = ( lambda_bar*( +((Wx*~Wx)*U[0][0]+(Wx*~Wy)*U[0][1]+
(Wy*~Wx)*U[1][0]+(Wy*~Wy)*U[1][1] ) ) ) | dV;
Quadrature qp1(2, 1);
H1 z(2, (double*)0, qp1), zai, eta,
n = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 4, 2, qp1);
zai &= z[0]; eta &= z[1];
n[0] = (1.0-zai)*(1.0-eta)/4.0; n[1] = (1.0+zai)*(1.0-eta)/4.0;
n[2] = (1.0+zai)*(1.0+eta)/4.0; n[3] = (1.0-zai)*(1.0+eta)/4.0;
H1 x = n*xl; H0 nx = d(n) * d(x).inverse(); J dv(d(x).det());
H0 w_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, nsd, nx), wx, wy;
wx &= w_x[0][0]; wy &= w_x[0][1];
C0 stiff_dev_shear = mu_* (+( (wy*~wy)*U[0][0] +(wy*~wx)*U[0][1]+
(wx*~wy)*U[1][0] +(wx*~wx)*U[1][1] ) )| dv;
H1 x1 = N*xl; H0 nx1 = d(n) * d(x1).inverse(); J dv1(d(x1).det());
H0 w_x1 = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, nsd, nx1), wx1, wy1;
wx1 &= w_x1[0][0]; wy1 &= w_x1[0][1];
C0 stiff_dev_direct_strain = (2.0*mu_)*
(+( (wx1*~wx1)*U[0][0]+(wy1*~wy1)*U[1][1] ) )| dv1;
C0 stiff_dev = stiff_dev_shear + stiff_dev_direct_strain;
stiff &= stiff_vol + stiff_dev;
}
Element_Formulation* Element_Formulation::type_list = 0;
Element_Type_Register element_type_register_instance;
static Elastic_Invariant_Formulation_Q4
elastic_invariant_formulation_q4_instance(element_type_register_instance);
int main() {
int ndf = 2;
Omega_h oh;
gh_on_Gamma_h gh(ndf, oh);
U_h uh(ndf, oh);
Global_Discretization gd(oh, gh, uh);
Matrix_Representation mr(gd);
mr.assembly();
C0 u = ((C0)(mr.rhs())) / ((C0)(mr.lhs()));
gd.u_h() = u;
gd.u_h() = gd.gh_on_gamma_h();
cout << gd.u_h() << endl;
return 0;
}
2 2 integration
volumetric terms
1 point integration(deviatoric stiffness
which only involve shear strain γ
xy
)
2 2 integration (deviatoric stiffness
which only involve direct strains ε
x
&
ε
y
; notice that if the coordinates has
been rotated the local preferred coordi-
nates is the same as the pure shear term
in the above not the volumetric term.)
×
λ
∂N
a
∂x
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂x
---------
∂N
b
∂y
---------
¸ ,
¸ _
∂N
a
∂y
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂y
---------
∂N
b
∂y
---------
¸ ,
¸ _
µ
∂N
a
∂x
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂y
---------
∂N
b
∂x
---------
¸ ,
¸ _
∂N
a
∂x
---------
∂N
b
∂y
---------
¸ ,
¸ _
∂N
a
∂y
---------
∂N
b
∂y
---------
¸ ,
¸ _
×
µ
2
∂N
a
∂x
---------
∂N
b
∂x
---------
¸ ,
¸ _
0
0 2
∂N
a
∂y
---------
∂N
b
∂y
---------
¸ ,
¸ _
Listing 4•16 Seletive reduce integration on the offending shear term (project workspace file “fe.dsw”,
project “invariance_formulation”.)
Workbook of Applications in VectorSpace C++ Library 403
Two Dimensional Problems
plane shear (after reduced integration) is to implement an algorithm to select, for example, the longest edge of
the elements to begin element node numbering.
1
Then transform the global coordinate system, for computing the
stiffness matrix, under a preferred local coordinate system. After the stiffness is computed at the element level, it
is transformed back to the global coordinate system then assembled to the global stiffness matrix. The origin of
the local coordinate system is chosen as center at the intersection of the two diagonals of the quadrilateral. The x-
axis is chosen to be the bisector of the diagonal angle as shown in Figure 4•47. This implementation can be acti-
vated by setting macro definition “__TEST_MACNEAL”. Note that for simplicity we do not implements the
part of algorithm that choose the longest edge. We only implemented the more mathematical part of the algo-
rithm that demonstrates how to translate to the center of the intersection of the two diagonals and then rotate to
the local coordinate x’-axis, which is the bisector of the diagonals.
1. p.292 in MacNeal, R.H., 1994, same as the above.
Figure 4•46 Hughes’s local preferred coordinate system for the invariance formulation of the
shear term under selective reduced integration. θ
1
||dy|| = θ
2
||dx||, or simply θ
1
= θ
2 ,
if ||dy|| ~ ||dx||
which is consistent with the infinitesimal mapping assumption.
x
y
x’
y’
ξ
η
θ
1
· ξ,
y
θ
2
· η,
x
Figure 4•47 MacNeal’s local preferred coordinate system for selective reduced
integration on shear term.
θ
1
θ
1
θ
2
θ
2
x
y
x’
y’
Finite Element Method Primer
404 Workbook of Applications in VectorSpace C++ Library
Chapter 4
The solutions of the selective reduced integration with invariance formulation is listed in TABLE 4•2. The
invariance formulation are performed on a distorted meshes as shown in Figure 4•48.
Full Integration Selective Reduced Hughes’ local coord. MacNeal’s local coord. Analytical
-0.00311871 -0.0061448 -0.00535423 -0.00565686 -0.00518750
TABLE 4•2. Tip-deflections for selective reduced integration to prevent shear locking and
choices of local preferred coordinate system for invariance of the formulation.
Figure 4•48 (a) Distorted element mesh for testing invariance formulation in the
selective reduced integration for shear terms. (b) 5 eigenvectors for an arbitary shape
distortion (x-, y- translation and rotation are not included).
0.125 0.0625
0.0625
(a)
(b)
x-stretching & y-stretching
rectangulars
parallelogram
x- tapering &y-tapering
trapezoids
1
Λ
δ δ
Workbook of Applications in VectorSpace C++ Library 405
Two Dimensional Problems
Quadratic Element: The Lagrangian 9-Node Element
The Lagrangian 9-node element is implemented as class “ElasticQ9” derived from class
Element_Formulation. The shape function is implemented based on a 4-to-9 nodes algorithm (see page 190 in
Chapter 3)
1 Quadrature qp(2, 9); // 2-dimension, 3 3 integration points;
2 H1 Z(2, (double*)0, qp), // Natrual Coordinates
3 N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 9, 2, qp), Zai, Eta;
4 Zai &= Z[0]; Eta &= Z[1];
// initial four corner nodes
5 N[0] = (1-Zai)*(1-Eta)/4; N[1] = (1+Zai)*(1-Eta)/4;
6 N[2] = (1+Zai)*(1+Eta)/4; N[3] = (1-Zai)*(1+Eta)/4;
// add ceter node
7 N[8] = (1-Zai.pow(2))*(1-Eta.pow(2));
// modification to four corner nodes due to the presence of the center node
8 N[0] -= N[8]/4; N[1] -= N[8]/4; N[2] -= N[8]/4; N[3] -= N[8]/4;
// add four edge nodes
9 N[4] = ((1-Zai.pow(2))*(1-Eta)-N[8])/2; N[5] = ((1-Eta.pow(2))*(1+Zai)-N[8])/2;
10 N[6] = ((1-Zai.pow(2))*(1+Eta)-N[8])/2; N[7] = ((1-Eta.pow(2))*(1-Zai)-N[8])/2;
// modification to four corner nodes due to the presence of the four edge nodes
11 N[0] -= (N[4]+N[7])/2; N[1] -= (N[4]+N[5])/2;
12 N[2] -= (N[5]+N[6])/2; N[3] -= (N[6]+N[7])/2;
The element is registered with element type number “1” in project “2d_beam”. When define element in the
constructor of the discretized domain “Omega_h” this is the number to be referred to the “ElasticQ9” element.
For using this example, we set macro definition to “__LAGRANGIAN_9_NODES”. The results of tip deflection
of the problem in this section are listed in TABLE 4•3.We observe that the shear locking problem in bilinear four-
node element is easily removed by using higher-order interpolation functions.
Element Type Tip Deflection
ElasticQ9 -0.00503098
Analytical -0.00518750
TABLE 4•3. Tip deflection of Lagrangian 9-node element
comparing to the analytical solution of Eq. 4•175.
×
Finite Element Method Primer
406 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Dilatation Locking of Nearly Incompressible Elasticity in Plane Strain
Considerable attention has been paid to the condition of incompressibility (with Poisson ratio ν = 0.5) or
nearly incompressibility ( ). We will show examples that standard element formulation, in plain strain
case, with will have its solution “locked” severely. A more systematic study is the main subject of
Chapter 5 on the “Mixed and hybrid finite element methods”. In this section, we introduce the popular engineer-
ing approach, the selective reduced integration for dilatational locking, which has been shown to be both very
simple and very successful. Let’s first resume the analysis for bending problem in the bilinear 4-node element in
plane strain. For ν = 0.5 in elasticity the condition is equivalent to imposing a kinematic constraint that the
material is incompressible. The analytical solution is
1
Eq. 4•208
The corresponding analytical strains are
Eq. 4•209
The volumetric strain is
Eq. 4•210
where the bulk modulus K and Young’s modulus E, Poisson’s ratio ν are related as
Eq. 4•211
Notice that even when , we have (Eq. 4•211), and (Eq. 4•210), while the pressure “p”
(Eq. 4•210) remains finite. For a 4-node rectangular element, the aliasing of solution in Eq. 4•208 leads to
Eq. 4•212
The corresponding strains manifested in the bilinear 4-node element are
Eq. 4•213
1. p.216-217 in MacNeal, R.H., 1994 “Finite elements: their design and performance”, Marcel Dekker, Inc., New York.
ν 0.5 →
ν 0.5 →
u xy = and v ,
1
2
---x
2

ν
2 1 ν – ( )
--------------------y
2
– =
ε
x
ε
y
γ
xy
y
ν
1 ν – ( )
----------------y –
0
=
ε
v
ε
x
ε
y
+
1 2ν –
1 ν –
---------------
¸ ,
¸ _
y = = and p , Kε
v
Ey
3 1 ν – ( )
-------------------- = =
K
E
3 1 2ν – ( )
----------------------- =
ν 0.5 → K ∞ → ε
v
0 →
u xy = and v ,
1
2
-- -Λ
2

ν
2 1 ν – ( )
-------------------- – =
ε
x
ε
y
γ
xy
y
0
x
=
Workbook of Applications in VectorSpace C++ Library 407
Two Dimensional Problems
Now the volumetric strain ε
v
= y, which is a finite value. When , and from Eq. 4•210. This
is the dilatation locking at the incompressible limit of . Comparing Eq. 4•209 and Eq. 4•213, both ε
y
and
γ
xy
are in error. The error is caused by the interpolating failure of the bilinear four-node element in representing
x
2
and y
2
. The situation is exactly the same as in the shear locking problem. Therefore, the non-conforming ele-
ment (page 502 in Chapter 5), which adds back x
2
and y
2
to the set of the interpolation functions, will have the
capability to remedy both the shear locking and dilation locking problems for the bilinear four-node element.
A quick fix to solve this “dilatation locking” problem is that we can divide the stiffness matrix into volumet-
ric and deviatoric part. Then, the volumetric part is applied the reduced integration. With this selective reduced
integration scheme, the condition of constant volume constraint can be relaxed.
It is not immediately clear that how we can perform selective reduced integration on the B-matrix formula-
tion, that is
Eq. 4•214
A volumetric-deviatoric split
1
is applied to the stiffness of Eq. 4•214 into the volumetric part and deviatoric part.
Define the volumetric strain ε
v
as
Eq. 4•215
In vector form of plane elasticity, m = [1, 1, 0]
T
and ε = [ε
x
, ε
y
, γ
xy
]
T
. The mean stress or pressure is
Eq. 4•216
K is the bulk modulus of the material. We define the devioatric strain ε
d
as
Eq. 4•217
The deviatoric stress σ
d
is (in vector form σ = [σ
x
, σ
y
, τ
xy
]
T
)
Eq. 4•218
where
Eq. 4•219
1. p.334-352 in Zienkiewicz, O.C., and R.L. Taylor, 1989, “The finite element method: basic formulation and linear prob-
lems”, 4th ed., vol. 1, McGraw-Hill, London, UK.
ν 0.5 → K ∞ → p ∞ →
ν 0.5 →
k
e
pq
k
e
i ajb
ε δu ( )
T
Dε u ( )dΩ


e
i
T
B
a
T
DB
b
dΩe
j


= = =
ε
v
ε
x
ε
y
+ m ε • = =
p
1
3
--- σ
x
σ
y
σ
z
+ + ( ) ≡ Kε
v
K m ε • = =
ε
d
ε

v
3
---------- – ≡ I
m m ⊗
3
----------------- –
¸ ,
¸ _
ε =
σ
d
µD
0
ε
d
µ D
0
2
3
---m m ⊗ –
¸ ,
¸ _
ε = =
D
0
2 0 0
0 2 0
0 0 1
=
Finite Element Method Primer
408 Workbook of Applications in VectorSpace C++ Library
Chapter 4
From Eq. 4•216 and Eq. 4•218 the volumetric-deviatoric split version of the B-matrix formulation (Eq. 4•214)
becomes
Eq. 4•220
We may define the volumetric stiffness and deviatoric stiffness separately as
Eq. 4•221
Therefore, the selective reduced integration can be applied to these two separate terms accordingly. The follow-
ing codes implemented Eq. 4•221 as
1 Quadrature qp(2, 4); // 2 2 points standard integration
2 H1 Z(2, (double*)0, qp),
3 N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int, int, Quadrature", 4, 2, qp),
4 Zai, Eta;
5 Zai &= Z[0]; Eta &= Z[1];
6 N[0] = (1-Zai)*(1-Eta)/4; N[1] = (1+Zai)*(1-Eta)/4;
7 N[2] = (1+Zai)*(1+Eta)/4; N[3] = (1-Zai)*(1+Eta)/4;
8 H1 X = N*xl; // Physical Coordinates
9 H0 Nx = d(N) * d(X).inverse();
10 J dv(d(X).det());
11 Quadrature qp1(2, 1); // 1-point reduced integration
12 H1 z(2, (double*)0, qp1),
13 n = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int, int, Quadrature", 4, 2, qp1),
14 zai, eta;
15 zai &= z[0]; eta &= z[1];
16 n[0] = (1-zai)*(1-eta)/4; n[1] = (1+zai)*(1-eta)/4;
17 n[2] = (1+zai)*(1+eta)/4; n[3] = (1-zai)*(1+eta)/4;
18 H1 x = n*xl;
19 H0 nx = d(n) * d(x).inverse();
20 J d_v(d(x).det());
21 double d_0[3][3] = { {2.0, 0.0, 0.0}, //
22 {0.0, 2.0, 0.0},
23 {0.0, 0.0, 1.0}};
24 C0 D_0 = MATRIX("int, int, const double*", 3, 3, d_0[0]);
k
e
pq
k
e
iaj b
ε δu ( )
T
σ u ( )dΩ


ε w ( )
T
σ
d
u ( ) mp u ( ) + [ ]dΩ


= = =
e
i
T
B
a
T
µ D
0
2
3
---m m ⊗ –
¸ ,
¸ _
B
b
dΩ B
a
T
K m m ⊗ ( )B
b
dΩ


+


¸ ,

¸ _
e
j
=
k
vol
e
i
T
B
a
T
K m m ⊗ ( )B
b
dΩe
j


=
k
dev
e
i
T
B
a
T
µ D
0
2
3
---m m ⊗ –
¸ ,
¸ _
B
b
dΩ


e
j
=
×
D
0
2 0 0
0 2 0
0 0 1
=
Workbook of Applications in VectorSpace C++ Library 409
Two Dimensional Problems
25 double m_0[3] = {1.0, 1.0, 0.0};
26 C0 m = VECTOR("int, const double*", 3, m_0); // m = [1, 1, 0]
T
27 H0 W_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, nsd, Nx),
28 Wx, Wy, B;
29 Wx &= W_x[0][0]; Wy &= W_x[0][1];
30 B &= (~Wx || C0(0.0)) &
31 (C0(0.0) || ~Wy ) &
32 (~Wy || ~Wx );
33 C0 stiff_dev = ((~B) * (mu_*(D_0-2.0/3.0*(m%m)) * B)) | dv; //
34 H0 w_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, nsd, nx),
35 wx, wy, b;
36 wx &= w_x[0][0]; wy &= w_x[0][1];
37 b &= (~wx || C0(0.0)) &
38 (C0(0.0) || ~wy ) &
39 (~wy || ~wx );
40 C0 stiff_vol = ((~b) * ((K_*(m%m)) * b)) | d_v; //
41 stiff &= stiff_dev + stiff_vol;
Lines 1-10 define 2 2 points integration, and lines 11-20 define 1-point integration. The deviatoric stiffness is
implemented in line 33, and the volumetric stiffness in line 40. This computation can be done with macros
“__TEST_PLAIN_STRAIN”,“__NEARLY_INCOMPRESSIBLE”,“__TEST_B_MATRIX_VOLUMETRIC_D
EVIATORIC_SPLIT”, and “__TEST_SELECTIVE_REDUCED_INTEGRATION” defined at compile time.
The result of tip deflection with standard integration scheme is “-0.000149628” (i.e., sever locking compared to
tip deflection of ElasticQ4 element with ν = 0.25 in TABLE 4•2.). With the selective reduced integration on the
volumetric term, under B-matrix formulation, the tip-deflection is “-0.00305825”.
For the coordinate-free tensorial formulation of Eq. 4•156,
: Eq. 4•222
and the indicial notation formulation of Eq. 4•161,
Eq. 4•223
We notice that in Eq. 4•221, the bulk modulus
1
is
Eq. 4•224
1. see p.129-130 in Fung, C.Y., 1965, “ Foundations of solid mechanics”, Prentice-Hall, Inc., Englewood Cliffs, N.J.
k
dev
e
i
T
B
a
T
µ D
0
2
3
---m m ⊗ –
¸ ,
¸ _
B
b
dΩ


e
j
=
k
vol
e
i
T
B
a
T
K m m ⊗ ( )B
b
dΩe
j


=
×
k
e
a φ
e
a
φ
e
b
, ( ) λ div N
a
• div N
b
( ) 2µ def N
a
( ) + [


= = def N
b
)]dΩ
k
e
i ajb
λ N
a i ,
N
b j ,
dΩ


µ δ
i j
N
a k ,
N
b k ,
dΩ


N
a j ,
N
b i ,
dΩ


+
¸ ,

¸ _
+ =
K λ
2
3
---µ + =
Finite Element Method Primer
410 Workbook of Applications in VectorSpace C++ Library
Chapter 4
At the nearly incompressible limit ( ), λ >> µ. We have . The two first terms of Eq. 4•222 and Eq.
4•223 are approximately equivalent to the k
vol
in Eq. 4•221. We can simply choose these two terms for reduced
integration and the implementation is straight forward. The implementation can be activated, in project
“2d_beam”, by setting the macro definitions “__TEST_PLAIN_STRAIN”, “__NEARLY_INCOMPRES
SIBLE”, and“__TEST_SELECTIVE_REDUCED_INTEGRATION” together with corresponding macro defini-
tions for the above two formulations, “__TEST_COORDINATE_FREE_TENSORIAL_FORMULATION” and
“__TEST_INDICIAL_NOTATION_FORMULATION”, respectively. These two alternative formulations,
involve µ and λ, give the same results. With standard integration scheme, the tip-deflection is “-0.000149995”,
and with the reduced integration scheme, the tip-deflection is “-0.00311641”.
ν 0.5 → K λ ≈
Workbook of Applications in VectorSpace C++ Library 411
Two Dimensional Problems
4.3.4 Patch Tests—Finite Element Test Suites for Software Quality Assurance (SQA)
Finite element is such a complicated method that the software quality assurance (SQA) can be quite a chal-
lenging task. As a framework based library, not a caned-program, fe.lib requires user’s participation in program-
ming to complete the application programs. Therefore, a well-thought-out plan for debugging and testing is of
primary importance for hands-on finite element practitioners. This section gives many examples of how to
develop proper test suites for finite element method. These test suites are based on a well known test plans
1
.
Patch Tests—Consistency and Stability
Consider an element patch shows in Figure 4•49 in plane stress with material properties of Young’s modulus
E = 1x10
3
, and Poisson’s ratio ν = 0.3. A simple constant stress (strain) solution over entire problem domain is
assumed. In this case, the only non-zero stress is a constant stress in x-direction σ
x
= 2, and σ
y
= τ
xy
= 0. The
strain-stress relation for the plane stress assumption gives solutions of constant strain, and displacement (u, v) as
Eq. 4•225
We observe that the imposing displacement field for the patch test is therefore linear. This gives a simple exact
solution the nodal displacements, nodal stresses, and nodal reactions shown in TABLE 4•4.
1. Taylor, R.L., O.C. Zienkiewicz, J.C. Simo, and A.H.C. Chan, 1986, “The patch test--a condition for assessing f.e.m. con-
vergence”, International Journal of Numerical Methods in Engineering, vol., 22, pp. 39-62, or, for more availability, an abbre-
viated representation as Chapter 11 in Zienkiewicz, O.C., and R.L. Taylor, 1989, “The finite element method: basic
formulation and linear problems”, McGraw-Hill, London., UK.
Node # u v σ
x
σ
y
τ
xy
r
x
r
y
0 0.0000 0.0000 2 0 0 2 0
1 0.0040 0.0000 2 0 0 -3 0
2 0.0040 -0.00180 2 0 0 -2 0
3 0.0000 -0.00120 2 0 0 3 0
4 0.0008 -0.00024 2 0 0 0 0
5 0.0028 -0.00036 2 0 0 0 0
6 0.0030 -0.00120 2 0 0 0 0
7 0.0006 -0.00096 2 0 0 0 0
TABLE 4•4. Nodal displacement, nodal stresses and nodal reactions of the element patch.
ε
x
σ
x
E
------
νσ
y
E
--------- –
σ
x
E
------ 0.002 u ⇒ 0.002x = = = =
ε
y
νσ
x
E
--------- –
σ
y
E
------ +
νσ
x
E
--------- – 0.0006 v ⇒ – 0.0006 – y = = = =
γ
xy
2 1 ν + ( )τ
xy
E
---------------------------- 0 = =
Finite Element Method Primer
412 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Consistency Requirement demands the governing partial differential equation to be satisfied exactly. The
matrix form of the weak statement derived from the governing partial differential equation is
K
ij
u
j
= f
i
Eq. 4•226
where K
ij
is the global stiffness matrix and f
i
is the global nodal force vector. We first specify all nodes with the
linear displacement calculated from u = 0.002x, and v = -0.0006y, where u
j
= [u
j
, v
j
]
T
is the solution vector, and
x = [x, y]
T
is the nodal coordinates. Since no loading, f
i
in Eq. 4•226, is specified for the internal nodes (# 4, 5,
6, 7), the “reaction” calculated according to “-K
ij
u
j
” should be identically zero, if the governing partial differen-
tial equation is to be satisfied. This is the “Test A” in Figure 4•49. The Test A is useful in checking the correct-
ness of program statements in implementing the stiffness matrix. The Program Listing 4•17 implements the test
suite for the Test A described in the above. The standard (full-) integration (2 2) for Test A is the default setting
of this program. The uniform reduced integration (1-point Gauss integration) can be performed on this program
by setting macro definition “__TEST_UNIFORM_REDUCED_INTEGRATION” at compile time. Both the
standard integration and uniform reduced integration produce the exact reaction, up to machine accuracy, as
listed in TABLE 4•4.
In the “Test B” in Figure 4•49, a second step for checking the consistency requirement, we specified only
nodes on the boundaries. Then, the unknown u
j
on internal nodes (# 4, 5, 6, 7) can be calculated according to
u
j
= (K
ij
)
-1
f
i
Eq. 4•227
This step requires the matrix solver to “invert” the stiffness matrix K
ij
. The matrix solver is a fixture in “fe.lib”.
Assuming the matrix solver chosen is appropriate to solve the problem at hand, the “Test B” checks the accu-
racy of the stiffness matrix maintained in the process of matrix solution step. A problematic stiffness matrix, or
improper matrix solver, will lose accuracy significantly and may give out erroneous solution. The Test B can be
Figure 4•49 Patch of elements for consistency and stablility test.
0
1
2
3
4
5
6
7
u = 0.002x v = -0.0006y
(0, 0) (2, 0)
(2, 3)
(0, 2)
(0.4,0.4)
(1.4, 0.6)
(1.5, 2.0)
(0.3, 1.6)
free d.o.f.s fixed d.o.f.s
f
x
=2
f
x
=3
Consistency
Stability
(Test A) (Test B)
E = 1x10
3
, ν = 0.3
σ
x
= 2, σ
y
= τ
xy
=0
(Test C)
×
Workbook of Applications in VectorSpace C++ Library 413
Two Dimensional Problems
#include "include\fe.h"
static const double E_ = 1.0e3; static const double v_ = 0.3;
static const double lambda_=v_*E_/((1+v_)*(1-2*v_)); static const double mu_=E_/(2*(1+v_));
static const double lambda_bar = 2*lambda_*mu_/(lambda_+2*mu_);
Omega_h::Omega_h() { double v[2]; Node* node; int ena[4]; Omega_eh* elem;
v[0] = 0.0; v[1] = 0.0; node = new Node(0, 2, v); the_node_array.add(node);
v[0] = 2.0; v[1] = 0.0; node = new Node(1, 2, v); the_node_array.add(node);
v[0] = 2.0; v[1] = 3.0; node = new Node(2, 2, v); the_node_array.add(node);
v[0] = 0.0; v[1] = 2.0; node = new Node(3, 2, v); the_node_array.add(node);
v[0] = 0.4; v[1] = 0.4; node = new Node(4, 2, v); the_node_array.add(node);
v[0] = 1.4; v[1] = 0.6; node = new Node(5, 2, v); the_node_array.add(node);
v[0] = 1.5; v[1] = 2.0; node = new Node(6, 2, v); the_node_array.add(node);
v[0] = 0.3; v[1] = 1.6; node = new Node(7, 2, v); the_node_array.add(node);
ena[0] = 0; ena[1] = 1; ena[2] = 5; ena[3] = 4;
elem = new Omega_eh(0, 0, 0, 4, ena); the_omega_eh_array.add(elem);
ena[0] = 5; ena[1] = 1; ena[2] = 2; ena[3] = 6;
elem = new Omega_eh(1, 0, 0, 4, ena); the_omega_eh_array.add(elem);
ena[0] = 7; ena[1] = 6; ena[2] = 2; ena[3] = 3;
elem = new Omega_eh(2, 0, 0, 4, ena); the_omega_eh_array.add(elem);
ena[0] = 0; ena[1] = 4; ena[2] = 7; ena[3] = 3;
elem = new Omega_eh(3, 0, 0, 4, ena); the_omega_eh_array.add(elem);
ena[0] = 4; ena[1] = 5; ena[2] = 6; ena[3] = 7;
elem = new Omega_eh(4, 0, 0, 4, ena); the_omega_eh_array.add(elem); }
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) { __initialization(df, omega_h);
for(int i = 0; i < 8; i++)
for(int j = 0; j < 2; j++) the_gh_array[node_order(i)](j) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(1)][0] = 0.004; the_gh_array[node_order(2)][0] = 0.004;
the_gh_array[node_order(2)][1] = -0.0018; the_gh_array[node_order(3)][1] = -0.0012;
the_gh_array[node_order(4)][0] = 0.0008; the_gh_array[node_order(4)][1] = -0.00024;
the_gh_array[node_order(5)][0] = 0.0028; the_gh_array[node_order(5)][1] = -0.00036;
the_gh_array[node_order(6)][0] = 0.003; the_gh_array[node_order(6)][1] = -0.0012;
the_gh_array[node_order(7)][0] = 0.0006; the_gh_array[node_order(7)][1] = -0.00096; }
class ElasticQ4 : public Element_Formulation { public:
ElasticQ4(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
ElasticQ4(int, Global_Discretization&); };
Element_Formulation* ElasticQ4::make(int en, Global_Discretization& gd) {
return new ElasticQ4(en,gd); }
static const double a_ = E_ / (1-pow(v_,2));
static const double Dv[3][3] = { {a_, a_*v_, 0.0}, {a_*v_, a_, 0.0}, {0.0, 0.0, a_*(1-v_)/2.0} };
C0 D = MATRIX("int, int, const double*", 3, 3, Dv[0]);
ElasticQ4::ElasticQ4(int en, Global_Discretization& gd) : Element_Formulation(en, gd) {
Quadrature qp(2, 4); H1 Z(2, (double*)0, qp), Zai, Eta,
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int, int, Quadrature", 4, 2, qp);
Zai &= Z[0]; Eta &= Z[1]; N[0] = (1-Zai)*(1-Eta)/4; N[1] = (1+Zai)*(1-Eta)/4;
N[2] = (1+Zai)*(1+Eta)/4; N[3] = (1-Zai)*(1+Eta)/4; H1 X = N*xl; J dv(d(X).det());
for(int b = 0; b < nen; b++) { B1 &= Nx[b][0]; B2 &= Nx[b][1];
DB[0][0] = Dv[0][0]*B1; DB[0][1] = Dv[0][1]*B2; DB[1][0] = Dv[0][1]*B1;
DB[1][1] = Dv[1][1]*B2; DB[2][0] = Dv[2][2]*B2; DB[2][1] = Dv[2][2]*B1;
for(int a = 0; a <= b; a++) { B1 &= Nx[a][0]; B2 &= Nx[a][1];
K[2*a ][2*b] = B1*DB[0][0] + B2*DB[2][0];
K[2*a ][2*b+1] = B1*DB[0][1] + B2*DB[2][1];
K[2*a+1][2*b] = B2*DB[1][0] + B1*DB[2][0];
K[2*a+1][2*b+1] = B2*DB[1][1] + B1*DB[2][1]; } }
for(int b = 0; b < nen; b++) for(int a = b+1; a < nen; a++) {
K[2*a ][2*b] = K[2*b ][2*a ]; K[2*a ][2*b+1] = K[2*b+1][2*a ];
K[2*a+1][2*b] = K[2*b ][2*a+1]; K[2*a+1][2*b+1] = K[2*b+1][2*a+1];
}
define nodes
define elements
define boundary conditions
define element “ElasticQ4”
Finite Element Method Primer
414 Workbook of Applications in VectorSpace C++ Library
Chapter 4
if(Matrix_Representation::Assembly_Switch == Matrix_Representation::REACTION) {
stiff &= K | dv; the_element_nodal_value &= stiff * (ul+gl);
} else if(Matrix_Representation::Assembly_Switch == Matrix_Representation::STRESS) {
H0 Sigma = INTEGRABLE_VECTOR("int, Quadrature", 3, qp); Sigma = 0.0;
for(int i = 0; i < nen; i++) { B1 &= Nx[i][0]; B2 &= Nx[i][1];
DB[0][0] = Dv[0][0]*B1; DB[0][1] = Dv[0][1]*B2; DB[1][0] = Dv[0][1]*B1;
DB[1][1] = Dv[1][1]*B2; DB[2][0] = Dv[2][2]*B2; DB[2][1] = Dv[2][2]*B1;
Sigma += DB(0)*(ul[i*ndf]+gl[i*ndf]) + DB(1)*(ul[i*ndf+1]+gl[i*ndf+1]);
}
int nqp = qp.no_of_quadrature_point();
for(int i = 0; i < nqp; i++) { cout << setw(9) << en
<< setw(14) << ((H0)X[0]).quadrature_point_value(i)
<< setw(14) << ((H0)X[1]).quadrature_point_value(i)
<< setw(14) << (Sigma[0].quadrature_point_value(i))
<< setw(14) << (Sigma[1].quadrature_point_value(i))
<< setw(14) << (Sigma[2].quadrature_point_value(i)) << endl;
}
} else if (Matrix_Representation::Assembly_Switch ==
Matrix_Representation::NODAL_STRESS) {
int stress_no = (ndf+1)*ndf/2; the_element_nodal_value &= C0(nen*stress_no, (double*)0);
C0 projected_nodal_stress = SUBVECTOR("int, C0&", stress_no, the_element_nodal_value);
H0 Sigma = INTEGRABLE_VECTOR("int, Quadrature", 3, qp); Sigma = 0.0;
for(int i = 0; i < nen; i++) { B1 &= Nx[i][0]; B2 &= Nx[i][1];
DB[0][0] = Dv[0][0]*B1; DB[0][1] = Dv[0][1]*B2; DB[1][0] = Dv[0][1]*B1;
DB[1][1] = Dv[1][1]*B2; DB[2][0] = Dv[2][2]*B2; DB[2][1] = Dv[2][2]*B1;
Sigma += DB(0)*(ul[i*ndf]+gl[i*ndf]) + DB(1)*(ul[i*ndf+1]+gl[i*ndf+1]);
}
for(int i = 0; i < nen; i++) { C0lumped_mass = ((H0)N[i]) | dv;
projected_nodal_stress(i) = ( ((H0)N[i])*Sigma | dv ) / lumped_mass;
}
} else stiff &= K | dv;
}
Element_Formulation* Element_Formulation::type_list = 0;
Element_Type_Register element_type_register_instance;
static ElasticQ4 elasticq4_instance(element_type_register_instance);
int main() { int ndf = 2; Omega_h oh; gh_on_Gamma_h gh(ndf, oh);
U_h uh(ndf, oh); U_h hh(ndf, oh);
Global_Discretization gd(oh, gh, uh);
Global_Discretization hd(oh, gh, hh);
Matrix_Representation mr(gd);
Matrix_Representation::Assembly_Switch = Matrix_Representation::REACTION;
mr.assembly(FALSE);
cout << "reaction:" << endl << (mr.global_nodal_value()) << endl;
Matrix_Representation::Assembly_Switch = Matrix_Representation::STRESS;
cout << "gauss point stresses: " << endl;
cout.setf(ios::left,ios::adjustfield);
cout << setw(9) << " elem #, " << setw(14) << "x-coor.," << setw(14) << "y-coor.,"
<< setw(14) << "sigma-11," << setw(14) << "sigma-22," << setw(14) << "sigma-12" << endl;
mr.assembly(FALSE);
Matrix_Representation::Assembly_Switch = Matrix_Representation::NODAL_STRESS;
mr.assembly(FALSE);
cout << "nodal stresses: " << endl << (mr.global_nodal_value()) << endl;
return 0;
}
Post-processing
compute reaction
compute stresses on Gauss integration
points
compute nodal stresses projection
declare global discretization and matrix
representation
compute reaction
compute stresses on Gauss points
compute nodal stresses projection
Listing 4•17 Patch test A(project workspace file “fe.dsw”, project “patch_test” with Macro definition
“__PATCH_TEST_A” set at compile time).
Workbook of Applications in VectorSpace C++ Library 415
Two Dimensional Problems
activated by setting macro definition “__PATCH_TEST_B” in the same project. Again, both the standard inte-
gration and uniform reduced integration produce the exact internal nodal displacements as listed in TABLE 4•4.
Stability Requirement examines if the zero-energy modes (eigenvalues) of the stiffness matrix K
ij
can be
excited from loading f
i
on the boundaries. If this does occur the eigenvectors, with arbitrary magnitudes but no
energy contribution, will pollute the solution and render the solution useless. In the “Test C” in Figure 4•49,
node # 0 is fixed on both directions and node # 3 is fixed on x-direction but allowed to be moved on y-direction.
This suppresses three degree of freedoms, which is chosen to prohibit three modes of rigid body motions,
namely, x-translation, y-translation, and infinitesimal rotation. Note that fixing these three degree of freedoms to
zero is still consistent with the assumed solution of u = 0.002x, and v = -0.0006y. In this case, node #1 is given
loading of f
x
= 3, and node #2 is given loading of f
x
= 2, which is also the same as the reactions on these two
nodes computed from the reaction of Test A. The displacement solutions from Test C are, then, checked against
the assumed solutions.
Figure 4•50 Deformation of the element patch magnifies 50 times in (a) solution with
standard 2 2 integration points, (b) solution with uniform reduced (1 1) integration, (c)
solution with uniform reduced integration using pseudo (Moore-Penrose) inverse for matrix
solver; i.e., with singular value decomposition, (d) and (e) are compared to two zero-energy
hourglass modes of a square bilinear element (the eigenvectors designated as the x-
hourglass and y- hourglass modes), associated with the signular values of the uniform
reduced (1 1) integration stiffness matrix.
× ×
×
(b) 1x1 solution (c) Pseudo-inverse 1x1
solution
(d) y-hourglass mode (for a square) (e) x-hourglass mode (for a square)
(a) 2x2 solution
Finite Element Method Primer
416 Workbook of Applications in VectorSpace C++ Library
Chapter 4
With the same project “patch_test” in project workspace file “fe.dsw”, the Test C is activated by setting the
macro definition “__PATCH_TEST_C”. For standard integration (2 2), the exact solution in TABLE 4•4. is
reproduced. The solution magnified by 50 times is shown in Figure 4•50a. If we set macro definition
“__TEST_UNIFORM_REDUCED_INTEGRATION” (1-point Gaussian integration) at compile time, the solu-
tion rendered is useless as shown in Figure 4•50 (b). We can then set macro defintion
“__TEST_SINGULAR_VALUE_DECOMPOSITION” to analyze the problem. Under the uniform reduced
integration, the rank of the stiffness matrix has rank deficiency of 2 (the full rank = 13 under 2 2 integration,
and the rank = 11 under 1-point integration). With singular value decomposition the matrix can be solved with
the so-called Moore-Penrose (pseudo-) inverse as in page 40 of Chapter 1. The effect of this generalized inverse
is to filter out two eigen-modes corresponding to the two singular values which are very close to zero. These two
eigen-modes are plotted and compared to two spurious hourglass modes of a square bilinear element (some use
the “key-stoning mode” for bilinear element and reserve the term “hourglass mode” for quadratic element)
1
.
Note that we compare the outlines of these two eigen-modes to the two spurious modes of a square bi-linear ele-
ment. The internal nodes of the current element patch can be considered as to add to higher order variations on
top of the two spurious modes. However, we emphasize that the singular value decomposition is used as an ana-
lytical tool to analyze the rank-deficient nature of the problem. The computation cost of the singular value
decomposition is very expensive compared to that of the Cholesky decomposition for a symmetrical matrix; i.e.,
the most expensive one among the matrix solver provided in Chapter 1.
1. see p.242 in Hughes, T. J.R., 1987, “The finite element method: linear static and dynamic finite element analysis”, Pren-
tice-Hall Inc., Englewood Cliffs, New Jersey.
×
×
Workbook of Applications in VectorSpace C++ Library 417
Two Dimensional Problems
Weak Patch Test for an Axisymmetrical Problem
The patch test require only when the mesh size “h” approach zero, the approximated solution should con-
verge to the exact solution. For problem in the Cartesian coordinates the “coefficients” of the governing partial
differential equation are constants. Therefore, an arbitrary mesh size should produce the exact solution. The
weak patch test, for problem written in other than the Cartesian coordinates, revert the criterion to pass the patch
test as a series of solutions converging to the exact solution when the mesh size approaches zero.
Consider an axisymmetrical problem as shown in Figure 4•51.
1
For an axisymmetrical problem with coordi-
nate system denoted as r, z, θ, and the displacement along θ-direction is assumed zero, and u, w are the displace-
ment along r-, and z- directions, respectively. We assume solution as
u = 2r, and w = 0 Eq. 4•228
The strain vector is defined as
2
Eq. 4•229
Therefore, with , where the B-matrix for the axisymmetrical case becomes
1. Taylor, R.L., O.C. Zienkiewicz, J.C. Simo, and A.H.C. Chan, 1986, “The patch test--a condition for assessing f.e.m. con-
vergence”, International Journal of Numerical Methods in Engineering, vol., 22, pp. 39-62.
2. Chapter 12 in Timoshenko, S.P., and J.N. Goodier, 1970, “ Theory of elasticity”, McGraw-Hill, Inc., London, U.K.
Figure 4•51An axisymmetricl problem for weak patch test.
r
z
θ
r·1
h
h h
0 1 2
3 4 5
ε
ε
z
ε
r
ε
θ
γ
rz
∂w
∂z
-------
∂u
∂r
------
u
r
---
∂u
∂z
------
∂w
∂r
------- +
= =
ε
e
h
B
a
u
ˆ
e
a
=
Finite Element Method Primer
418 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Eq. 4•230
For isotropic material case the D matrix becomes
Eq. 4•231
In the integration of axisymmetrical problem the stiffness matrix is
Eq. 4•232
The infinitesimal volume is taken over the whole ring of material as dV = 2πr dr dz. For the selective reduced
integration, the volumetric and deviatoric split of the stiffness matrix as in Eq. 4•221 is still valid
Eq. 4•233
with two simple modifications for axisymmetrical consideration that m = [1, 1, 1, 0]
T
, and
Eq. 4•234
For the current problem, the material constants are given as E = 1, and υ = 0, for simplicity. This gives
ε
r
= ε
θ
= σ
r
= σ
θ
= 2 Eq. 4•235
B
a
0
∂N
a
∂z
---------
∂N
a
∂r
--------- 0
N
a
r
------ 0
∂N
a
∂z
---------
∂N
a
∂r
---------
= and u
ˆ
e
a
,
u
ˆ
e
a
v
ˆ
e
a
=
D
E 1 ν – ( )
1 ν + ( ) 1 2ν – ( )
--------------------------------------
1
ν
1 ν –
------------
ν
1 ν –
------------ 0
ν
1 ν –
------------ 1
ν
1 ν –
------------ 0
ν
1 ν –
------------
ν
1 ν –
------------ 1 0
0 0 0
1 2ν –
2 1 ν – ( )
--------------------
=
K
e
B
T
DBdΩ

=
k
vol
e
i
T
B
a
T
K m m ⊗ ( )B
b
dΩe
j


=
k
dev
e
i
T
B
a
T
µ D
0
2
3
---m m ⊗ –
¸ ,
¸ _
B
b
dΩ


e
j
=
D
0
2 0 0 0
0 2 0 0
0 0 2 0
0 0 0 1
=
Workbook of Applications in VectorSpace C++ Library 419
Two Dimensional Problems
and all other stresses and strains as zero. The z-displacement of node number 1 is fixed to zero to prevent the z-
translation of the rigid body motion. For the patch test we take the element size “h” as 0.05, 0.1, 0.2, 0.4, and 0.8.
The Program Listing 4•18 implements the axisymmetrical patch test (Eq. 4•230, and Eq. 4•233 with definition of
D
0
in Eq. 4•234). The results of nodal radial displacement (u) at node number 1 and 4 are all exact under the 2 2
integration scheme. The macro definition “__TEST_SELECTIVE_REDUCED_INTEGRATION” can be set at
compiled time for the selective reduced integration. The radial displacement on nodes 1 and 4 under the selective
reduced integration are shown in TABLE 4•5. It shows that when the element size “h” goes down, the radial dis-
placement solution converges quickly to the assumed solution
We notice that the implementation of the B-matrix for the axisymmetrical problem is implemented according to
Eq. 4•230 as
1 H0 W_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, nsd, Nx),
2 Wr, Wz, B, R;
3 Wr &= W_x[0][0]; Wz &= W_x[0][1];
4 R &= (H0)X[0];
5 B &= (C0(0.0) || ~Wz ) & //
6 (~Wr || C0(0.0) ) &
7 (~((H0)N)/R || C0(0.0) ) &
8 (~Wz || ~Wr );
Lines 5-8 use matrix concatenation operation to capture the semantics of B-matrix directly.
Element size “h” Node # 1, and 4
0.05 2.0
0.1 2.0
0.2 2.00003
0.4 2.00049
0.8 2.01114
TABLE 4•5. The radial displacement for axisymmetrical problem.
×
B
a
0
∂N
a
∂z
---------
∂N
a
∂r
--------- 0
N
a
r
------ 0
∂N
a
∂z
---------
∂N
a
∂r
---------
=
Finite Element Method Primer
420 Workbook of Applications in VectorSpace C++ Library
Chapter 4
#include "include\fe.h"
static const double E_ = 1.0;
static const double v_ = 0.0;
static const double lambda_=v_*E_/((1+v_)*(1-2*v_));
static const double mu_ = E_/(2*(1+v_));
static const double K_ = lambda_ + 2.0/3.0 * mu_;
static const double h_=0.8;
static const double r_=1.0;
static const double PI_=3.14159265359;
Omega_h::Omega_h() {
double v[2];
Node* node;
int ena[4];
Omega_eh* elem;
v[0] = r_-h_; v[1] = 0.0;
node = new Node(0, 2, v);
the_node_array.add(node);
v[0] = r_;
node = new Node(1, 2, v);
the_node_array.add(node);
v[0] = r_+h_;
node = new Node(2, 2, v);
the_node_array.add(node);
v[0] = r_-h_; v[1] = h_;
node = new Node(3, 2, v);
the_node_array.add(node);
v[0] = r_;
node = new Node(4, 2, v);
the_node_array.add(node);
v[0] = r_+h_;
node = new Node(5, 2, v); the_node_array.add(node);
ena[0] = 0; ena[1] = 1; ena[2] = 4; ena[3] = 3;
elem = new Omega_eh(0, 0, 0, 4, ena);
the_omega_eh_array.add(elem);
ena[0] = 1; ena[1] = 2; ena[2] = 5; ena[3] = 4;
elem = new Omega_eh(1, 0, 0, 4, ena);
the_omega_eh_array.add(elem); }
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
__initialization(df, omega_h);
the_gh_array[node_order(1)](1) = gh_on_Gamma_h::Dirichlet;
double sigma_r, r, f_r;
sigma_r = 2.0; r = 1.0-h_;
f_r = -2.0*PI_*r*h_*sigma_r;
the_gh_array[node_order(0)][0] = f_r / 2.0;
the_gh_array[node_order(3)][0] = f_r / 2.0;
r = 1.0+h_;
f_r = 2.0*PI_*r*h_*sigma_r;
the_gh_array[node_order(2)][0] = f_r / 2.0;
the_gh_array[node_order(5)][0] = f_r / 2.0;
}
class ElasticAxisymmetricQ4 : public Element_Formulation {
public:
ElasticAxisymmetricQ4(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
ElasticAxisymmetricQ4(int, Global_Discretization&);
};
Element_Formulation* ElasticAxisymmetricQ4::make(int en, Global_Discretization& gd) {
return new ElasticAxisymmetricQ4(en,gd);
}
Workbook of Applications in VectorSpace C++ Library 421
Two Dimensional Problems
Listing 4•18 Axisymmetrical patch test (project workspace file “fe.dsw”, project
“axisymmetrical_patch_test” with Macro definition
ElasticAxisymmetricQ4::ElasticAxisymmetricQ4(int en, Global_Discretization& gd) :
Element_Formulation(en, gd) {
Quadrature qp(2, 4);
H1 Z(2, (double*)0, qp), Zai, Eta,
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int, int, Quadrature", 4, 2, qp);
Zai &= Z[0]; Eta &= Z[1];
N[0] = (1-Zai)*(1-Eta)/4; N[1] = (1+Zai)*(1-Eta)/4;
N[2] = (1+Zai)*(1+Eta)/4; N[3] = (1-Zai)*(1+Eta)/4;
H1 X = N*xl;
H0 Nx = d(N) * d(X).inverse();
J dV(d(X).det());
H0 W_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, nsd, Nx), Wr, Wz, B, R;
Wr &= W_x[0][0]; Wz &= W_x[0][1]; R &= (H0)X[0];
B &= (C0(0.0) || ~Wz ) &
(~Wr || C0(0.0)) &
(~((H0)N)/R || C0(0.0)) &
(~Wz || ~Wr );
double d_0[4][4]={ {2.0, 0.0,0.0,0.0},
{0.0, 2.0,0.0,0.0},
{0.0, 0.0,2.0,0.0},
{0.0, 0.0,0.0,1.0} };
C0 D_0 = MATRIX("int, int, const double*", 4, 4, d_0[0]);
double m_0[4] = {1.0, 1.0, 1.0, 0.0};
C0 m = VECTOR("int, const double*", 4, m_0);
C0 stiff_dev = 2.0*PI_*(((~B) * ((mu_*(D_0-2.0/3.0*(m%m))) * B) * R) | dV);
Quadrature qp1(2, 1);
H1 z(2, (double*)0, qp1), zai, eta,
n = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 4, 2, qp1);
zai &= z[0]; eta &= z[1];
n[0] = (1-zai)*(1-eta)/4; n[1] = (1+zai)*(1-eta)/4;
n[2] = (1+zai)*(1+eta)/4; n[3] = (1-zai)*(1+eta)/4;
H1 x = n*xl;
H0 nx = d(n) * d(x).inverse();
J dv(d(x).det());
H0 w_x= INTEGRABLE_SUBMATRIX("int, int, H0&", 1, nsd, nx), wr, wz, b, r;
wr &= w_x[0][0]; wz &= w_x[0][1]; r &= (H0)x[0];
b &= (C0(0.0) || ~wz ) &
(~wr || C0(0.0)) &
(~((H0)n)/r || C0(0.0)) &
(~wz|| ~wr );
C0 stiff_vol = 2.0*PI_*(((~b) * ((K_*(m%m)) * b) * r) | dv);
stiff &= stiff_vol + stiff_dev;
}
Element_Formulation* Element_Formulation::type_list = 0;
Element_Type_Register element_type_register_instance;
static ElasticAxisymmetricQ4 elasticaxisymmetricq4_instance(element_type_register_instance);
int main() {
int ndf = 2; Omega_h oh; gh_on_Gamma_h gh(ndf, oh); U_h uh(ndf, oh);
Global_Discretization gd(oh, gh, uh);
Matrix_Representation mr(gd);
mr.assembly();
C0 u = ((C0)(mr.rhs())) / ((C0)(mr.lhs()));
gd.u_h() = u; gd.u_h() = gd.gh_on_gamma_h();
cout << gd.u_h() << endl;
return 0;
}
m = [1, 1, 1, 0]
T
2 2 integration on
1-point integration on
B
a
0
∂N
a
∂z
---------
∂N
a
∂r
--------- 0
N
a
r
------ 0
∂N
a
∂z
---------
∂N
a
∂r
---------
=
D
0
2 0 0 0
0 2 0 0
0 0 2 0
0 0 0 1
=
×
k
dev
e
i
T
B
a
T
µ D
0
2
3
---m m ⊗ –
¸ ,
¸ _
B
b
dΩ


e
j
=
k
vol
e
i
T
B
a
T
K m m ⊗ ( )B
b
dΩe
j


=
Finite Element Method Primer
422 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Higher-Order Patch Test
In the patch Test A, B, and C, the assumed solution is linear. We now study when the assumed solution is
quadratic, which may reveal additional problems. In the first set of problem, the element shape sensitivity effect
is considered for eight-nodes serendipity element and nine-nodes Lagrangian element. This is followed by
robustness of an element formulation when the material becomes incompressible. The successfulness of the
selective reduced integration will be evident.
Shape Sensibility: Consider two quadratic elements. Either eight-nodes or nine-nodes elements as shown in Fig-
ure 4•52. The common edge of the two elements is slanted with the distortion, away from axes of Cartesian
coordinates, denoted as “d”, and shown in Figure 4•52.
There is no new program implementation needed for the higher-order patch test. The project
“higher_order_patch_test” implemented program for the present test. The eight-nodes and nine-nodes elements
are activated by setting macro definition “__TEST_Q8” and “__TEST_Q9”, respectively. The distortion factor
is a static constant “d_” in the very beginning of the program. The uniform reduced integration can be achieved
by setting all qaudrature point to 2 2 in the program. The tip deflection on the middle point of the left edge is
listed in TABLE 4•6.
Distortion Integration Points 8-nodes Quadrilateral 9-nodes Quadrilateral
d=0 3 3 0.750000 0.750000
d=0 2 2 0.750000 0.750000
d=1 3 3 0.744849 0.750000
d=1 2 2 0.750000 0.788531
d=2 3 3 0.666839 0.750000
d=2 2 2 0.750000 0.676616
TABLE 4•6. Tip deflection of 8-nodes and 9-nodes quadrilaterals.
Figure 4•52 Beam subject to bending moement on the left. Three amount of element
distortion away from rectangular shape (d = 0).
d
15
-15
10
2
d = 0
d = 1
d = 2
d
E = 10
3
, ν = 0.3
×
×
×
×
×
×
×
Workbook of Applications in VectorSpace C++ Library 423
Two Dimensional Problems
The exact solution is “0.75”. The standard integration scheme (3 3) for 8-nodes and 9-nodes elements with no
distortion both match the exact solution. When the distortion occurs, the accuracy of the 8-nodes element deteri-
orates fast when 9-nodes element is still capable of producing the exact solution. This is because the 9-nodes ele-
ment is capable of reproducing arbitrary quadratic displacement of straight-edged quadrilateral while the 8-nodes
element is not.
1

With uniform reduced integration (2 2), the reverse is true. Each integration point contribute to 3 indepen-
dent relations from the definitions of 3 strain equations. For the present case 2 2 uniform reduced integration
gives 3 8 = 24 independent relations. The 9-nodes element has “total degree of freedom” = 15(nodes) 2(dofs)-
4(constraints) = 26 > 24. Therefore, under this integration scheme the 9-nodes element is rank deficient. The
accuracy of the solution collapses fast with the increasing amount of the distortion. For the 8-nodes element, the
total degree of freedoms is 13 2-4 = 22 < 24, which is not only rank sufficient, but also less stiff compared to 8-
nodes element with standard integration scheme. Therefore, it produces better result. The displacement formula-
tion usually leads to over-estimated stiffness. The lowest order of numerical integration required for convergence
relaxes the stiffness and produces improved results.
2
Convergence of bilinear 4-node element: We show the convergence of bilinear 4-node element at (1) Poisson ratio
ν = 0.3 in plane stress and (2) ν = 0.4999 in plane strain (with the same boundary value problem in Figure 4•52).
The options of (a) the selective reduce integration on the shear term of the deviatoric stiffness and (b) the volu-
metric stiffness are also tested. The same problem is divided with successively finer meshes, and is shown in Fig-
ure 4•53. The test suite is implemented in project “higher_order_q4” in project workspace file “fe.dsw”. For total
element number greater than 8, the macro definitions “__TEST_Q4_32”, “__TEST_Q4_128”, and
“__TEST_Q4_512”, with the last numbers indicate the total element number, can be set at compile time. For the
selective reduced integration on the offending shear terms and dilatational term in incompressible materials, the
corresponding macro definitions are “__SHEAR_SELECTIVE_REDUCED_INTEGRATION” and
“__INCOMPRESSIBLE_ SELECTIVE_REDUCED_INTEGRATION”.
The results with various combinations of the options are shown in TABLE 4•7. For Poisson ratio ν = 0.3, in
plane stress, the convergence is clear with increasing number of element used in the computation. The successive
results agree on more digits after the decimal points. This convergence is guaranteed by the patch test for the 4-
nodes bi-linear element, since it pass the consistency and stability parts of the patch test. Both the full integration
and selective reduced integration on the offending shear treatment converge to exact solution of 0.75. For ν =
0.4999, the nearly incompressible condition, in plane strain case, the solution shows significant locking without
signs of convergence, when applied with the full integration. The solution and its convergence are obtainable
with the selective reduced integration schemes as shown in the last two columns, which both converge to value
of ~0.56 comparing to “0.5625” in mixed u-p formulation (ν = 0.5 in Chapter 5).
1. p. 167-169 in Zienkiewicz, O.C., and R.L. Taylor, 1989, “The finite element method: basic formulation and linear prob-
lems”, McGraw-Hill, London., UK.
2. p. 164-165 in Bathe, K.-J. and W.L. Wilson, 1976, “ Numerical method in finite element analysis”, Prentice-Hall, Inc.,
Englewood Cliffs, New Jersey.
×
×
×
× ×
×
Finite Element Method Primer
424 Workbook of Applications in VectorSpace C++ Library
Chapter 4
_
Number of
E.lements
ν = 0.3
(standard)
ν = 0.3
(selective reduced
on shear)
ν = 0.4999
(standard)
ν = 0.4999
(selective reduced
on dilatation)
ν = 0.4999
(selective reduced on
shear & dilatation)
8 0.6920159 1.097860 0.00271635 0.666701 0.964387
32 0.7295910 0.839392 0.00799228 0.595018 0.655087
128 0.7443220 0.772274 0.02823080 0.570704 0.584802
512 0.7485400 0.755571 0.09621180 0.564732 0.568095
TABLE 4•7. Convergence of four node bi-linear element with selective reduced integration on
offending shear terms to prevent shear-locking ν = 0.3 in plane stress case, and selective reduced
integration on volumetric terms when the Possion ratios ν = 0.4999 in plane strain case to prevent
dilatational locking.
Figure 4•53 Mesh refinement of 4-nodes quadrilateral element.
8 elements
32 elements
128 elements
512 elements
Workbook of Applications in VectorSpace C++ Library 425
Two Dimensional Problems
4.3.5 Stokes Flow
For a fluid particle with density ρ and velocity u relative to an inertial frame of reference. The Newton’s sec-
ond law of motion requires the linear momentum of the fluid particle is equal to the forces applied to it.
Eq. 4•236
where divergence of interal stresses, div σ, equals the external surface force, and f is the body force. The Du/Dt
in the left-hand-side is the fluid particle in Lagragian (material) description, in which u(x, t) can be differentiated
with respect to time “t” (by first applying the Lebniz rule, i.e., d(xy) = x dy + y dx, and then the chain rule, d f(x)
/ dt = (df / dx) (dx / dt), on the second term of the Lebniz rule)
Eq. 4•237
where we have applied the definitions of the velocity, u ∂x/∂t, and the velocity gradient, grad u ∂u/∂x. The
stress in the first term of the right-hand-side of Eq. 4•236 can be expressed as in Eq. 4•146 that
where p is the pressure, I is the unit tensor, and τ is the viscous stress. The constitutive equations is
Eq. 4•238
where µ is the fluid viscosity, and is the second viscosity (this term gives the deviatoric stress caused by the
volumetirc deformation which is a process attributed to molecular relaxation). For monatomic gas = -2µ/3,
and it can be proved as the lower bound for thermodynamically. In most applications, , is nearly
completely negligible compared to the pressure, “p”.
A popular treatment for the incompressible condition is to use penalty method where the pressure variable is
eliminated by taking
Eq. 4•239
Now λ and µ are equivalent to the Lamé constants in elasticity. As discussed earlier (see page 409), near the
incompressible condition >> µ. In the penalty method in the stokes problem, the penalty parameter, λ, is
usually taken as
λ = (10
7
~10
10
) µ Eq. 4•240
to approximate the nearly incompressible condition.
1
Substituting Eq. 4•237 and viscous stress of Eq. 4•238 into
Eq. 4•236, we have the Navier-Stokes equation
1. p.520 in Zienkiewicz and R.L. Taylor, 1991, “The finite element method”, 4th ed., vol. 2. McGraw-Hill, Inc., UK.
ρ
Du
Dt
-------- div σ f + =
Du x t , ( )
Dt
--------------------
∂u
∂t
------
∂u
∂x
------
∂x
∂t
------ +
∂u
∂t
------ u grad u • + = =
≡ ≡
σ p I – τ + =
τ 2µ def u λ' Idiv u + =
λ'
λ'
λ' λ' div u
p λ – div u =
K λ ≈
Finite Element Method Primer
426 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Eq. 4•241
We have dropped out the second viscosity and use the identity that “div(p I) = grad p”. For steady incom-
pressible viscous fluid, the Navier-Stokes equation simplifies to
Eq. 4•242
From Eq. 4•242, the Reynolds number (denoted as Re) is the dynamic similarity of the inertia force
“ “ to the viscous force “div(2µ def u)” as
1
Eq. 4•243
At very low Reynolds number (Re << 1) the inertia force is negligible compared to the viscous force. The Eq.
4•242 can be simplified to
Eq. 4•244
Therefore, the resultant equation is completely identical to Eq. 4•140 with the constitutive equation of Eq. 4•146
and Eq. 4•147 for elasticity. The physical interpretation is different in that instead of regarding u as the displace-
ment, it is the velocity in the stokes flow. µ now plays the role of fluid viscosity instead of the shear modulus G
in elasticity. λ is now the penalty parameter we take λ = 10
8
µ, and certainly with the selective reduced integra-
tion for the volumetric term, in the computation. The finite element formulation in the last section for plane elas-
ticity can be applied to the stokes flow problem without modification. Considering the B-matrix formulation for
plane elasticity
, and Eq. 4•245
Since at the incompressible limit, , and λ = 10
8
µ for the penalty method, Eq. 4•245 becomes
2
, and where Eq. 4•246
1. p. 97 in Tritton, D.J., 1988, “ Physical fluid dynamics”, 2nd ed., Oxford University Press, Oxford, UK.
2. see Hughes, T.J.R., W.K. Liu, and A. Brooks, 1979, “Review of finite element analysis of incompressible viscous flows
by the penalty function formulation”, Journal, of Computational Physics, vol. 30, no. 1, p. 1-60.
ρ
∂u
∂t
------ ρu grad u grad p + • + div 2µ def u ( ) f + =
λ'
ρu grad u grad p + • div 2µ def u ( ) f + =
ρu grad u •
ρu grad u •
div 2µ def u ( )
------------------------------------------
ρUL
µ
----------- Re ≡ ≈
grad p div 2µ def u ( ) f + =
k
dev
e
i
T
B
a
T
µ D
0
2
3
---m m ⊗ –
¸ ,
¸ _
B
b
dΩ


e
j
= k
vol
e
i
T
B
a
T
K m m ⊗ ( )B
b
dΩe
j


=
λ K ≈
k
dev
e
i
T
B
a
T
D
µ
B
b
dΩ


e
j
≅ k
vol
e
i
T
B
a
T
D
λ
B
b
dΩe
j



D
λ
λ λ 0
λ λ 0
0 0 0
= D
µ
,
2µ 0 0
0 2µ 0
0 0 µ
=
Workbook of Applications in VectorSpace C++ Library 427
Two Dimensional Problems
Plane Couette-Poiseuille Flow
Consider a plane uni-directional flow (v = w = 0) drives by both pressure gradient (the Poiseuille flow) and
relative motion (U) of two bounding plates (the Couette flow) as shown in Figure 4•54 . The distance between
two rigid plates is “d” with the pressure gradient from the entrance of the flow to the exit as -∇p = G. The viscos-
ity of the fluid is µ. The velocity profile can be expressed as a function of y coordinate
1
Eq. 4•247
This solution can be derived from Eq. 4•244 from the superposition of two solutions of the viscous flow induced
by the pressure gradient and by the bounding plates separately. That is the first term corresponding to the Poi-
seuille flow caused by the applied horizontal pressure gradient, the second term corresponding to the Couette
flow induced by the relative motion of the two bounding plates. In these test cases, the Couette flow provides an
assumed linear solution, and the Poiseuille flow provides an assumed higher-order (quadratic) solution.
Program Listing 4•19, in the project “plane_couette_poiseuille_flow” in project workspace file “fe.dsw”, is
implemented for these tests. To emphasize its relation to plane elasticity, we use “elasticq9.cpp” as a separate
compilation unit, as a dependent source file for this project. The “elasticq9.cpp” is the implementation very close
to of Lagrangian 9-node element for plane elasticity.
The plane Couette flow can be activated by setting macro definition “__TEST_PLANE_COUETTE_FLOW”
and the plane Poiseuille flow can be activated by setting macro definition “__TEST_PLANE_POISEUILLE_
FLOW”. The default is a combined flow with both pressure gradient applied on the entrance and relative motion
of bounding plates. The results of the computation are shown in Figure 4•55. The finite element solutions are
shown in dashed curves with arrows to indicate the velocity profiles in the middle of the channel to avoid the
entrance and exit effects. The exact solution are shown in solid curves. We notice that the solution for the plane
Poiseuille flow, quadratic in nature, is less accurate compared to the solution for the plane Couette flow, which is
linear.
1. p. 182 in Batchelor, G.K., 1967, “An introduction to fluid dynamics”, Cambridge University Press, UK.
u y ( )
G

------y d y – ( )
Uy
d
------- + =
Figure 4•54 Plane Couette-Poiseuille flow problem.
p = GL = 40. p · 0
U = 1
L = 10
µ · 1
d = 1
Finite Element Method Primer
428 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Figure 4•55 Plane Couette flow and plane Poiseuille flow. The exact solutions are shown in
solid curves the finie element solutions are shown in dashed curves with arrows. The finite
element solutions are velocity profiles taken from the middle of the channel to avoid
entrance and exit effect.
0.1 0.2 0.3 0.4 0.5
0.2
0.4
0.6
0.8
1
Plane Poiseuille Flow
0.2 0.4 0.6 0.8 1
0.2
0.4
0.6
0.8
1
Plane Couette Flow
+
0.2 0.4 0.6 0.8 1
0.2
0.4
0.6
0.8
1
Plane Couette-Poiseuille Flow
Workbook of Applications in VectorSpace C++ Library 429
Two Dimensional Problems
#include "include\fe.h"
static const double mu_ = 1.0; static const double lambda_ = 1.0e8*mu_;
class ElasticQ9 : public Element_Formulation { public:
ElasticQ9(Element_Type_Register);
Element_Formulation *make(int, Global_Discretization&);
ElasticQ9(int, Global_Discretization&); };
ElasticQ9::ElasticQ9(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation* ElasticQ9::make(int en, Global_Discretization& gd) {
return new ElasticQ9(en,gd); }
ElasticQ9::ElasticQ9(int en, Global_Discretization& gd) : Element_Formulation(en, gd) {
Quadrature qp(2, 9);
H1 Z(2, (double*)0, qp), Zai, Eta,
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int, int, Quadrature", 9, 2, qp);
Zai &= Z[0]; Eta &= Z[1];
N[0] = (1-Zai)*(1-Eta)/4; N[1] = (1+Zai)*(1-Eta)/4;
N[2] = (1+Zai)*(1+Eta)/4; N[3] = (1-Zai)*(1+Eta)/4;
N[8] = (1-Zai.pow(2))*(1-Eta.pow(2));
N[0] -= N[8]/4; N[1] -= N[8]/4; N[2] -= N[8]/4; N[3] -= N[8]/4;
N[4] = ((1-Zai.pow(2))*(1-Eta)-N[8])/2; N[5] = ((1-Eta.pow(2))*(1+Zai)-N[8])/2;
N[6] = ((1-Zai.pow(2))*(1+Eta)-N[8])/2; N[7] = ((1-Eta.pow(2))*(1-Zai)-N[8])/2;
N[0] -= (N[4]+N[7])/2; N[1] -= (N[4]+N[5])/2; N[2] -= (N[5]+N[6])/2; N[3] -= (N[6]+N[7])/2;
Quadrature qp1(2, 4);
H1 z(2, (double*)0, qp1), zai, eta,
n = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 9, 2, qp1);
zai &= z[0]; eta &= z[1];
n[0] = (1-zai)*(1-eta)/4; n[1] = (1+zai)*(1-eta)/4;
n[2] = (1+zai)*(1+eta)/4; n[3] = (1-zai)*(1+eta)/4;
n[8] = (1-zai.pow(2))*(1-eta.pow(2)); n[0] -= n[8]/4; n[1] -= n[8]/4; n[2] -= n[8]/4; n[3] -= n[8]/4;
n[4] = ((1-zai.pow(2))*(1-eta)-n[8])/2; n[5] = ((1-eta.pow(2))*(1+zai)-n[8])/2;
n[6] = ((1-zai.pow(2))*(1+eta)-n[8])/2; n[7] = ((1-eta.pow(2))*(1-zai)-n[8])/2;
n[0] -= (n[4]+n[7])/2; n[1] -= (n[4]+n[5])/2; n[2] -= (n[5]+n[6])/2; n[3] -= (n[6]+n[7])/2;
H1 X = N*xl; H0 Nx = d(N) * d(X).inverse(); J dV(d(X).det());
H1 x = n*xl; H0 nx = d(n) * d(x).inverse(); J dv(d(x).det());
#if defined(__TEST_B_MATRIX_FORMULATION)
double d_lambda[3][3] = { {lambda_, lambda_, 0.0}, {lambda_, lambda_, 0.0}, {0.0, 0.0, 0.0} };
C0 D_lambda = MATRIX("int, int, const double*", 3, 3, d_lambda[0]);
H0 w_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, nsd, nx), wx, wy, b;
wx &= w_x[0][0]; wy &= w_x[0][1]; b &= (~wx|| C0(0.0)) & (C0(0.0) || ~wy ) & (~wy || ~wx);
C0 stiff_vol = ((~b) * (D_lambda * b)) | dv;
double d_mu[3][3] = { {2*mu_, 0.0, 0.0}, {0.0, 2*mu_, 0.0}, {0.0, 0.0, mu_} };
C0 D_mu = MATRIX("int, int, const double*", 3, 3, d_mu[0]);
H0 W_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, nsd, Nx), Wx, Wy, B;
Wx &=W_x[0][0]; Wy &=W_x[0][1]; B &= (~Wx|| C0(0.0)) & (C0(0.0) || ~Wy) & (~Wy|| ~Wx );
C0 stiff_dev = ((~B) * (D_mu * B)) | dV;
#else
C0 e = BASIS("int", ndf), E = BASIS("int", nen), U = (e%e)*(E%E);
H0 w_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, nsd, nx), wx, wy;
wx &= w_x[0][0]; wy &= w_x[0][1];
C0 stiff_vol = lambda_* (
+( wx*~wx*U[0][0]+wx*~wy*U[0][1] +wy*~wx*U[1][0]+wy*~wy*U[1][1] ) | dv);
H0 W_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, nsd, Nx), Wx, Wy;
Wx &= W_x[0][0]; Wy &= W_x[0][1];
C0 stiff_dev = mu_* (
+( ((2*Wx*~Wx)+(Wy*~Wy))*((e[0]%e[0])*(E%E))+(Wy*~Wx) *((e[0]%e[1])*(E%E))
+(Wx*~Wy) *((e[1]%e[0])*(E%E))+((2*Wy*~Wy)+(Wx*~Wx))*((e[1]%e[1])*(E%E)) )
| dV);
#endif
stiff &= stiff_vol + stiff_dev;
}
a separate source file “elasticq9.cpp”
taken from plane elasticity problem
penalty parameter is λ = 10
8
µ
3x3 integration
9-node Lagrangian shape functions
2x2 reduced integration
9-node Lagrangian shape functions
B-matrix formulation for incompressib-
lility
,
standard λ−µ formulation
D
λ
λ λ 0
λ λ 0
0 0 0
= D
µ
,
2µ 0 0
0 2µ 0
0 0 µ
=
k
dev
e
i
T
B
a
T
D
µ
B
b
dΩ


e
j

k
vol
e
i
T
B
a
T
D
λ
B
b
dΩe
j



Finite Element Method Primer
430 Workbook of Applications in VectorSpace C++ Library
Chapter 4
#include "include\fe.h"
static const int row_node_no = 7; static const int col_node_no = 9;
static const int row_element_no = (row_node_no-1)/2;
static const int col_element_no = (col_node_no-1)/2;
static const int row_segment_no = row_node_no-1;
static const double L_ = 10.0; static const double c_ = 0.125;
static const double h_e_ = L_/((double)row_segment_no); static const double mu_ = 1.0;
static const double lambda_ = 1.0e8*mu_; static const double p_ = 40.0;
Omega_h::Omega_h() { double v[2]; int ena[9]; Omega_eh *elem;
for(int i = 0; i < col_node_no; i++) for(int j = 0; j < row_node_no; j++) {
int nn = i*row_node_no+j; v[0] = ((double)j)*h_e_; v[1] = ((double)i)*c_;
Node* node = new Node(nn, 2, v); the_node_array.add(node); }
for(int i = 0; i < col_element_no; i++) for(int j = 0; j < row_element_no; j++) {
int en = i * row_element_no + j, fnn = i * 2 * row_node_no + j * 2;
ena[0] = fnn; ena[1] = fnn + 2; ena[2] = ena[1] + 2*row_node_no;
ena[3] = ena[0] + 2*row_node_no; ena[4] = ena[0] + 1; ena[5] = ena[1] + row_node_no;
ena[6] = ena[3] + 1; ena[7] = ena[0] + row_node_no; ena[8] = ena[4]+row_node_no;
elem = new Omega_eh(en, 0, 0, 9, ena); the_omega_eh_array.add(elem); } }
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) { __initialization(df, omega_h);
#if defined(__TEST_PLANE_POISEUILLE_FLOW)
for(int i = 0; i < row_node_no; i++) {
the_gh_array[node_order(i)](0) = the_gh_array[node_order(i)](1) =
the_gh_array[node_order((col_node_no-1)*row_node_no+i)](0) =
the_gh_array[node_order((col_node_no-1)*row_node_no+i)](1)=gh_on_Gamma_h::Dirichlet;}
double weight[9] = { 0.0, 3.0/2.0, 1.0, 1.0, 1.0, 1.0, 1.0, 3.0/2.0, 0.0 };
for(int i = 1; i < col_node_no -1; i++) {
the_gh_array[node_order(i*row_node_no)](0) = gh_on_Gamma_h::Neumann;
the_gh_array[node_order(i*row_node_no)][0] = p_*weight[i]*c_; }
#elif defined(__TEST_PLANE_COUETTE_FLOW)
for(int i = 0; i < row_node_no; i++) {
the_gh_array[node_order(i)](0) = the_gh_array[node_order(i)](1) =
the_gh_array[node_order((col_node_no-1)*row_node_no+i)](0) =
the_gh_array[node_order((col_node_no-1)*row_node_no+i)](1) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order((col_node_no-1)*row_node_no+i)][0] = 1.0; }
#else
for(int i = 0; i < row_node_no; i++) {
the_gh_array[node_order(i)](0) = the_gh_array[node_order(i)](1) =
the_gh_array[node_order((col_node_no-1)*row_node_no+i)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order((col_node_no-1)*row_node_no+i)](1) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order((col_node_no-1)*row_node_no+i)][0] = 1.0; }
double weight[9] = { 0.0, 3.0/2.0, 1.0, 1.0, 1.0, 1.0, 1.0, 3.0/2.0, 0.0 };
for(int i = 1; i < col_node_no -1; i++) {
the_gh_array[node_order(i*row_node_no)](0) = gh_on_Gamma_h::Neumann;
the_gh_array[node_order(i*row_node_no)][0] = p_*weight[i]*c_; }
#endif
}
class ElasticQ9 : public Element_Formulation {public:
ElasticQ9(Element_Type_Register); Element_Formulation *make(int, Global_Discretization&);
ElasticQ9(int, Global_Discretization&); };
Element_Formulation* Element_Formulation::type_list = 0;
Element_Type_Register element_type_register_instance;
static ElasticQ9 stokesq9_instance(element_type_register_instance);
int main() {
int ndf = 2; Omega_h oh; gh_on_Gamma_h gh(ndf, oh); U_h uh(ndf, oh);
Global_Discretization gd(oh, gh, uh); Matrix_Representation mr(gd);
mr.assembly(); C0 u = ((C0)(mr.rhs())) / ((C0)(mr.lhs()));
gd.u_h() = u; gd.u_h() = gd.gh_on_gamma_h(); cout << gd.u_h() << endl;
return 0; }
define nodes
define elements
B.C.
Poiseuille flow only
open integration rule (see Numerical
Reciepe)
Couette flow only
Poiseuille & Couette flow
declare “ElasticQ9” class
register “ElasticQ9” as element # 0
solution phase
Listing 4•19 Plane Couette-Poiseuille flow in project “plane_couette_poiseuille_flow”
Workbook of Applications in VectorSpace C++ Library 431
Two Dimensional Problems
Driven Cavity Flow
1
The stokes flow in a square cavity is shown in Figure 4•56a. The bottom and the two sides are rigid-walls.
The top is a boundary with velocity given as u(x) = 4(1-x)x. This velocity boundary condition causes a convect-
ing current in the cavity, which is known as forced convection. The top horizontal velocity boundary conditions
vanish at the two top corners, which are to avoid the difficulty in defining boundary conditions at these two cor-
ner nodes.
2
Program Listing 4•20, the project “square_cavity_flow” in project workspace file “fe.dsw”, is imple-
mented for this computation. Again, the “elasticq9.cpp” is a separate compilation unit for this project. The
penalty method (λ = 10
8
µ) is used with selective reduced integration and the 9-nodes Lagrangian quadrilateral
element. The velocity vectors are shown in Figure 4•56b.
1. p. 462-465 in J.N. Reddy, 1986, “Applied functional analysis and variational methods in engineering”, McGraw-Hill, Inc.,
New York.
2. such as corner node treatments described in p.231 in Hughes, T.J.R., “The finite element method: linear static and dynamic
finite element analysis”, Prentice-Hall, Inc., Englewood Cliffs, New Jersey.
Figure 4•56(a) Flow in square cavity with sixteen 9-nodes Lagrangian elements. (b) velocity
vectors.
u = 4(1-x)x
(a)
(b)
y
x
(0, 0) (1,0)
(0, 1)
(1, 1)
Finite Element Method Primer
432 Workbook of Applications in VectorSpace C++ Library
Chapter 4
#include "include\fe.h"
static const int row_node_no = 9; static const int col_node_no = 9;
static const int row_element_no = (row_node_no-1)/2;
static const int col_element_no = (col_node_no-1)/2;
static const double h_e_ = 1.0/((double)row_element_no*2);
static const double v_e_ = 1.0/((double)col_element_no*2);
static const double mu_ = 1.0; static const double lambda_ = 1.e8 * mu_;
EP::element_pattern EP::ep = EP::LAGRANGIAN_9_NODES;
Omega_h::Omega_h() {
double x[4][2] = {{0.0, 0.0}, {1.0, 0.0}, {1.0, 1.0}, {0.0, 1.0}};
int control_node_flag[4] = {1, 1, 1, 1};
block(this, row_node_no, col_node_no, 4, control_node_flag, x[0]);
}
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
__initialization(df, omega_h);
for(int i = 0; i < col_node_no; i++) {
the_gh_array[node_order((i+1)*row_node_no-1)](0) =
the_gh_array[node_order((i+1)*row_node_no-1)](1) = gh_on_Gamma_h::Dirichlet;
}
for(int i = 0; i < col_node_no; i++) {
the_gh_array[node_order(i*row_node_no)](0) =
the_gh_array[node_order(i*row_node_no)](1) = gh_on_Gamma_h::Dirichlet;
}
for(int i = 1; i < row_node_no-1; i++) {
the_gh_array[node_order(i)](0) =
the_gh_array[node_order(i)](1) = gh_on_Gamma_h::Dirichlet;
}
for(int i = 1; i < row_node_no-1; i++) {
int nn = (col_node_no-1)*row_node_no+i;
double x = ((double)i)*h_e_, u = 4.0 * (1.0-x) * x;
the_gh_array[node_order(nn)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(nn)][0] = u;
the_gh_array[node_order(nn)](1) = gh_on_Gamma_h::Dirichlet;
}
}
class ElasticQ9 : public Element_Formulation {
public:
ElasticQ9(Element_Type_Register);
Element_Formulation *make(int, Global_Discretization&);
ElasticQ9(int, Global_Discretization&);
};
Element_Formulation* Element_Formulation::type_list = 0;
Element_Type_Register element_type_register_instance;
static ElasticQ9 stokesq9_instance(element_type_register_instance);
int main() {
int ndf = 2; Omega_h oh;
gh_on_Gamma_h gh(ndf, oh);
U_h uh(ndf, oh);
Global_Discretization gd(oh, gh, uh);
Matrix_Representation mr(gd);
mr.assembly();
C0 u = ((C0)(mr.rhs())) / ((C0)(mr.lhs()));
gd.u_h() = u; gd.u_h() = gd.gh_on_gamma_h();
cout << gd.u_h() << endl;
return 0;
}
right; u = v = 0
left; u = v = 0
bottom; u = v = 0
top, forced B.C.; u = 4(1-x)x, v = 0
declare “ElasticQ9” class
register “ElasticQ9” as element # 0
solution phase
Listing 4•20 Driven cavity flow (in project: “square_cavity_flow” in project workspace file “fe.dsw”.).
Workbook of Applications in VectorSpace C++ Library 433
Two Dimensional Problems
4.3.6 Plate Bending Problems
The plate theory probably is only to interest the structural engineers. However, it has often been argued that
since the plate bending is the subject in the fourth-order differential equation that has been most extensively stud-
ied in finite element. The experiences gained in the plate finite element analysis may serve as an important exam-
ple for solving other fourth-order partial differential equation, such as the biharmonic equation of general
physical interests.
Basic Plate Theory
The basic assumption of the plate is that the plane sections, “fiber”, remain plane under deformation (see Fig-
ure 4•57a). Each lamina, which is parallel to the mid-surface, is assumed to be under plane stress; e.g., σ
z
= 0.
We also assumed, inconsistent to the plane stress assumption, that ε
z
is almost negligible, so w(x
α
, x
β
) does not
vary with thickness (z = [-t/2, t/2]). The displacements can be expressed as.
u
α
= u
α0

α
z, u
β
= u
β0

β
z, w = w
0
, or
u = u
0

x
z, v = v
0

y
z, w = w
0
Eq. 4•248
The membrane bending strains can be expressed as
1
Eq. 4•249
1. p.8 in Zienkiewicz and R.L. Taylor, 1991, “The finite element method”, 4th ed., vol. 2. McGraw-Hill, Inc., UK.
Figure 4•57 (a) the displacements of plate under deformation, (b) the shear forces (S
x
, S
y
),
the normal momenets (M
x
, M
y
), and the twisting moments (M
xy
, M
yx
) of a plate.
θ
α
γ
α
: shear strain
w = w
0
u
α
= u
α0

α
z
fiber
midsurface
M
y
M
x
M
yx
M
xy
S
y
S
x
(a) (b)
ε
ε
x
ε
y
γ
xy
z

∂x
------ 0
0

∂y
------

∂y
------

∂x
------
θ
x
θ
y
– zLθ – = = =
Finite Element Method Primer
434 Workbook of Applications in VectorSpace C++ Library
Chapter 4
and the transverse shear strains [γ
x
, γ
y
] as
Eq. 4•250
From the Figure 4•57b, the normal moments (M
x
, M
y
) and the twisting moment (M
xy
) are
Eq. 4•251
where D, assumed plane stress, is defined as
Eq. 4•252
The shear forces [S
x
, S
y
] are
Eq. 4•253
where α = βGt, and the correction factor is for rectangular homogeneous section with parabolic shear
stress distribution.
Parallel to the equilibrium equations, Eq. 4•26 and Eq. 4•27 for 1-D beam bending problem, we have in plate
bending problem
Eq. 4•254
or we can express their components explicitly in matrix form as
Eq. 4•255
γ
γ
x
γ
y
θ
x
θ
y

∂w
∂x
-------
∂w
∂y
-------
+ θ – ∇w + = = =
M
M
x
M
y
M
xy
σ
x
σ
y
τ
xy
z z d
t
2
--- –
t
2
---

– DLθ = = =
D
Et
3
12 1 ν
2
– ( )
-------------------------
1 ν 0
ν 1 0
0 0
1 ν –
2
------------
=
S
S
x
S
y
βGt θ – ∇w + ( ) α θ – ∇w + ( ) ≡ = =
β
5
6
--- =
L
T
M S + 0 = and ∇
T
S q + , 0 =

∂x
------ 0

∂y
------
0

∂y
------

∂x
------
M
x
M
y
M
xy
S
x
S
y
+
0
0
and

∂x
------

∂y
------
S
x
S
y
q
x
q
y
+ ,
0
0
= =
Workbook of Applications in VectorSpace C++ Library 435
Two Dimensional Problems
Kirchhoff (Thin-) Plate Theory and Finite Element Formulation—C
1
Continuity Requirement
In thin plate theory, we assume that the fiber remains normal to the mid-surface during deformation.There-
fore, the transverse shear strains are all zero. That is, , and from Eq. 4•250, we identify
Eq. 4•256
Substituting first part of Eq. 4•254 into the second part of it, we get
Eq. 4•257
Then, use Eq. 4•251 to substitute M in Eq. 4•257, and substitute θ, with thin plate assumption, in Eq.
4•256, we get
Eq. 4•258
From the definition of operators L and ∇, we have the combined operator “L∇” as
Eq. 4•259
For constant D, the Eq. 4•258 becomes the well-known classical biharmonic equation
1
Eq. 4•260
The homogeneous solution for a simply supported rectangular plate with lengths of “a” and “b” has the simple
form of
Eq. 4•261
The finite element formulation is obtained by substituting element shape function (N
a
) into Eq. 4•256 to Eq.
4•259. The B-matrix is defined as
Eq. 4•262
1. e.g., Airy’s stress function satisfies the biharmonic equation as described in p.32, and p.538 in Timoshenko, S.P., and J.N.
Goodier, 1970, “ Theory of elasticity”, 3rd ed., McGraw-Hill Book Company.
γ 0 =
θ ∇w =
∇ –
T
L
T
M q + 0 =
θ ∇w =
L∇ ( )
T
DL∇w q – 0 =
L∇

∂x
------ 0
0

∂y
------

∂y
------

∂x
------

∂x
------

∂y
------

2
∂x
2
--------

2
∂y
2
--------
2

2
∂x∂y
-------------
= =

4
w
∂x
4
---------- 2

4
w
∂x
2
∂y
2
------------------

4
w
∂y
4
---------- q
12 1 ν
2
– ( )
Et
3
------------------------- – + + 0 =
w
mπx
a
-----------
¸ ,
¸ _
nπy
b
----------
¸ ,
¸ _
cos cos = where m n , , 1 3 5 … , , , =
B
a
L∇ ( )N
a
=
Finite Element Method Primer
436 Workbook of Applications in VectorSpace C++ Library
Chapter 4
From Eq. 4•251 and Eq. 4•256, we have the moment vector as
Eq. 4•263
where is the nodal deflection vector. The element stiffness matrix has no difference from Eq. 4•173; i.e.,
, with p = ndf (a-1) + i, and q = ndf (b-1)+j Eq. 4•264
Nonconforming Rectangular Plate Element (12 degrees of freedom)
The nodal variables for this four-node rectangular element is
Eq. 4•265
where
Eq. 4•266
The nonconforming element defines a 12-terms polynomial for the deflection “w” as
w = α
0
+ α
1
x + α
2
y + α
3
x
2
+ α
4
xy + α
5
y
2
+
α
6
x
3
+ α
7
x
2
y + α
8
xy
2
+ α
9
y
3
+ α
10
x
3
y + α
11
xy
3

Eq. 4•267
where
Eq. 4•268
Notice that the polynomial is not complete up to the third-order. For each of four nodes on the corner of the rect-
angle (a = 0, 1, 2, 3), we have twelve equations
Eq. 4•269
M
e
h
DB
a
w
ˆ
e
a
=
w
ˆ
e
a
k
e
pq
k
e
i aj b
e
i
T
B
a
T
DB
b
dΩe
j


= =
u
ˆ
e
a
w
a
θ
ˆ
xa
θ
ˆ
ya

θ
ˆ
xa
∂w
∂y
------- –
¸ ,
¸ _
a
= and θ
ˆ
ya
,
∂w
∂x
-------
¸ ,
¸ _
a
=
Pα ≡
P 1 x y x
2
xy y
2
x
3
x
2
y xy
2
y
3
x
3
y xy
3
=
w
a
θ
ˆ
xa
θ
ˆ
ya
α
0
α
1
x
a
α
2
y
a
α
3
x
a
2
α
4
x
a
y
a
α
5
y
a
2
α
6
x
a
3
α
7
x
a
2
y
a
α
8
x
a
y
a
2
α
9
y
a
3
α
10
x
a
3
y
a
α
11
x
a
y
a
3
+ + + + + + + + + + +
α –
2
α
4
x
a

5
y
a
– α
7
x
a
2
– 2α
8
x
a
y
a

9
y
a
2
– α
10
x
a
3
– 3α
11
x
a
y
a
2
– – –
α
1

3
x
a
α
4
y
a

6
x
a
2

7
x
a
y
a
α
8
y
a
2

10
x
a
2
y
a
α
11
y
a
3
+ + + + + + +
=
C
a
α ≡
Workbook of Applications in VectorSpace C++ Library 437
Two Dimensional Problems
where C
a
is defined as
Eq. 4•270
for a = 0, 1, 2, 3. Therefore, C is a 12 12 matrix. The vector α can be obtained by inverting Eq. 4•269 as
Eq. 4•271
Therefore, the B-matrix is defined as
Eq. 4•272
where the shape function is
N = PC
-1
Eq. 4•273
The Program Listing 4•21 implements the generic procedure in the above to derive the nonconforming shape
function (Eq. 4•273) for the thin-plate bending rectangular element. Eq. 4•262 and Eq. 4•264 are then taken to
define the B-matrix and the stiffness matrix, respectively. The plate is clamped at four sides and with uniform
unit loading. Only a quarter (upper-right) of the plate is modeled due to the symmetry of the geometry and the
boundary conditions. 4 4 (= 16) elements are used in the computation. At the right and the top edges of the
model the boundary conditions are w = w/ x = w/ y = 0 (clamped). At the bottom and the left edges are
taken as w/ y =0, and w/ x =0, respectively (see Figure 4•58a). The solution of the vertical deflection is
shown in Figure 4•58b.
The maximum deflection is at the center of the plate, or at the lower-left corner of the finite element model.
The exact solution is 226800.
1
The results are shown in TABLE 4•8., which shows the convergence toward the
exact solution when the mesh size is refined.
1. The exact solution is computed from formula provided in p. 31 in Zienkiewicz, O.C. and R.L. Taylor, 1991, “The finite
element method”, 4th ed., vol. 2. McGraw-Hill, Inc., UK, and reference therein.
Mesh Center Deflection
2 2 251691
4 4 234449
8 8 229464
16 16 228186
Exact 226800
TABLE 4•8. Center deflection.
C
a
1 x
a
y
a
x
a
2
x
a
y
a
y
a
2
x
a
3
x
a
2
y
a
x
a
y
a
2
y
a
3
x
a
3
y
a
x
a
y
a
3
0 0 1 – 0 x
a
– 2y
a
– 0 x
a
2
– 2x
a
y
a
– 3y
a
2
– x
a
3
– 3x
a
y
a
2

0 1 0 2x
a
y
a
0 3x
a
2
2x
a
y
a
y
a
2
0 3x
a
2
y
a
y
a
3

×
α C
1 –
u
ˆ
e
a
=
B L∇ ( )PC
1 –
=
×
∂ ∂ ∂ ∂
∂ ∂ ∂ ∂
×
×
×
×
Finite Element Method Primer
438 Workbook of Applications in VectorSpace C++ Library
Chapter 4
#include "include\fe.h"
static row_node_no = 5;
EP::element_pattern EP::ep = EP::QUADRILATERALS_4_NODES;
Omega_h::Omega_h() {
double coord[4][2] = {{0.0, 0.0}, {1.0, 0.0}, {1.0, 1.0}, {0.0, 1.0}};
int control_node_flag[4] = {TRUE, TRUE, TRUE, TRUE};
block(this, row_node_no, row_node_no, 4, control_node_flag, coord[0]);
}
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
__initialization(df, omega_h);
for(int i = 0; i < row_node_no-1; i++)
the_gh_array[node_order(i)](1) = gh_on_Gamma_h::Dirichlet;
for(int i = 0; i < row_node_no-1; i++)
the_gh_array[node_order(i*row_node_no)](2) = gh_on_Gamma_h::Dirichlet;
for(int i = 1; i <= row_node_no; i++) {
the_gh_array[node_order(i*row_node_no-1)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(i*row_node_no-1)](1) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(i*row_node_no-1)](2) = gh_on_Gamma_h::Dirichlet; }
for(int i = 0; i < row_node_no-1; i++) {
the_gh_array[node_order(row_node_no*(row_node_no-1)+i)](0) =
gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(row_node_no*(row_node_no-1)+i)](1) =
gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(row_node_no*(row_node_no-1)+i)](2) =
gh_on_Gamma_h::Dirichlet;
}
}
class PlateR4 : public Element_Formulation {
public:
PlateR4(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
PlateR4(int, Global_Discretization&);
};
Element_Formulation* PlateR4::make(int en, Global_Discretization& gd) {
return new PlateR4(en,gd);
}
static const double E_ = 1.0; static const double v_ = 0.25; static const double t_ = 0.01;
static const double D_ = E_ * pow(t_,3) / (12.0*(1-pow(v_,2)));
static const double Dv[3][3] = { {D_, D_*v_, 0.0 },
{D_*v_, D_, 0.0 },
{0.0, 0.0, D_*(1-v_)/2.0} };
C0 D = MATRIX("int, int, const double*", 3, 3, Dv[0]);
PlateR4::PlateR4(int en, Global_Discretization& gd) : Element_Formulation(en, gd) {
int ndf = 3;
Quadrature qp(2, 16);
H0 dx_inv;
H2 X;
{
H2 z(2, (double*)0, qp),
n = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE(
"int, int, Quadrature", 4/*nen*/, 2/*nsd*/, qp), zai, eta;
zai &= z[0]; eta &= z[1];
n[0] = (1-zai)*(1-eta)/4; n[1] = (1+zai)*(1-eta)/4;
n[2] = (1+zai)*(1+eta)/4; n[3] = (1-zai)*(1+eta)/4;
X &= n*xl;
}
dx_inv &= d(X).inverse();
J dv(d(X).det());
bottom B.C. w/ y =0
left B.C. w/ x =0
top B.C. w = w/ x = w/ y =0
right B.C. w = w/ x = w/ y =0
coordinate transformation rule
∂ ∂
∂ ∂
∂ ∂ ∂ ∂
∂ ∂ ∂ ∂
D
Et
3
12 1 ν
2
– ( )
-------------------------
1 ν 0
ν 1 0
0 0
1 ν –
2
------------
=
Workbook of Applications in VectorSpace C++ Library 439
Two Dimensional Problems
Listing 4•21 Plate bending using nonconformming rectangular element (project workspace file “fe.dsw”,
project “rectangular_plate_bending” with Macro definition
“__GENERIC_NONCONFORMING_SHAPE_FUNCTION” set at compile time).
{
H2 Z(2, (double*)0, qp),
N = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE(
"int, int, Quadrature", nen*ndf/*nenxndf*/, 2/*nsd*/, qp),
Zai, Eta;
Zai &= Z[0]; Eta &= Z[1];
C0 C(12, 12, (double*)0),
C_sub = SUBMATRIX("int, int, C0&", 3, 12, C);
for(int i = 0; i < nen; i++) {
C0 x(xl[i][0]), y(xl[i][1]), x2 = x.pow(2), x3 = x.pow(3), y2 = y.pow(2), y3 = y.pow(3),
zero = C0(0.0), one = C0(1.0);
C_sub(i) =
( one | x | y | x2 | (x*y) | y2 | x3 |(x2*y) | (x*y2) | y3 | (x3*y) | (x*y3) ) &
(zero|zero|-one|zero |(-x)|(-2.0*y)|zero |(-x2) |(-2*x*y)|(-3*y2)|(-x3) |(-3*x*y2) ) &
(zero|one |zero|(2*x)|y |zero |(3*x2)|(2*x*y) |y2 |zero |(3*x2*y)|y3 );
}
C0 C_inv = C.inverse();
H2 P = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE(
"int, int, Quadrature", 12/*nenxndf*/, 2/*nsd*/, qp);
{
H2 x = X[0], y = X[1];
P[0] = 1.0; P[1] = x; P[2] = y; P[3] = x.pow(2);
P[4] = x*y; P[5] = y.pow(2); P[6] = x.pow(3);
P[7] = x.pow(2)*y; P[8] = x*y.pow(2); P[9] = y.pow(3);
P[10] = x.pow(3)*y; P[11] = x*y.pow(3);
}
for(int i = 0; i < 12; i++) N[i] = P * C_inv(i);
H0 Nxx = INTEGRABLE_MATRIX("int, int, Quadrature", 2*nen*ndf, 2, qp);
H0 w_xx = INTEGRABLE_SUBMATRIX("int, int, H0&", 2, 2, Nxx);
for(int i = 0; i < nen*ndf; i++) w_xx(i) = (~dx_inv) * dd(N)(i) * dx_inv;
H0 B = (~w_xx[0][0]) &
(~w_xx[1][1]) &
(2.0*(~w_xx[0][1]));
stiff &= ((~B) * (D * B)) | dv;
double f_0 = 1.0;
force &= (((H0)N)*f_0) | dv;
}
}
Element_Formulation* Element_Formulation::type_list = 0;
static Element_Type_Register element_type_register_instance;
static PlateR4 plate_r4_instance(element_type_register_instance);
int main() {
int ndf = 3;
Omega_h oh; gh_on_Gamma_h gh(ndf, oh); U_h uh(ndf, oh);
Global_Discretization gd(oh, gh, uh);
Matrix_Representation mr(gd);
mr.assembly();
C0 u = ((C0)(mr.rhs())) / ((C0)(mr.lhs()));
gd.u_h() = u; gd.u_h() = gd.gh_on_gamma_h();
cout << "[w, -dw/dy, dw/dx]:" << endl;
for(int i = 0; i < row_node_no; i++)
for(int j = 0; j < row_node_no; j++)
cout << "#" << (i*row_node_no+j) << ": " << gd.u_h()[i*row_node_no+j] << endl;
return 0;
}
Shape functions N
Eq. 4•270 for C-matrix
C
-1

P =
N = PC
-1
, and
1 x y x
2
xy y
2
x
3
x
2
y xy
2
y
3
x
3
y xy
3
B
a
L∇ ( )N
a
= L∇

2
∂x
2
--------

2
∂y
2
--------
2

2
∂x∂y
-------------
=
k
e
i ajb
e
i
T
B
a
T
DB
b
dΩe
j


=
Finite Element Method Primer
440 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Alternatively, we can substitute the explicit shape functions
1
in Eq. 4•262 with
Eq. 4•274
where “2a” and “2b” are the lengths of a rectangular element, and the nodal normalized coordinates are [ξ
a
, η
a
]
= {(-1, -1), (1, -1), (1, 1), (-1, 1)}. Implementation of Eq. 4•274, to be substituting in Eq. 4•262, is straight for-
ward as
1 H2 Z(2, (double*)0, qp),
2 N = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE(
3 "int, int, Quadrature", nen*ndf/*nenxndf*/, 2/*nsd*/, qp),
4 Zai, Eta;
5 Zai &= Z[0]; Eta &= Z[1];
6 double a = fabs( ((double)(xl[0][0]-xl[1][0])) )/2.0,
1. see p. 17 in Zienkiewicz, O.C. and R.L. Taylor, 1991, “The finite element method”, 4th ed., vol. 2. McGraw-Hill, Inc.,
UK, and reference therein.
Figure 4•58 Clamped boundary conditions and nodal deflections for rectangular plate bending
elements (4 4 mesh are shown) using non-conformming shape function. ×
w = ∂w/∂x = ∂w/∂y =0
∂w/∂y =0
∂w/∂x = 0
1.0
1.0
(a)
(b)
1
2
3
4
5
1
2
3
4
5
0
50000
100000
150000
200000
1
2
3
4
5
1
2
3
4
5
0
00
00
0
0
N
a
1
8
---
ξξ
a
1 + ( ) ηη
a
1 + ( ) 2 ξξ
a
ηη
a
ξ
2
– η
2
– + + ( )
b – η
a
ξξ
a
1 + ( ) ηη
a
1 + ( )
2
ηη
a
1 – ( )

a
ξξ
a
1 + ( )
2
ξξ
a
1 – ( ) ηη
a
1 + ( )

Workbook of Applications in VectorSpace C++ Library 441
Two Dimensional Problems
7 b = fabs( ((double)(xl[2][1]-xl[1][1])) )/2.0,
8 zai[4] = {-1.0, 1.0, 1.0, -1.0}, eta[4] = {-1.0, -1.0, 1.0, 1.0};
9 for(int i = 0; i < nen; i++) {
10 N[i*ndf] = (Zai*zai[i]+1)*(Eta*eta[i]+1)*(2+Zai*zai[i]+Eta*eta[i]-Zai.pow(2)-Eta.pow(2))/8.0;
11 N[i*ndf+2] = a*zai[i]*(Zai*zai[i]+1).pow(2)*(Zai*zai[i]-1)*(Eta*eta[i]+1)/8.0;
12 N[i*ndf+1] = -b*eta[i]*(Zai*zai[i]+1)*(Eta*eta[i]+1).pow(2)*(Eta*eta[i]-1)/8.0;
13 }
On the other hand, the Eq. 4•272 is quite generic especially when no one is deriving an explicit formula like Eq.
4•274 for us. The computation is done on the same project (“rectangular_plate_bending” in project workspace
file “fe.dsw”) with macro definition “__EXPLICIT_NONCONFORMING_SHAPE_FUNCTION” set at com-
pile time. The solutions is certainly identical to the one with generic procedure for computing the shape function.
Conforming Rectangular Plate Element (16 degrees of freedom)
Instead of Eq. 4•265, we can extend the nodal variables to
Eq. 4•275
with four nodes at each corner of the rectangle we have totally 16 degree of freedoms. Therefore, a complete
third-order polynomial can be used to represent the deflection w, with P defined as
Eq. 4•276
and parallel to the derivation of Eq. 4•269, we have
Eq. 4•277
Eq. 4•276 and the inverse of Eq. 4•277 can be substituted in Eq. 4•272 to define the B-matrix. The explicit shape
functions for the conforming rectangular element, , is defined as
u
ˆ
e
a
w
a
∂w
∂y
-------
¸ ,
¸ _
a
∂w
∂x
-------
¸ ,
¸ _
a

2
w
∂x∂y
-------------
¸ ,
¸ _
a

P 1 x y x
2
xy y
2
x
3
x
2
y xy
2
y
3
x
3
y x
2
y
2
xy
3
x
3
y
2
x
2
y
3
x
3
y
3
=
C
a
1 x
a
y
a
x
a
2
x
a
y
a
y
a
2
x
a
3
x
a
2
y
a
x
a
y
a
2
y
a
3
x
a
3
y
a
x
a
2
y
a
2
x
a
y
a
3
x
a
3
y
2
a
x
a
2
y
3
a
x
a
3
y
3
a
0 0 1 0 x
a
2y
a
0 x
a
2
2x
a
y
a
3y
a
2
x
a
3
2x
a
2
y
a
3x
a
y
a
2
2x
a
3
y
a
3x
a
2
y
2
a
3x
a
3
y
2
a
0 1 0 2x
a
y
a
0 3x
a
2
2x
a
y
a
y
a
2
0 3x
a
2
y
a
2x
a
y
a
2
y
a
3
3x
a
2
y
2
a
2x
a
y
3
a
3x
a
2
y
3
a
0 0 0 0 1 0 0 2x
a
2y
a
0 3x
a
2
4x
a
y
a
3y
a
2
6x
a
2
y
a
6x
a
y
a
2
9x
a
2
y
2
a

Finite Element Method Primer
442 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Eq. 4•278
where “2a” and “2b” are the lengths of the rectangular element, and the subscript a = 0, 1, 2, 3 are the nodal
numbers (developed by Bogner et al.
1,2
). The same project “rectangular_plate_bending” can be used with macro
definition “__EXPLICIT_CONFORMING_SHAPE_FUNCTION” set at compile time for using Eq. 4•278, or
no macro definition set at compile time for its generic counterpart via Eq. 4•277. The results of center deflection
of the conforming rectangular plate are shown in TABLE 4•9. .
Triangular Plate Element (9 degrees of freedom)
For triangular element we use the area coordinates L
0
, L
1
, and L
2
. The polynomial has 9-terms to match the
9-dof on the three corner nodes. Therefore, P can be defined as
3
Eq. 4•279
Three third order terms are chosen in addition to the first six complete second order terms. The explicit shape
function for the first node is (with cyclic permutation of 0, 1, 2 for other two nodes)
1. see p. 49 in Zienkiewicz and R.L. Taylor, 1991, “The finite element method”, 4th ed., vol. 2. McGraw-Hill, Inc., UK, and
reference therein.
2. see also p. 419, Table 9.1 for the “Hermite cubic element” in Reddy, J.N., 1993, “An introduction to the finite element
method”, 2nd ed., McGraw-Hill, Inc., New York.
3. see p. 244 in Zienkiewicz, O.C., 1977, “The finite element method”, 3rd ed., McGraw-Hill, Inc., UK.
Mesh Center Deflection
2 2 363735
4 4 275114
8 8 242597
16 16 232124
Exact 226800
TABLE 4•9. Center deflection.
N
a
1
16
------
ξ ξ
a
+ ( )
2
ξξ
a
2 – ( ) η η
a
+ ( )
2
ηη
a
2 – ( )
a – ξ
a
ξ ξ
a
+ ( )
2
ξξ
a
1 – ( ) η η
a
+ ( )
2
ηη
a
2 – ( )
b – ξ ξ
a
+ ( )
2
ξξ
a
2 – ( )η
a
η η
a
+ ( )
2
ηη
a
1 – ( )
abξ
a
ξ ξ
a
+ ( )
2
ξξ
a
1 – ( )η
a
η η
a
+ ( )
2
ηη
a
1 – ( )

×
×
×
×
P
L
0
L
1
L
2
L
0
L
1
L
1
L
2
L
2
L
0
L
0
2
L
1
L
1
2
L
2
L
2
2
L
0
=
Workbook of Applications in VectorSpace C++ Library 443
Two Dimensional Problems
Eq. 4•280
where b
0
= y
1
- y
2
, and c
0
= x
2
-x
1
. The explicit shape function for the triangular element can be implemented as
1 H2 L(2, (double*)0, qp), // area coordinates
2 N = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE(
3 "int, int, Quadrature", 9, 2, qp), // shape functions
4 L0 = L[0], L1 = L[1], L2 = 1.0 - L0 - L1;
5 double b0 = (double)(xl[1][1]-xl[2][1]), c0 = (double)(xl[2][0]-xl[1][0]),
6 b1 = (double)(xl[2][1]-xl[0][1]), c1 = (double)(xl[0][0]-xl[2][0]),
7 b2 = (double)(xl[0][1]-xl[1][1]), c2 = (double)(xl[1][0]-xl[0][0]);
8 N[0] = L0+L0.pow(2)*L1+L0.pow(2)*L2-L0*L1.pow(2)-L0*L2.pow(2); // first node
9 N[1] = b2*(L0.pow(2)*L1+L0*L1*L2/2.0)-b1*(L2*L0.pow(2)+L0*L1*L2/2.0);
10 N[2] = c2*(L0.pow(2)*L1+L0*L1*L2/2.0)-c1*(L2*L0.pow(2)+L0*L1*L2/2.0);
11 N[3] = L1+L1.pow(2)*L2+L1.pow(2)*L0-L1*L2.pow(2)-L1*L0.pow(2); // second node
12 N[4] = b0*(L1.pow(2)*L2+L0*L1*L2/2.0)-b2*(L0*L1.pow(2)+L0*L1*L2/2.0);
13 N[5] = c0*(L1.pow(2)*L2+L0*L1*L2/2.0)-c2*(L0*L1.pow(2)+L0*L1*L2/2.0);
14 N[6] = L2+L2.pow(2)*L0+L2.pow(2)*L1-L2*L0.pow(2)-L2*L1.pow(2); // third node
15 N[7] = b1*(L2.pow(2)*L0+L0*L1*L2/2.0)-b0*(L1*L2.pow(2)+L0*L1*L2/2.0);
16 N[8] = c1*(L2.pow(2)*L0+L0*L1*L2/2.0)-c0*(L1*L2.pow(2)+L0*L1*L2/2.0);
Program Listing 4•22 implements the 9-dof triangular plate bending problem. The project
“triangle_plate_bending” in project workspace file “fe.dsw” implements the 9-dofs triangular element for plate
bending problem. The maximum deflection, for a (4 4) 2 triangular mesh, is 205175.
N
0
L
0
L
0
2
L
1
L
0
2
L
2
L
0
L
1
2
L
0
L
2
2
– – + +
b
2
L
0
2
L
1
1
2
---L
0
L
1
L
2
+
¸ ,
¸ _
b
1
L
2
L
0
2
1
2
---L
0
L
1
L
2
+
¸ ,
¸ _

c
2
L
0
2
L
1
1
2
---L
0
L
1
L
2
+
¸ ,
¸ _
c
1
L
2
L
0
2
1
2
---L
0
L
1
L
2
+
¸ ,
¸ _


× ×
Finite Element Method Primer
444 Workbook of Applications in VectorSpace C++ Library
Chapter 4
#include "include\fe.h"
static row_node_no = 5;
EP::element_pattern EP::ep = EP::SLASH_TRIANGLES;
Omega_h::Omega_h() {
double coord[4][2] = {{0.0, 0.0}, {1.0, 0.0}, {1.0, 1.0}, {0.0, 1.0}};
int control_node_flag[4] = {TRUE, TRUE, TRUE, TRUE};
block(this, row_node_no, row_node_no, 4, control_node_flag, coord[0]);
}
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
__initialization(df, omega_h);
for(int i = 0; i < row_node_no-1; i++)
the_gh_array[node_order(i)](1) = gh_on_Gamma_h::Dirichlet;
for(int i = 0; i < row_node_no-1; i++)
the_gh_array[node_order(i*row_node_no)](2) = gh_on_Gamma_h::Dirichlet;
for(int i = 1; i <= row_node_no; i++) {
the_gh_array[node_order(i*row_node_no-1)](0) =
the_gh_array[node_order(i*row_node_no-1)](1) =
the_gh_array[node_order(i*row_node_no-1)](2) = gh_on_Gamma_h::Dirichlet;
}
for(int i = 0; i < row_node_no-1; i++) {
the_gh_array[node_order(row_node_no*(row_node_no-1)+i)](0) =
the_gh_array[node_order(row_node_no*(row_node_no-1)+i)](1) =
the_gh_array[node_order(row_node_no*(row_node_no-1)+i)](2) =
gh_on_Gamma_h::Dirichlet;
}
}
class PlateT3 : public Element_Formulation {
public:
PlateT3(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
PlateT3(int, Global_Discretization&);
};
Element_Formulation* PlateT3::make(int en, Global_Discretization& gd) {
return new PlateT3(en,gd);
}
static const double E_ = 1.0; static const double v_ = 0.25; static const double t_ = 0.01;
static const double D_ = E_ * pow(t_,3) / (12.0*(1-pow(v_,2)));
static const double Dv[3][3]={
{D_, D_*v_, 0.0 },
{D_*v_, D_, 0.0 },
{0.0, 0.0, D_*(1-v_)/2.0 }
};
C0 D = MATRIX("int, int, const double*", 3, 3, Dv[0]);
PlateT3::PlateT3(int en, Global_Discretization& gd) : Element_Formulation(en, gd) {
int ndf = 3;
Quadrature qp(2, 16);
H0 dx_inv;
H1 X;
{
H1 l(2, (double*)0, qp),
n = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int, int, Quadrature", 3, 2, qp),
l0 = l[0], l1 = l[1], l2 = 1.0 - l0 - l1;
n[0] = l0; n[1] = l1; n[2] = l2;
X &= n*xl;
}
dx_inv &= d(X).inverse();
J dv(d(X).det());
bottom B.C. - w/ y =0
left B.C. w/ x =0
right B.C. w = w/ x = w/ y =0
top B.C. w = w/ x = w/ y =0
coordinate transformation rule
∂ ∂
∂ ∂
∂ ∂ ∂ ∂
∂ ∂ ∂ ∂
D
Et
3
12 1 ν
2
– ( )
-------------------------
1 ν 0
ν 1 0
0 0
1 ν –
2
------------
=
Workbook of Applications in VectorSpace C++ Library 445
Two Dimensional Problems
Listing 4•22 9 dof triangular plate bending using nonconformming rectangular element (project workspace
file “fe.dsw”, project “triangular_plate_bending”).
{
H2 L(2, (double*)0, qp),
N = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE(
"int, int, Quadrature", 9, 2, qp),
L0 = L[0],
L1 = L[1],
L2 = 1.0 - L0 - L1;
double b0 = (double)(xl[1][1]-xl[2][1]),
c0 = (double)(xl[2][0]-xl[1][0]),
b1 = (double)(xl[2][1]-xl[0][1]),
c1 = (double)(xl[0][0]-xl[2][0]),
b2 = (double)(xl[0][1]-xl[1][1]),
c2 = (double)(xl[1][0]-xl[0][0]);
N[0] = L0+L0.pow(2)*L1+L0.pow(2)*L2-L0*L1.pow(2)-L0*L2.pow(2);
N[1] = b2*(L0.pow(2)*L1+L0*L1*L2/2.0)-b1*(L2*L0.pow(2)+L0*L1*L2/2.0);
N[2] = c2*(L0.pow(2)*L1+L0*L1*L2/2.0)-c1*(L2*L0.pow(2)+L0*L1*L2/2.0);
N[3] = L1+L1.pow(2)*L2+L1.pow(2)*L0-L1*L2.pow(2)-L1*L0.pow(2);
N[4] = b0*(L1.pow(2)*L2+L0*L1*L2/2.0)-b2*(L0*L1.pow(2)+L0*L1*L2/2.0);
N[5] = c0*(L1.pow(2)*L2+L0*L1*L2/2.0)-c2*(L0*L1.pow(2)+L0*L1*L2/2.0);
N[6] = L2+L2.pow(2)*L0+L2.pow(2)*L1-L2*L0.pow(2)-L2*L1.pow(2);
N[7] = b1*(L2.pow(2)*L0+L0*L1*L2/2.0)-b0*(L1*L2.pow(2)+L0*L1*L2/2.0);
N[8] = c1*(L2.pow(2)*L0+L0*L1*L2/2.0)-c0*(L1*L2.pow(2)+L0*L1*L2/2.0);
H0 Nxx = INTEGRABLE_MATRIX("int, int, Quadrature", 2*nen*ndf, 2, qp);
H0 w_xx = INTEGRABLE_SUBMATRIX("int, int, H0&", 2, 2, Nxx);
for(int i = 0; i < nen*ndf; i++)
w_xx(i) = (~dx_inv) * dd(N)(i) * dx_inv;
H0 B = (~w_xx[0][0]) &
(~w_xx[1][1]) &
(2.0*(~w_xx[0][1]));
stiff &= ((~B) * (D * B)) | dv;
double f_0 = 1.0;
force &= (((H0)N)*f_0) | dv;
}
}
Element_Formulation* Element_Formulation::type_list = 0;
static Element_Type_Register element_type_register_instance;
static PlateT3 plate_t3_instance(element_type_register_instance);
int main() {
int ndf = 3;
Omega_h oh;
gh_on_Gamma_h gh(ndf, oh);
U_h uh(ndf, oh);
Global_Discretization gd(oh, gh, uh);
Matrix_Representation mr(gd);
mr.assembly();
C0 u = ((C0)(mr.rhs())) / ((C0)(mr.lhs()));
gd.u_h() = u;
gd.u_h() = gd.gh_on_gamma_h();
cout << "[w, -dw/dy, dw/dx]:" << endl;
for(int i = 0; i < row_node_no; i++)
for(int j = 0; j < row_node_no; j++)
cout << "#" << (i*row_node_no+j) << ": " << gd.u_h()[i*row_node_no+j] << endl;
return 0;
}
shape functions N
b
0
= y
1
- y
2
, and c
0
= x
2
-x
1
.
explicit shape functions
, and B
a
L∇ ( )N
a
= L∇

2
∂x
2
--------

2
∂y
2
--------
2

2
∂x∂y
-------------
=
k
e
iaj b
e
i
T
B
a
T
DB
b
dΩe
j


=
Finite Element Method Primer
446 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Morley’s Triangular Plate Element (6 degrees of freedom)
A complete quadratic polynomial has only six terms as
Eq. 4•281
A triangular element can be conceived with six degree of freedoms, with three deflection variables “w” on the
corner nodes and three normal derivatives “ w/ n” on the three middle points of the triangle sides as depicted
in Figure 4•59.
Parallel to the derivation of Eq. 4•269 for a generic shape function, we have
w
0
= α
0
, w
1
= α
1
, w
2
= α
2
Eq. 4•282
The normal derivatives to the node number “3” can be obtained according to the formula
1
Eq. 4•283
where l
0
is the length of the edge opposing to node number “0”, ∆ is the area of the triangle, and µ
i
is defined as
, , and Eq. 4•284
Similarly we can define for the other two normal derivatives and . The derivatives of “Pα”
with respect to L
0
, L
1
, and L
2
are
1. p.27 in Zienkiewicz, O.C. and R.L. Taylor, 1991, “The finite element method”, 4th ed., vol. 2. McGraw-Hill, Inc., UK.
P
L
0
L
1
L
2
L
0
L
1
L
1
L
2
L
2
L
0 =
∂ ∂
Figure 4•59 Morley’s six degrees of freedom triangular element.
w
0
(∂w/∂n)
5
w
1
w
2
(∂w/∂n)
3
(∂w/∂n)
4

∂n
------
¸ ,
¸ _
3
l
0
4∆
-------

∂L
1
---------

∂L
2
--------- 2

∂L
0
--------- – µ
0

∂L
2
---------

∂L
1
--------- –
¸ ,
¸ _
+ + =
µ
0
l
2
2
l
1
2

l
0
2
--------------- = µ
1
l
0
2
l
2
2

l
1
2
--------------- = µ
2
l
1
2
l
0
2

l
2
2
--------------- =
∂ ∂n ⁄ ( )
4
∂ ∂n ⁄ ( )
5
Workbook of Applications in VectorSpace C++ Library 447
Two Dimensional Problems
Eq. 4•285
Therefore, using Eq. 4•283, we have
Eq. 4•286
Therefore, C can be expressed as
Eq. 4•287
The shape function is defined as N = PC
-1
. We can still use the definition of stiffness matrix from Eq. 4•264,
Eq. 4•288
Recall it has been defined with a particular choice
Eq. 4•289
that improves the symmetry of plate theory equations. The relation of to and can be expressed as
Eq. 4•290
∂ Pα ( )
∂L
0
----------------- α
0
α
3
L
1
α
5
L
2
+ + =
∂ Pα ( )
∂L
1
----------------- α
1
α
3
L
0
α
4
L
2
+ + =
∂ Pα ( )
∂L
2
----------------- α
2
α
4
L
1
α
5
L
0
+ + =
∂w
∂n
-------
¸ ,
¸ _
3
l
0
4∆
------- 2α
0
– 1 µ
0
– ( )α
1
1 µ
0
+ ( )α
2
α
3
α
4
+ – α
5
– + + [ ] =
∂w
∂n
-------
¸ ,
¸ _
4
l
1
4∆
------- 2α
1
– 1 µ
1
– ( )α
2
1 µ
1
+ ( )α
0
α
4
α
5
+ – α
3
– + + [ ] =
∂w
∂n
-------
¸ ,
¸ _
5
l
2
4∆
------- 2α
2
– 1 µ
2
– ( )α
0
1 µ
0
+ ( )α
1
α
5
α
3
+ – α
4
– + + [ ] =
C
1 0 0 0 0 0
0 1 0 0 0 0
0 0 1 0 0 0
2l –
0
4∆
----------
l
0
1 µ
0
– ( )
4∆
------------------------
l
0
1 µ
0
+ ( )
4∆
------------------------ 1 – 1 1 –
l
1
1 µ
1
+ ( )
4∆
------------------------
2l –
1
4∆
----------
l
1
1 µ
1
– ( )
4∆
------------------------ 1 – 1 – 1
l
2
1 µ
2
– ( )
4∆
------------------------
l
2
1 µ
2
+ ( )
4∆
------------------------
2l –
2
4∆
---------- 1 1 – 1 –

k
e
pq
k
e
i ajb
e
i
T
B
a
T
DB
b
dΩe
j


= =
θ
ˆ
x
∂w
∂y
------- – = and θ
ˆ
y
,
∂w
∂x
------- =
θ
n
θ
ˆ
x
θ
ˆ
y
θ
n
∂w
∂n
------- n
x
∂w
∂x
------- n
y
∂w
∂y
------- + n –
y
( )θ
ˆ
x
n
x
θ
ˆ
y
+ = = =
Finite Element Method Primer
448 Workbook of Applications in VectorSpace C++ Library
Chapter 4
where n = [n
x
, n
y
]
T
is the outward unit surface normal on the mid-side node. Therefore, the B-matrix corre-
sponding to -dof is multiplied with a factor “(n
x
- n
y
)” to take care of the conventional choice in Eq. 4•289.
Program Listing 4•23 implements the Morley’s 6-dof triangular plate element. The result of the computation are
shown in TABLE 4•10..

No. of Elements Center Deflection
(4 4) 2 125704
(8 8) 2 165518
(16 16) 2 192789
Exact 226800
TABLE 4•10. Center Deflection of Morley’s
triangular plate element.
θ
n
× ×
× ×
× ×
Workbook of Applications in VectorSpace C++ Library 449
Two Dimensional Problems
bottom B.C. w/ n =0
left B.C. w/ n =0
right B.C. w = w/ n =0
top B.C. w = w/ n =0
∂ ∂
∂ ∂
∂ ∂
∂ ∂
D
Et
3
12 1 ν
2
– ( )
-------------------------
1 ν 0
ν 1 0
0 0
1 ν –
2
------------
=
#include "include\fe.h"
static row_node_no = 9;
Omega_h::Omega_h() {
int row_segment_no = (row_node_no - 1)/2;
double v[2]; int ena[6];
for(int i = 0; i < row_node_no; i++)
for(int j = 0; j < row_node_no; j++) {
int nn = i*row_node_no+j;
v[0] = (double)j/(double)(row_node_no-1); v[1] = (double)i/(double)(row_node_no-1);
Node* node = new Node(nn, 2, v); the_node_array.add(node);
}
for(int i = 0; i < row_segment_no; i++)
for(int j = 0; j < row_segment_no; j++) {
int nn = i*row_node_no*2+j*2;
ena[0] = nn; ena[1] = ena[0]+row_node_no*2+2; ena[2] = ena[1]-2;
ena[3] = ena[2] + 1; ena[4] = ena[0]+row_node_no; ena[5] = ena[4]+1;
int en = i*row_segment_no*2+j*2;
Omega_eh* elem = new Omega_eh(en, 0, 0, 6, ena); the_omega_eh_array.add(elem);
ena[0] = nn; ena[1] = nn+2; ena[2] = ena[1] + row_node_no*2;
ena[3] = ena[1] + row_node_no; ena[4] = ena[3] -1; ena[5] = ena[0] +1;
elem = new Omega_eh(en+1, 0, 0, 6, ena); the_omega_eh_array.add(elem);
}
}
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) { __initialization(df, omega_h);
for(int i = 1; i < row_node_no-1; i+=2)
the_gh_array[node_order(i)](0) = gh_on_Gamma_h::Dirichlet;
for(int i = 1; i < row_node_no-1; i+=2)
the_gh_array[node_order(i*row_node_no)](0) = gh_on_Gamma_h::Dirichlet;
for(int i = 0; i < row_node_no; i+=2) {
the_gh_array[node_order(i*row_node_no-1)](0) =
the_gh_array[node_order((i+1)*row_node_no-1)](0) = gh_on_Gamma_h::Dirichlet;
}
for(int i = 0; i < row_node_no-1; i+=2) {
the_gh_array[node_order(row_node_no*(row_node_no-1)+i)](0) =
the_gh_array[node_order(row_node_no*(row_node_no-1)+i+1)](0) =
gh_on_Gamma_h::Dirichlet;
}
the_gh_array[node_order(row_node_no*row_node_no-1)](0) = gh_on_Gamma_h::Dirichlet;
}
class PlateMorley6 : public Element_Formulation {
public:
PlateMorley6(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
PlateMorley6(int, Global_Discretization&);
};
Element_Formulation* PlateMorley6::make(int en, Global_Discretization& gd) {
return new PlateMorley6(en,gd);
}
static const double E_ = 1.0;
static const double v_ = 0.25;
static const double t_ = 0.01;
static const double D_ = E_ * pow(t_,3) / (12.0*(1-pow(v_,2)));
static const double Dv[3][3] = {
{D_, D_*v_, 0.0 },
{D_*v_, D_, 0.0 },
{0.0, 0.0, D_*(1-v_)/2.0}
};
C0 D = MATRIX("int, int, const double*", 3, 3, Dv[0]);
Finite Element Method Primer
450 Workbook of Applications in VectorSpace C++ Library
Chapter 4
Listing 4•23 Morley’s 6-dof triangular plate bending(project workspace file “fe.dsw”, project
“morley_plate_bending”).
PlateMorley6::PlateMorley6(int en, Global_Discretization& gd) : Element_Formulation(en, gd) {
Quadrature qp(2, 16);
H1 l(2, (double*)0, qp),
n = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 3, 2, qp),
l0 = l[0], l1 = l[1], l2 = 1.0 - l0 - l1; n[0] = l0; n[1] = l1; n[2] = l2;
C0 x = MATRIX("int, int, C0&, int, int", 3, 2, xl, 0, 0);
H1 X = n*x; H0 dx_inv = d(X).inverse(); J dv(d(X).det()/2.0);
{
H2 L(2, (double*)0, qp),
N = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE(
"int, int, Quadrature", 6, 2, qp),
L0 = L[0], L1 = L[1], L2 = 1.0 - L0 - L1;
H0 unit(qp); unit = 1.0; double area = (double)(unit | dv);
double l_0 = (double)norm(xl[1]-xl[2]), l_1 = (double)norm(xl[2]-xl[0]),
l_2 = (double)norm(xl[0]-xl[1]), mu_0 = (pow(l_2,2)-pow(l_1,2))/pow(l_0,2),
mu_1 = (pow(l_0,2)-pow(l_2,2))/pow(l_1,2), mu_2 = (pow(l_1,2)-pow(l_0,2))/pow(l_2,2),
d3 = l_0/(4.0*area), d4 = l_1/(4.0*area), d5 = l_2/(4.0*area);
C0 C = ( C0(1.0) | C0(0.0) | C0(0.0) | C0(0.0) | C0(0.0) | C0(0.0) ) &
( C0(0.0) | C0(1.0) | C0(0.0) | C0(0.0) | C0(0.0) | C0(0.0) ) &
( C0(0.0) | C0(0.0) | C0(1.0) | C0(0.0) | C0(0.0) | C0(0.0) ) &
(d3*( C0(-2.0) | C0(1.0-mu_0) | C0(1.0+mu_0) | C0(-1.0) | C0(1.0) | C0(-1.0) )) &
(d4*( C0(1.0+mu_1) | C0(-2.0) | C0(1.0-mu_1) | C0(-1.0) | C0(-1.0) | C0( 1.0) )) &
(d5*( C0(1.0-mu_2) | C0(1.0+mu_2) | C0(-2.0) | C0( 1.0) | C0(-1.0) | C0(-1.0) ));
C0 C_inv = C.inverse();
H2 P = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE(
"int, int, Quadrature", 6/*nenxndf*/, 2/*nsd*/, qp);
P[0] = L0; P[1] = L1; P[2] = L2; P[3] = L0*L1; P[4] = L1*L2; P[5] = L2*L0;
for(int i = 0; i < 6; i++) N[i] = P * C_inv(i);
H0 Nxx = INTEGRABLE_MATRIX("int, int, Quadrature", 2*nen*ndf, 2, qp);
H0 w_xx = INTEGRABLE_SUBMATRIX("int, int, H0&", 2, 2, Nxx);
for(int i = 0; i < nen*ndf; i++) w_xx(i) = (~dx_inv) * dd(N)(i) * dx_inv;
H0 B = (~w_xx[0][0]) & (~w_xx[1][1]) & (2.0*(~w_xx[0][1]));
for(int i = 0; i < 3; i++) {
int next = ((i == 2)? 0 : i+1);
C0 t = xl[next]-xl[i]; t = t/norm(t);
C0 nx = t[1], ny = -t[0];
B(i+3) = B(i+3)*(nx-ny);
}
stiff &= ((~B) * (D * B)) | dv;
double f_0 = 1.0;
force &= (((H0)N)*f_0) | dv;
}
}
Element_Formulation* Element_Formulation::type_list = 0;
static Element_Type_Register element_type_register_instance;
static PlateMorley6 plate_m6_instance(element_type_register_instance);
int main() {
int ndf = 1; Omega_h oh; gh_on_Gamma_h gh(ndf, oh); U_h uh(ndf, oh);
Global_Discretization gd(oh, gh, uh); Matrix_Representation mr(gd);
mr.assembly(); C0 u = ((C0)(mr.rhs())) / ((C0)(mr.lhs()));
gd.u_h() = u; gd.u_h() = gd.gh_on_gamma_h();
for(int i = 0; i < row_node_no; i++)
for(int j = 0; j < row_node_no; j++)
cout << "#" << (i*row_node_no+j) << ": " << gd.u_h()[i*row_node_no+j] << endl;
return 0; }
coordinate transformation rule
shape functions N
natural coordinate L
0
, L
1
, L
2

C
C
-1
N = PC
-1

, and
B (n
x
-n
y
) for θ
n
, to be compatible with
stiffness matrix that is defined for
B
a
L∇ ( )N
a
= L∇

2
∂x
2
--------

2
∂y
2
--------
2

2
∂x∂y
-------------
=
θ
n
n –
y
( )θ
ˆ
x
n
x
θ
ˆ
y
+ =
θ
ˆ
x
∂w
∂y
------- – = and θ
ˆ
y
,
∂w
∂x
------- =
k
e
iaj b
e
i
T
B
a
T
DB
b
dΩe
j


=

Chapter

4 Finite Element Method Primer

matical problems just like what VectorSpace C++ Library is designed for. However, for general purpose programming, none of these alternative approaches could come close to rival that of C++. If we have chosen those alternative approaches, we will be seriously penalized by their limited capability in non-mathematical aspects of the programming. If you choose to program in C++ with the VectorSpace C++ Library, your programming task will be significantly empowered by the object-oriented programming—the modern programming paradigm. Time have proven that specific purpose languages do not last long, they come and go and never gaining any wide acceptance. Sometimes, they are even quickly forgotten by the communities of the applications that they are specifically targeting for. Jump on the band wagon of C++, you have entire software industry (particularly all the first-ranked compiler vendors), professional programmers, and a vast number of C++ literatures behind you. Our program’s potential can only be limited by our own imagination, not some un-supported language features.

4.1 Basics of Finite Element Method
4.1.1 Mathematical Abstraction of Finite Element Method
Finite element method can be considered as a special case of variational methods, with special emphases on the a systematic treatment of complicated geometry.

Finite Element—A Systematic Treatment for Complex Geometry
In finite element method, the approximation basis functions for the variable ue is defined in each subdoh main—element Ωe (see Figure 4•1, the subscript “e” denotes “element”, and “h” denotes element discretization into a “characteristic size”—h)
h a ˆa u e ≅ u e ≡ φ e u e , where a = 0, … ,n en – 1

Eq. 4•1

where “nen” is the number of nodes in an element. The space of ue is infinite dimensional, in which every point x on the element has a variable ue(x) associated with it. In Figure 4•1, this infinite dimensional variable ue(x) is h approximated by a finite dimensional space of approximated function u e (x) which in turn only depends on finite
h element— Ωe

discretized global domain— Ω h global domain —Ω

boundary—Γ Figure 4•1 Geometry of global domain discretization.
266

Workbook of Applications in VectorSpace C++ Library

Basics of Finite Element Method
ˆa number of nodal values ue (“a” is the element node number, “hat” denotes a nodal value). The approximated h ˆa a function, rewritten as u e ( u e ; x), is defined through a set of interpolation (basis) function φ e on the element as a h a in Eq. 4•1. The space spans by these bases, φe , is known as the finite element space. The trio set ≡ { Ωe , φe , h a u e }, defined as a finite element is consists of (1) element (domain) “Ωe”, (2) interpolation functions “ φ e ”, and h (3) degree of freedoms “ u e ”.1

We have seen some examples of linear and quadratic interpolation functions for the purpose of numerical integration in 1-D and 2-D in the Chapter 3. For example, interpolation functions for a bilinear four-node element can be defined with the formula
1 N a ( ξ, η ) = -- ( 1 + ξ a ξ ) ( 1 + η a η ) 4

Eq. 4•2

where index “a” ( = 0, 1, 2, 3) is the element node number. The coordinate (ξa , ηa) = {{-1, -1}, {1, -1}, {1, 1}, {1, 1}} is the natural (or referential) coordinates of the four nodes. Therefore, the explicit forms for the interpolation functions are
1 1 1 1 N 0 = -- ( 1 – ξ ) ( 1 – η ), N 1 = -- ( 1 + ξ ) ( 1 – η ), N 2 = -- ( 1 + ξ ) ( 1 + η ), N 3 = -- ( 1 – ξ ) ( 1 + η ) 4 4 4 4

Eq. 4•3

The interpolation function formula for linear triangular element can be degenerated from Eq. 4•3 by setting Tri Tri N 0 = N 0, N 1 = N 1 , and
1 1 1 Tri N 2 = N 2 + N 3 = -- ( 1 + ξ ) ( 1 + η ) + -- ( 1 – ξ ) ( 1 + η ) = -- ( 1 + η ) 4 4 2

Eq. 4•4

(or using “triangular area coordinates” as in page 454 of Chapter 5). That is
1 1 1 Tri Tri Tri N 0 = -- ( 1 – ξ ) ( 1 – η ), N 1 = -- ( 1 + ξ ) ( 1 – η ), N 2 = -- ( 1 + η ) 4 4 2

Eq. 4•5

Coordinate transformation using Eq. 4•3 for quadrilateral and Eq. 4•5 for triangular elements are shown in the middle column of Figure 4•2. From those integration examples, we note that a reference element1., Ωe , can be defined in a normalized region with a coordinate transformation rule x ≡ x(Ωe) which maps the reference eleh ment, Ωe , to a physical element, Ωe ; i.e., a normalized domain in natural coordinates ξ is transformed to a physical domain in coordinate x. The interpolation functions for the coordinate transformation rule can be chosen to h be the same as the interpolation for the approximated function u e (x) as in Eq. 4•1. That is
a a x ( Ω e ) ≡ φ e x e , where a = 0, nen – 1

Eq. 4•6

a where x e is the nodal coordinates (“over-bar” indicates fixed nodal values). A finite element with the same set of interpolation functions for (1) approximation functions and (2) coordinate transformation rule is called an iso-

1. P. G. Ciarlet, 1978, “ The finite element method for elliptic problems”, North-Holland, Amsterdam.

Workbook of Applications in VectorSpace C++ Library

267

4•1. The continuity requirement demands that the approximated function to be continuous both in the interior and the boundaries of the element. we have the so-called non-conforming elements. linear quadrilateral and trianglular elements. The interpolation functions in finite element method are further subject to continuity and completeness requirements. where W = 1 on each subdomain and W = 0 elsewhere. Finite Element Approximation In the standard finite element method. parametric element. W. 268 Workbook of Applications in VectorSpace C++ Library .e. up to certain order. Eq. which are the same as the element interpolation funca tions φ e in Eq. for u e = g ≡ φ Γ u e on Γ g e W= a φe .. a a ˆa 0. 4•7 otherwise a g is the essential boundary conditions on the boundary Γg. this particular choice of weighting function resembles the subdomain collocation method (see page 229) in the weighted residual method. When these requirements are relaxed. the weighting functions. i. but vanishing at boundaries corresponding to the essential boundary conditions. is taken as that in the Galerkin method in the context of weighted residual methods (see page 232). in the approximated function can be accurately represented. and (2) 2-D curve. and u e (“over-bar” indicates fixed nodal values) is a the nodal value of the essential boundary condition with a boundary interpolation function φ Γ on the boundary e a associated with the element. Since φ e is defined in the element domain only. and quadratic quadrilateral and triangular elements. The completeness requirement demands arbitrary variation.Chapter 1-D 4 Finite Element Method Primer 2-D curve quadratic quadrilateral η 2 1 7 0 1 η 2 ξ η 2 ξ 0 1 0 4 1 1 2 0 degenerated linear triangle degenerated quadratic triangle 7 8 5 4 ξ 2 η 3 8 5 ξ 6 2 linear 0 quadratic 0 linear quadrilateral 3 0 1 1 Figure 4•2 (1) 1-D linear and quadratic line elements.

h ) Γ – a ( φ e . φ e )u e Eq. Springer-Verlag. Fortin. the rows (or equations) corresponding to the fixed degree of freedoms will be ab eliminated at the global level. h ) Γ – a ( φ e . Brezzi and M. applied the Green’s theorem to change the resultant righthand-side domain integral into a boundary integral. 4•1). “ Mixed and hybrid finite element methods”. and to enable the reuse of existing code in its evolutionary course. 4•10 The difference of Eq. 4•8 or in matrix forms ab ˆ b a ke ue = fe Eq. for a differential equation problem. 4•8. Since W has been taken according to Eq. the rows (or equations) corresponding to the fixed degree of freedoms (essential boundary conditions) will have vanishing weighting function (W = 0) multiplies through-out every term of Eq. New York. and (2) a corresponding set of variables approximated by a selected set of interpolation functions. 4•10 the Γe index “a” is the element equation number. 4•7. the finite element approximation. we first discretize its domain into elements (as in Figure 4•1) and approximate its variables (Eq. In Eq. The object-oriented programming has a lot to offer in this regard.Basics of Finite Element Method For a self-adjoint operator. then. p. from Eq. Inc. and weighting functions (Eq. In summary. 3 in F. According to the first line of Eq. 1991. 3•125 in Chapter 3 is now we have second and third terms in the right-handside. ab a b k e = a ( φe . f ) + ( φ e . We also note that the element tensors ke is the element stiffness matrix. Therefore. at each element. φe )u e = ( φ e . A finite element approximation depends on the choice of (1) the variational principle. 4•7. The various variational principles make the finite element method such an open area for improvements. 4•8 from Eq. and the a element tensors fe is the element force vector. 1. f ) + ( φ e . and the index “b” is the element variable (degree of freedom) number. Workbook of Applications in VectorSpace C++ Library 269 . The second terms is the non-homogeneous natural boundary conditions q • n = h ≡ φ a h e on Γh Γe a Eq. rewritten with a new index “b” as g ≡ φ b u e. The third term is due to non-homogeneous essential boundb ary conditions. This term occurs when we take integration by parts on the weighted-residual statement. φ e ) a a a a b b f e = ( φ e . 4•7) corresponding to a variational principle. φ e )u e Eq. These steps are known as the finite element approximation1. 4•9 where. gives a b ˆb a a a b b a ( φ e . These various variational principles also bring a challenge that a finite element program should be able to endure a dramatic impact of changes in its design structure. 3•125 in Chapter 3. 4•11 where q • n is flux q projected on the outward unit surface normal n.

while the variational principle needs to be h . the object-oriented design. the class. The most impressive power comes out of this inheritance relation is the dynamic binding mechanism provided to implement the concept of polymorphism.e. Each of such a software module —class defines the states of an object as its member data. i. In an abstract data type. protected. 4•8 to Eq.1. The objects are empowered with inheritance and virtual function mechanism.lib”. It sounds so familiar that we used to write “go to” among Fortran statements which has the potential to grow into an extremely complicated flow chart (a network of statements). However.. Secondly. the element stiffness matrix k ab and the element force vecapplied on the global discretized domain— Ω e a tor f e need to be assembled into a global stiffness matrix K and global force vector F as ˆ K ij u j = Fi K ij = A ∀e ab k e . The symbol A stands for the procedure of assembly of all ele∀e ˆ ments. The procedure programming is the old discipline proposed to rescue the old-world from chaos. As we have mentioned earlier. which is the very first company to attempt a large-scale C++ project1. 270 Workbook of Applications in VectorSpace C++ Library . The object-oriented paradigm is meant to replace the old-way—the procedure programming. i. The object-oriented analysis is applied on the problem domains to understand the dependency relations among objects and the object-oriented design is the newly programming discipline taken to harness the rampant power of C++. 4. in which the source codes are provided for demonstrating the object-oriented method. data structure and algorithms (subroutines) performing on the data structure are separate. C++ also provides user access control mechanism to declare its member data or functions as private. A new discipline. the inheritance relation enables factoring of common parts to define a more general base class higher in the class hierarchy.. they are organized into a coherent unit. is a lesson learned from a frequently cited costly experience from Mentor Graphics (one of the world largest CAD company today). where a specific behavior is actually defined.e. we go further on. and the behaviors of the object as its member functions. and F i = A ∀e a fe Eq. 4•10 are defined only on an element domain— Ωe . The index “i” is the global equation number and index “j” is the global variable number of uj . A call on the virtual function of a base class is dispatched by the virtual function mechanism to the corresponding member function of the derived class.2 Object-Oriented Modeling of the Finite Element Method The central theme of the object-oriented programming is the data abstraction and inheritance.Chapter 4 Finite Element Method Primer Global Matrix and Solution Phase—Systematic Treatment for Large-Size Degree of Freedoms h Eq. Then. the data abstraction enabling software modules to be modeled after the real world objects. or public. We explore all these programming concepts in the modeling of the finite element library— “fe. More specific classes can be derived from the base class by adding details to facilitate the idea of programming by specification and to enforce code reuse. In C++ such mechanism is provided by declare member functions as virtual. Now. In the procedure programming method. we introduce the object-oriented method and the resultant complicated network of objects turns out to be a serious problem too. 4•12 ˆ where u j is the global nodal solution vector. the data and function are now organized together as a coherent abstract data type—class. such that the complexities can be encapsulated inside the abstract data type. Firstly. the dependency relations among the objects can grow to an extremely complicated network of objects.

However. 2. 1996. int *node_number_array). v = new double[2]. The private members of the object are encapsulated from users that the access are only possible through its public members. Addison-Wesley. “Large-scale C++ software design”. An element h Ω e is often described as simple geometrical area like triangles or quadrilaterals. Reading. int material_type_number. This instantiates an object of type “Node” pointed to by a pointer “nd”. The states of the “Node” is consist of private data members include the node number. and preface in J.0. The “operator[](int)” is used to extract the components of the coordinates..0. 4•10 contain the core of mathematics of the differential equation problems. Massachusetts. 2. “Taming C++”. int number_of_spatial_dimension.. and logical operators “operator==(Node&)” and “operator !=(Node&)” are used for the comparison of the values of two nodes. and coordinates x = {1. v[1] = 2. 4•8 to Eq. The behaviors of the “Node” are public member functions that provide user to query the states of the “Node” including it node number.0. . which are actually quite a challenging task that we will use object-oriented modeling for their implementation.0}T double *v. One considers that the “node_number” as the identifier for an instance of the class Node. these trivia are no simple matter. v). the finite element method can be viewed as a systematic treatment for these non-mathematical trivia. the spatial_dimension. Data abstraction is applied to model the “Node” as an object. All other things in finite element method are really complicated details. Discretization Global Domain— Ω h h The first step of the finite element method is to discretize the problem domain into element— Ωe . see p. Addison-Wesley.Basics of Finite Element Method In the section on the mathematical abstraction of finite element method. Reading. Massachusetts. int element_type_number. int element_node_number. The vertices for these simple geometrical objects are called nodes with nodal coordinates as x . Workbook of Applications in VectorSpace C++ Library 271 . only Eq. 1 in J. As we have mentioned earlier. The encapsulation mechanism provides a method to hidden complexities from bothering users (see Figure 4•3). 1. double* array_of_coordinates). The following example is to define a 2-D case with the node number “5”. Using the terminology of the relational database the “node_number” is the key to this abstract data type— “Node”. The data and the functions that operating on them are now organized together into a coherent unit—class. Soukup. h An element— Ω e is constructed by 1 2 3 Omega_eh(int element_number. and spatial dimension. Step 1. Lakos. etc. and the values of its coordinates. A node object is instantiated by its constructor Node(int node_number. Node *nd = new Node(5. 1994. v[0] = 1.

ena = new int[4]. The “node_number_array” points to an int pointer array of global node numbers for the element.. ena[2] = 11. The “element_number” play the role of the key for the element class “Omega_eh”. 0. ena[1] = 1. 272 Workbook of Applications in VectorSpace C++ Library .Chapter 4 Finite Element Method Primer class Node == . For example. which is conventional in finite element method. 4.. ena). . The private members are encapsulated away from the controlled access through the public members. The “element_type_number” and the “material_type_number” are integers greater or equal to “0”. int node_no int spatial_dim double* value node_no() [] controlled access Figure 4•3 The class Node is consists of private data members to describe its states. as illustrated in the comment area after each statement. Omega_eh *elem = new Omega_eh(0.. and public member functions provide the access to query its states. The default values for the both numbers are “0”. the “element_node_number” is “3” for a triangular element. Dynamic_Array<Omega_eh> the_omega_eh_array. ena[0] = 0... 0. and “4” for a four-node element. }. public: Omega_h().. An example is 1 2 3 4 5 6 int *ena. // 10 11 // +-------------+ // | | // | | // +-------------+ // 0 1 The order of global node numbers in the “node_number_array” is counter-clockwise from the lower-left corner. // declared by not defined . ena[3] = 10. A discretized global domain— Ω h basically consists of a collection of all nodes and elements as 1 2 3 4 5 6 7 8 class Omega_h { // discretized global domain— Ω h protected: Dynamic_Array<Node> the_node_array.

v[1] = (double) i. 1995. // +-------------------+ for(int i = 0. int en = i * row_element_no + j. // +-------------------+ v[0] = (double) j. i < row_element_no. ena[2] = ena[3] +1.add(node).J. It is a simplified version of <dynarray> in the standard C++ library1. v). for(int i = 0. Inc. // element number 1. j++) { // ena[3] ena[2] int nn = i * row_node_no + j. ena[3] = nn + row_node_no. New Jersey. The users of the “fe. Workbook of Applications in VectorSpace C++ Library 273 . i++) for(int j = 0. i < row_node_no.h”. Plauger. // | | Node *node = new Node(nn. j < row_element_no. P.Basics of Finite Element Method 12 6 8 3 4 0 5 9 4 6 13 7 10 14 8 11 15 5 7 2 1 0 3 1 2 Figure 4•4 Nine elements in a rectangular area consist of 16 nodes. The default constructor “Omega_h::Omega_h()” is declared in the header file.. “The draft standard C++ library”. // | | } // | | int ena[4]. 2. i++) // ena[0] ena[1] for(int j = 0. // node number at lower left corner ena[0] = nn. j < row_node_no. The data structure Dynamic_Array<T> does what it means. // | | the_node_array. Two protected member data consist of “the_node_array” and “the_omega_eh_array” (element array).lib” are responsible for its definition. which is declared and defined in “dynamic_array. j++) { int nn = i * row_node_no + j. double v[2]. Prentice-Hall. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Omega_h::Omega_h() { // define default constructor int row_node_no = 4. Englewood Cliffs. row_element_no = row_node_number -1. ena[1] = ena[0] + 1. The following code segment shows an example of a user defined discretized global domain as illustrated in Figure 4•4.

0. 1 2 3 4 class Nodal_Value { protected: int the_node_no. and h h are nodal quantities..add(elem). x . New York. Al Stevens. The function “Dynamic_Array<T>::add(T*)” is an example of data manipulation language (DML) that assists user to modify the database. which serves the function of the data definition language (DDL). } } Then. Omega_eh. Therefore.e. e. A constraint flag is And. The instance “oh” calls the default constructor “Omega_h::Omega_h()” that is custom made by the user.Chapter 19 20 21 22 23 4 Finite Element Method Primer Omega_eh *elem = new Omega_eh(en. Step 2. 0. which are somewhat similar to the coordinates of a node. 4. 2nd eds. The essential boundary conditions (fixed degree of freedoms) and natural boundary conditions are h g h on Γ h g . “ C++ database development”. Henry Holt and Company. ena). the class definitions of Node. the global boundary conditions g used to switch in between “Dirichlet” and “Neumann” to indicate whether the stored values are essential or natural boundary conditions. Free and Fixed Variables The discretized global free degree of freedoms are (“hat” indicate a nodal value) ˆ u h on Ωh. i.. Remark: For users who are familiar with database languages1. // number of dimension 1. the format of the data.g. nd. New York. the_omega_eh_array.. we can make an instance of the discretized global domain “Omega_h” by declaring in main() function Omega_h oh. 274 Workbook of Applications in VectorSpace C++ Library . Inc. where the “over-bar” indicates a fixed value. The global variables u h are modeled as class “U_h”.e. we can factor out the code segment on coordinates in the class Node and create a more abstract class Nodal_Value for all of them.. h and h h are modeled as class “gh_on_Gamma_h”.lib” are the node selector “Node& Omega_h::operator [ ](int)” and the element selector “Omega_eh& Omega_h::operator ( )(int)”. And two most important features of data query language provided by “fe. g h .. ˆ All three kinds of values u h . respectively. 1994. i. and Omega_h per se define the database schema. and h h on Γ h ˆ respectively.

in the “main()” program. the variable “uh” of class U_h. U_h uh(ndf.Basics of Finite Element Method 5 6 7 8 9 double *the_value. the access is performed by 1 2 3 nd[0]. and “gh” is an instance of the class gh_on_Gamma_h. oh). it also help to classify these software modules. oh). For example. uh[1]. If we have found out in the future that the way we modeled the “nodal values” is unsatisfactory. The code will be significantly duplicated. } class U_h : public Nodal_Value { . then. We now consider an example of heat conduction (see Figure 4•5) using the discretized global domain. the object-oriented programming method not only use data abstraction to organize data and functions (the algorithm operating upon data).. // first coordinate value // second degree of freedom // first constraint values The common part of the three classes are factored out to form a new base class “Nodal_Value”... } class gh_on_Gamma_h : public Nodal_Value { . the essential and natural boundary conditions in the class gh_on_Gamma_h are parts of every differential equation prob- Workbook of Applications in VectorSpace C++ Library 275 . We should instantiate. if we have not done so. The number of degree of freedom “ndf” = 1. into a hierarchical structure. .. However. int ndf = 1. We note that classification of things into hierarchical structure is one of the most powerful tools that human beings have to built knowledge. the temperature... If “nd” is an instance of the class Node and “uh” is an instance of the class U_h. which are modeled after real world objects.. } All three derived classes inherit the public interfaces (member functions) of the class Nodal_Value. gh[0]. .e. declared as “oh” previously. and the boundary conditions “gh” of class gh_on_Gamma_h as the followings 1 2 3 4 5 int main() { . factoring out this common part is good for the maintenance of the code. } The constructor of class U_h is defined in “fe. }.. The users do not need to worry about it. i. gh_on_Gamma_h gh(ndf. public: operator[](int).lib”.. Now the three classes are publicly derived from the base class “Nodal_Value” as 1 2 3 class Node : public Nodal_Value { .. In general... and was illustrated in Figure 4•4.. now all three derived classes can use the operator[](int) to access the nodal values. changes made in this single class are sufficient comparing to changes needed to be made in all three classes. In addition.

Line 7 uses a constraint value selector “operator [ ](int degree_of_freedom)” to assign 30oC to the nodes on the upper boundary. for the present problem. i < row_node_no. The default condition and default value. for(int i = 0. This function initiates a private data member “Dyanmic_Array<Nodal_Constraint> the_gh_array” for the class gh_on_Gamma_h. defining the constructor of class gh_on_Gamma_h is users’ responsibility. This is a mandatory first statement for every constructor of this class for ochestrating internal data structure. to either as “gh_on_Gamma_h::Dirichlet” to indicate an essential boundary condition or as “gh_on_Gamma_h::Neumann” to indicate a natural boundary condition. respectively. lems. } } The first line in the constructor (line 2) called a private member function of class gh_on_Gamma_h.Chapter 4 Finite Element Method Primer g = 30 oC 12 6 8 h=0 4 0 0 1 3 5 1 2 g = 0 oC 9 4 6 2 3 13 7 10 5 7 14 8 11 h=0 15 Figure 4•5 Heat conduction problem with two side insulated. On the bottom boundary conditions. Omega_h& oh) { __initialization(df. the natural boundary condition with “0” on two sides can be neglected. for each degree of freedom. 276 Workbook of Applications in VectorSpace C++ Library . int row_node_no = 4. are natural boundary condition and “0”. the_gh_array[node_order(row_node_no*(row_node_no-1)+i)][0] = 30. i++) { the_gh_array[node_order(i)](0) = gh_on_Gamma_h::Dirichlet. Therefore. For the problem at hand. the_gh_array[node_order(row_node_no*(row_node_no-1)+i)](0) = gh_on_Gamma_h::Dirichlet. the assignment of value of “0. we only need to specify their constraint type as essential boundary conditions.0” (the default value) can be skipped too. bottom and top temperature boundary conditions are set to 0 oC and 30 oC. respectively. It can be assigned. Therefore. The first line in the for loop uses a constraint type selector “operator ( )(int degree_of_freedom)”.0. This constructor needed to be defined before it is instantiated in the above. following finite element method convention. omega_h). we have 1 2 3 4 5 6 7 8 9 gh_on_Gamma_h::gh_on_Gamma_h(int df.

f / df The corresponding C++ code can be written as a function C0 dx(const C0& f. the element formulation is different. The VectorSpace C++ Library is therefore most heavily used in the element formulation. “Horse”. we may have a base class of “Animal”. the design of the “element formulation definition language”. and the “whale” catches tons of fishes. introduced in Chapter 3 in page 217. f. Now we can have one single generic command for all kinds of desperately different individual objects. we have introduced the non-linear and transient problems in the context of variational methods which are now the kernel of the element formulation. const C0& df) { return .f / df.lib” user’s perspective. “horse”. further flexibility for element formulation can be obtained through the polymorphism supported in C++.5. A simple algebraic example is described in root-finding problem in page 40 of Chapter 1. Now. In Chapter 3. We defer the more complicated matrix substructuring until Section 4. is for (1) definition of an element formulation and (2) registration of an element type. and “whale” of general type “Animal”. where the Newton’s formula gives the increment of solution dx as dx = . and “Whale”. Then. QR decomposition (for ill-conditioned case) or even the singular value decomposition (for rank deficient case). if we use global matrix substructuring solution method (or “static condensation”). if you would. Next. and different operations intelligently dispatched to perform upon themselves. the “horse” bites grass. it is soon recognized that an element subroutine should be used to form an replaceable module. “Horse”. For every differential equation problem. from “fe. For n-dimensional problem. Now the objects put into the hierarchical structure can be made to be intelligent to perform some autonomous tasks. Under the procedure programming paradigm. this part is highly mathematical. we declare three instances “lion”. In object-oriented programming. The “C0::operator / (const C0&)” now no longer implies “divide” operation. The inheritance is provided to build hierarchical structure of objects and enable code reuse. df. This single function is sufficient for the very different arguments taken. First. } For one dimensional problem.2. It actually means to invoke matrix solver that use df as the left-hand-side matrix and “-f” as the right-hand-side vector. For example. God says “Animals eat food !” The “lion” goes to catch a zebra. We have seen that for data abstraction C++ provides class to organize data and functions into a coherent object. Henceforth. The default behavior of VectorSpace C++ Library is the LU decomposition. n > 1. and dx are all Scalar object of C0 type. each of polymorphic concrete types “Lion”. The impact of change to the code from one problem to the other is a routine rather than an exception. The user code segment for the declaration and instantiation of a class HeatQ4 is Workbook of Applications in VectorSpace C++ Library 277 . although you have the freedom to change the default setting to Cholesky decomposition (for symmetrical case only). We note that an even greater impact will be played out in the mixed formulation. f and dx are Vector object of C0 type with length “n” and df is a Matrix object of C0 type with size “n × n”. and “Whale”. Element Formulation At the very heart of finite element program is the element formulation. we derived from this class of “Animal” to form classes of “Lion”.Basics of Finite Element Method Step 3. The advantage of this higher level of intelligent is enormous. This part does every thing that is most relevant to the variational methods we have introduced in Chapter 3. We considers the impact of change by these two types of problems that will be played out in the element formulation.

} From this code. force. 58 “handle / body idiom”. Element_Formulation* HeatQ4::make(int en. i.} HeatQ4::HeatQ4(int en.. Global_Discretization&). The definition of this constructor is user customized. 1992... public: . enhanced by emulating symbolic language by C++1. Therefore.. Element_Formulation *rep_ptr. gd). and (3) p. let’s look at the fe. Global_Discretization& gd) {return new HeatQ4(en. Reading. } .. protected: virtual C0& __lhs() { return stiff. The symbol class Element_Formulation is responsible for doing all the chores including memory management and default behaviors of the element formulation. 315 “symbolic canonical form” in J... 70 “envelope / letter” idiom..e. Global_Discretization&).. 278 Workbook of Applications in VectorSpace C++ Library . Addison-Wesley. the variational formulation. “ Advanced C++: Programming styles and idioms”.O. Polymorphism: First.. The class Element_Formulation has a private data member “rep_ptr” (representing pointer) which is a pointer to an Element_Formulation type as 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Element_Formulation { . Global_Discretization&)”. (2) p. The Element_Formulation is like a symbol class for its actual content class— HeatQ4. Since the derived class HeatQ4 is publicly derived from the base class Element_Formulation. the contents of this constructor is the variational formulation of differential equation problem at hand. } .. } virtual C0& __rhs() { return force. Massachusetts. C0 stiff. C0& lhs() { return rep_ptr->__lhs().lib implementation of polymorphism. Global_Discretization&) : Element_Formulation(en. HeatQ4(int. the rep_ptr can point to an instance 1.. an instance of HeatQ4 has its own copy of Element_Formulation as its “header”. see (1) p.. the line 5 which is the declaration of the constructor of the heat conduction element formulation—“HeatQ4(int. }. The class Element_Formulation and the custom defined user class HeatQ4 are used hand-in-hand. }. We will get to the details of definitions for the constructor (line 8) at the end of this section.. } C0& rhs() { return rep_ptr->__rhs(). The content class HeatQ4 does what application domain actually required.Chapter 1 2 3 4 5 6 7 8 9 10 4 Finite Element Method Primer class HeatQ4 : public Element_Formulation { public: HeatQ4(Element_Type_Register a) : Element_Formulation(a) {} Element_Fomulation *make(int. in this code segment. gd) { . Coplien. .

 R ( u i ) ≡ KT ( u i ) R ( u i ) ∂u u i  –1 Eq. Global_Discretization&) { ul &= gd. That is at the T ˆ element level. We have explained the mechanisms built for polymorphism.δ u i = 0 ∂u u i Eq. to its derived class protected member functions __lhs() and __rhs(). a new class derived from class Element_Formulation is 1 2 3 4 class Nonlinear : public Element_Formulation { C0 ul. 4•12) defined as ∂R R i + 1 ≡ R ( u i + 1 ) = R ( u i + δ u i ) ≅ R ( u i ) + ------. 4•14 where both the tangent stiffness matrix K –1 ( u i ) and the residual vector R ( u i ) are functions of ui. Global_Discretization&)” to produce a pointer to HeatQ4 instance. in the present case. We also see that the two public member functions lhs() and rhs() are forwarding. by its delegate “rep_ptr”.element_free_variable(en).K(u) u (from Eq. For a nonlinear problem the solution is obtained from an iterative scheme u i+1 = ui + δui for the convergence of the residual vector R = F . Now we can consider how the impact of change bring out by nonlinear and transient problems can be accommodated under this design. we have the incremental solution δui as the solution of the simultaneous linear algebraic equations δui  ∂R  –1 = –  -----.Basics of Finite Element Method Symbol Element_Formulation rep_ptr Content Element_Formulation HeatQ4 Figure 4•6 Emulating symbolic language features using C++. the nodal values— u i must be available. This is done by invoking “Element_Formulation* HeatQ4::make(int. 4•13 From this approximated equation. void __initialization(int. forwarding to an instance of HeatQ4’s two protected virtual member function __lhs() and __rhs(). of HeatQ4. The default behaviors of these two protected virtual member function has been defined to return element stiffness matrix and element force vector. Therefore. } public: Workbook of Applications in VectorSpace C++ Library 279 .

From Eq.. 6 . // . 4•15 ˆ In this case.. We show the parabolic case here. Global_Discretization& gd) : Element_Formulation(en. // central difference θ = 0. // M + ∆tθK return the_lhs. gd). it is imperative to invoke its private member function “Nonlinear::__initialization(int.. 3•191 in Chapter 3 (page 253) we have ( M + ∆tθK ) u n + 1 = ( M – ∆t ( 1 – θ )K ) u n – f ˆ ˆ Eq.3.element_free_variable(en). gd) { __initialization(en.5 C0& Transient::__lhs() { the_lhs &= mass + dt*theta *stiff. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Transient::public Element_Formulation { C0 mass. When the class “Nonlinear” is defined. } C0& Transient::__rhs() { Element_Formulation::__rhs(). }. C0& __lhs(). . for this nonlinear element. ul. }.f. the use of inheritance for programming by specification is very straight forward. gd) { 9 __initialization(en. static double theta = 0. Global_Discretization&)” to setup the element nodal variables. In Chapter 5. the default force vector the_rhs += (mass -dt*(1-theta)*stiff)*ul. Global_Discretization&). C0& __rhs(). An example of a simple nonlinear problem is shown in Section 4. 7 }. void __initialization(int. The class “Nonlinear” inherits all the public interfaces of the class Element_Formulation. 11 }. 10 . the element nodal variables. the nodal values from the last time step— u n is also needed. For a transient problem. In addition. Global_Discretization&) { ul &= gd. gd). On top of that we have declared a private data member “ul”. .. we investigate state-of-the-art material nonlinear (elastoplasticity) and geometrical nonlinear (finite deformation problems). Global_Discretization& gd) : Element_Formulation(en.5. the polymorphic technique is much more complicated. // ( M – ∆t ( 1 – θ )K ) u n – f 280 Workbook of Applications in VectorSpace C++ Library ... dt = 0. Transient::Transient(int en. 8 Nonlinear::Nonlinear(int en. we also need to compute the mass (heat capacitance) matrix “M”.Chapter 4 Finite Element Method Primer 5 Nonlinear(int.2.01.. } public: Transient(Global_Discretization&).. In this case.

Element_Type_Register element_type_register_instance. An example of transient program is shown in Section4.1. 2. solved by a finite element method may apply different elements for different regions. // register element type // element type number “2” // element type number “1” // element type number “0” The element type register uses a list data structure.4. We number the last registered element’s element type number as “0”. If these two protected virtual member functions have been overwritten (lines15-23). We can have a “truss” element on certain parts of “planner” elements to simulated a strengthened structure.3. he needs to register multi-elements as 1 2 3 4 5 Element_Fomulation* Element_Formulation::type_list = 0. When an instance of Element_Formulation calls its public member functions “Element_Formulation::lhs()” and “Element_Formulation::rhs()”. 4. (double*)0. linear finite element problems.Basics of Finite Element Method 22 23 } return the_rhs. Workbook of Applications in VectorSpace C++ Library 281 . the default behaviors in the base class will be taken over by the derived class. the requests are forwarding to its delegates’ virtual member functions. // alias Zai &= Z[0]. Element Formulation Definition: Now we finally get to the core of the Element_Formulation. 4). Eta. The second argument is supplied with this number such as Omega_eh *elem. static T3 t3_instance(element_type_register_instance). Global_Discretization& gd) : Element_Formulation(en. Note that in the definition of class Element_Formulation the default behaviors of the last two protected member functions are through two virtual member functions to return element “stiff” matrix and element “force” vector as virtual C0& __lhs() { return stiff. 0. This number increases backwards to the first registered element in the “type_list”. qp). That is the definition of its constructor. } This is standard for the static. H1 Z(2. element_type_number. Element Type Register: A differential equation problem. We show an example of heat conduction four-node quadrilateral element 1 2 3 4 5 6 HeatQ4::HeatQ4(int en.2. static Truss truss_instance(element_type_register_instance). ena). Eta &= Z[1]. Zai. // natrual coordinates N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE(4. qp). When we define an element as introduced in page 271. we can choose triangular elements to cover some of the areas. gd) { Quadrature qp(2. static Q4 q4_instance(element_type_register_instance). elem = new Omega_eh(0. } virtual C0& __rhs() { return force. From user’s perspective. For example. The C++ idiom to implement the element type register is discussed in Section 4. while quadrilateral elements to cover the rest of the areas.

N[3] = (1-Zai)*(1+Eta)/4. h ) Γ and the essential boundary conditions j i j – a ( φ e .inverse(). J dv(d(X). H1 X = N*xl H0 Nx = d(N)*d(X). } These two default behaviors can be overwritten as in the class “Transient” in the above. N[2] = (1+Zai)*(1+Eta)/4. } The the “reaction” is added to the element force vector as C0 & Element_Formulation::__rhs() { the_rhs &= __reaction(). force &= (((H0)N)*q) | dv.0.lib” adopts the conventional treatment that the natural boundary conditions are taken care of at the global level in Matrix_Representation::assembly() where the user input equivalent nodal forces of natural boundary condition are directly added to the global force vector. Another example is that we might want to have different interpolation function to approximate the boundary conditions.Chapter 7 8 9 10 11 12 13 14 15 } 4 Finite Element Method Primer N[1] = (1+Zai)*(1-Eta)/4. q = 1. in Eq.det()). first we need to call “Matrix_Representation::assembly()” in main() program as assembly(FALSE). The “stiff” is the element stiffness matrix. // N a ( ξ. If you have mastered Chapter 3 already. stiff &= (Nx * k * (~Nx)) | dv. these lines should be completely transparent to you. 4•8 in page 269. The treatment of the third term is also conventional that when the Element_Formulation::__rhs() is called it automatically call Element_Formulation::__reaction() which is defined as C0 & Element_Formulation::__reaction() { the_reaction &= -stiff *gl. return the_rhs. i The treatment of the terms on natural boundary conditions ( φe . a square matrix of size (nen × ndf) × (nen × ndf) (“ndf” as number of degree of freedoms). double k = 1. if(force. requires some explanation. η ) = -.( 1 + ξa ξ ) ( 1 + η a η ) 1 4 N[0] = (1-Zai)*(1-Eta)/4. // coordinate transformation // derivative of shape functions // the Jacobian // conductivity and heat source // element stiffness matrix // element force vector The “xl” is the element nodal coordinates which is a C0 type Matrix object of size nen × nsd(number of element nodes) × (number of spatial dimension). The VectorSpace C++ Library is most heavily used in this code segment.rep_ptr()) the_rhs += force. since it concerns the subject of variational methods the most. // FALSE turns off nodal force loading 282 Workbook of Applications in VectorSpace C++ Library . // “gl” is the element fixed boundary conditions return the_reaction.0. φ e )u e . In such case. The “force” is the element force vector of size (nen × ndf). “fe.

A beginner of “fe. the flexibility does not come by sacrifying the organization or simplicity of the code.lib” can always study the same simple kernel code. Matrix Representation and Solution Phase The user’s code for the steps of matrix representation and solution phase is 1 2 3 4 5 6 7 8 9 int main() { . In between the Step 4c and Step 4d. and “ndf” is number of degree of freedoms assumed as “1” for simplicity). } // instantiation of Global_Discretization object We show an example illustrated in Figure 4•7. respectively (where “tnn” is the total number of node.u_h(). This global-element relation is also established in line 2. although Workbook of Applications in VectorSpace C++ Library 283 . Now we have shown that object-oriented programming does provide unprecedented flexibility to implement seemly incompatible problems in finite element method.lib” to encompass more advanced problems. A regular matrix solver provided in C0 type Matrix in Chapter 1 can be applied to solve this problem. The “code-reuse” and “programming by specification” can be repeated applied to the “fe. Most importantly.lib” relentlessly. The fixed degree of freedoms are then removed from the global stiffness matrix (with remaining size = 5 × 5) and global force vector (with remaining size = 5). The values for the fixed degree of freedoms can be retrieved from the program input of the problem. Matrix_Representation mr(gd). Step 4c.lib” may reside in the ever grander architecture un-disturbed.Basics of Finite Element Method Then. return 0. A global stiffness matrix is a square matrix of size tnn × ndf = 7 × 7 per side. Step 4a. This is done at line 2 when an instance of Matrix_Representation “mr” is initialized with an instance of Global_Discretization “gd”.lhs())). Step 4. This is done in line 5 where the global solution vector gd. gd. where the public member function “Matrix_Representation::assembly()” is called. The kernel code does not grow because of the irrelevant details have been added during the course of evolution of “fe. Step 4b. C0 u = ((C0)(mr. gd.gh_on_gamma_h()”. we can define boundary integration of these two terms to the element force vector.u_h() is updated with fixed degree of freedom “gd.. The maps in Step 4b are used to add element stiffness matrices and element force vectors to the global stiffness matrix and global force vector as in line 3. That is the line 6 where the same global solution vector gd. redefine “__rhs()” in user defined element.rhs())) / ((C0)(mr. cout << gd. the variational problem has been reduced to a matrix solution problem.gh_on_gamma_h(). and element force vector to global force vector can be constructed element by element. Then.u_h() = u. In the definition of the user element.assembly(). The mapping relationship of element stiffness matrix to global stiffness matrix. and global force vector is of size tnn × ndf = 7. while the very kernel of the “fe.. The solution is in the order of free degree of freedom number which is then mapped back to the global degree of freedom number for output of the solution.u_h() is updated with the solution “u”.u_h() = gd. the global stiffness matrix and global force vector are used for linear algebraic solution of the finite element problem as in line 4. mr. Step 4d. The basic idea is just like we can overwrite the virtual functions “__lhs()” and “__rhs()” for the transient problem.

element to global mapping. Step 3. Step 2. respectively Element 0: 0 1 2 4 5 0 1 2 4 5 Step 4c: assembly of all elements Step 4d: map equation number to global degree of freedom number global degree of freedom number equation number 0 0 1 2 4 5 1 2 3 4 5 6 7 Figure 4•7 Element connectivity example.Chapter 4 Finite Element Method Primer Step 4a: eliminate fixed degree of freedoms 0 1 2 3 4 5 6 7 Element connectivity 2 0 1 1 3 3 4 4 5 2 6 0 7 0 1 2 3 4 5 6 7 Step 4b: map element stiffness matrix and element force vector to global matrix and global vector. Step 1. assembly all elements. elimination of fixed degree of freedoms. Element 1: Element 2: Element 3: Element 4: 284 Workbook of Applications in VectorSpace C++ Library . and Step 4. equation number to global degree of freedoms number.

. In abstract mathematical form.e.. and the nodal variables u depends on how nodes and element. We can draw a tetrahedron with the four vertices represent the four components and the six edges represents their mutual relations (see Figure 4•8). Ω h . i. 4•1.Basics of Finite Element Method there are many matrix computational methods specifically designed for the finite element method. In a planner graph. The global stiffness matrix and global force vector can be replaced by corresponding special matrix and vector. Some problems are unraveled only after first model has been proposed.lib”. to reduce it to a lower dimension. These methods are not supported by “fe. However. Examples of these changes are mixed and hybrid method and contact mechanics. “ Numerical solution of partial differential equations by the finite element method”. (2) variables u h . In this perspective.. we need to applied dependency breakers (to be discussed later) to the graph to reduce it to a lower dimension. If there is any such difficulty. logically. the modeling in the previous section provides us the materials to begin with for analysis and design process. and nested dissection1. Johnson. Dependency Graph The four major components in the modeling of finite element method are (1) the discretized global domain Ω h . Workbook of Applications in VectorSpace C++ Library 285 .. they all belong to the category of constrained optimization problems. object-oriented programming provides mechanisms to deal with impact of change for a swift evolution of “fe. ˆ ˆ conceptually u h ( φ. an experienced programmer will point out that the nature of the programming is more like an iterative process that one goes over again and again from analysis/design to modeling then re-analysis/re-design and then to re-modeling. and (4) matrix representation (MR).. The first thing we can do is this tetrahedron can be reduced to a planner graph.3 Object-Oriented Analysis and Design of Finite Element Method As in many books on object-oriented analysis and design have suggested. The 1. For a component. C.lib”. “ . provided you have code all the needed interfaces for retrieving the components in the special forms of the global matrix and vector.. we define that the object-oriented analysis is to understand the object dependency relation. user defined string to identify special matrix . we represent a component as a node. To name a few. the number of arrows pointing towards the node is called degree of entrance. meaning that no edge among them can cross each other. and their relations as the arrows. are defined. profiled sparse matrix. This step can not always be done. and the object-oriented design is the discipline to manage the potentially complicated dependency relation among objects. Just as in the Element_Formulation. u ) . We briefly explain these dependency relations. The entrance number “0” says the global discretized variables u h depends on the global discretization Ω h . we reserve an entry point to declare the Matrix_Representation as Matrix_Representation mr(gd. Press Syndicate of the University of Cambridge. even before the modeling in the previous section. We may think of analysis and design probably is the first thing to consider.e..1. 4. However.. an arrow stands for a dependency relation that the node on the starting point of an arrow depends on the node at the ending point of the arrow. In the convention of object-oriented method. UK. u h is defined as interpolation of nodal variables as in Eq. i.”). frontal method. (3) element formulation (EF). 1987.

the variable number in MR is different from the number of global degree of freedom. The numbers marked are the entrance numbers. u h depends on the knowledge of MR. we need to specify the element type number. The entrance number “3” is a redundant dependency relation. but it isn’t the best for human mind to comprehend. we proceed to sort out the planner graph into a graph level structure. We would like to change the graph into a level structure such as a tree or even better a simple chain. This can be transformed to a planner graph with arrows to show dependency relation. and after we get solution from solving MR we need to map the solution vector from MR back to u h . 286 Workbook of Applications in VectorSpace C++ Library . The entrance number 4 is a similar redundant relation with one more step of MR depending on EF. Since u h depends on Ω h and EF depends on u h . It is much easier to understand if the relation is hierarchical. The entrance number 5 and 7 show a mutual dependency relation that MR depends on u h for MR is just the lhs and rhs to solve for u h . When we define elements. Therefore. Therefore. 7 MR Graph Level Structure A complicated network such as the one in Figure 4•8 may look aesthetically pleasant. we can conclude that EF must depend on Ω h . The entrance number “2” says the matrix representation depends on the element formulation. entrance number “1” says the element formulation depends on the global variables u h . The entrance number “6” has Ω h depends on EF. since the element stiffness matrix and element force vector are all calculated corresponding to the interpolated value of the element ˆ nodal variables u e . A clique is formed if we starts the flows of dependency steps from node MR to EF then to u h it goes right back to MR itself.Chapter 4 Finite Element Method Primer tetrahedron Ωh uh planner graph 1 uh 0 Ωh 3 6 EF 2 5 4 EF MR Figure 4•8Tetrahedron to show four components on the vertices with six edges. since element formulation supplies the element stiffness matrices and element force vectors to be mapped to the global matrix and global vector. The members in a clique depend on each other so strongly that they are not separable. These are same structures that we always preferred in procedure programming method. since the fixed degree of freedom is excluded from the MR. In our mind we only need to picture a simple sequence of states and top-dwon relations.

For example one can just bear in mind that only components that are lower in the hierarchy depend on those on the above. This breaking of dependency relations can be done with the forward declaration in traditional 1. tested and maintained all together. Firstly. The dynamical interaction patterns among the components seems to have a life of its own. Ω h . Workbook of Applications in VectorSpace C++ Library 287 . Now not only the graph is simple to understand for human mind. therefore. which make the graph not to be a level structure. There are so many cliques among them. Secondly. The Ω h has highest degree of entrance that means it should be at the highest root of class hierarchy. Pointer to a Forward Declaration Class: We can apply a traditional C technique to break the dependency relation caused by entrance number 7. One nodes can lead to the other and then back to itself. Therefore. by escalation and demotion1 of nodes on the planner graph in Figure 4•8. u h and EF have same degree of entrance. However. Divide and conquer is the principal strategy that we always need to deploy in the development. we need to map this internal order of the Matrix_Representation back to the order of global nodal degree of freedoms u h according to the specification from the problem. we just mark them as such. The interaction among the components can be understood easier. 4. if there are exception. are drawn as light arrows. Since the EF explicitly depends on u h . Lakos. EF. there are still two un-resolved entrances (entrance 6 and 7 pointing downwards) in the left-hand-side of Figure 4•9. the complicated network of software components such as the one in the left-hand-side of Figure 4•8 will be extremely difficult to follow. Reading. For output of solution. MR. The order in the class hierarchical is. and 5. Therefore. the interfaces of the software components are much more simplified. but also it will have a profound impact on the organization of the software components. The graph level structure in the right-hand-side of Figure 4•9 means that now these processes can be done in a more modulized fashion from top level 0 down to level 3 incrementally. is corresponding to the order of variable number in the Matrix_Representation. These redundant dependencies are first to be eliminated. That is the output for solution u h needs the knowledge of class Matrix_Representation. the model based on the graph level structure will be less error proned. “Large-scale C++ software design”. And . J. We discuss two dependency breakers in the followings. u h . in the rest of this section we will explore C++ levelization idioms1 that help us to break these two dependency relations. with a simplified dependency hierarchy. the complicated network demands all module to be developed. The sequence of events can be acted out differently every time. The redundant relations.Basics of Finite Element Method First we compare the degree of entrance of the four components (see TABLE 4•1) to transform. Massachusetts. in the main(). On the other hand. Component Ωh uh EF MR Degree of entrance 3 2 2 1 TABLE 4•1 Degree of entrance of the four components. Next. into a graph level structure. as the order shown in TABLE 4•1 The pseudo-level structure is shown in the right-hand-side of Figure 4•9. entrance numbers 3. such as entrances 6 and 7. Addison-Wesley. testing and maintenance of a program. The the order of the solution vector “u”. 1996. u h is to be escalated and EF is to be demoted. then.

“u_h. 7 Matrix_Representation* &matrix_representation() { return mr. 18 .... are shown in the followings. “matrix_representation.cpp”.cpp”.. “u_h.. 16 protected: 17 Global_Discretization &the_global_discretization.h”. 19 public: 288 Workbook of Applications in VectorSpace C++ Library .cpp” 12 #include “u_h. Four separate files “u_h. Ib. The entrances 6 and 7 remained.. 4 .h” 13 .h” 14 class Matrix_Representation { 15 . “matrix_representation.Chapter Level 0 4 Finite Element Method Primer (a) levelization 0 3 uh Ωh (b) simplify to a chain 4 Ωh 0 eliminate redundant dependancies uh Level 1 1 5 6 EF 2 MR 7 1 EF 2 7 MR 6 Level 2 Level 3 Figure 4•9levelization of non-hierachical network into a level structure then to a chain. IIa. 11 }.. } 8 U_h& operator=(C0&). 5 public: 6 . Ia..h” and “matrix_representation.. 10 U_h& operator-=(C0&). 2 class U_h { 3 Matrix_Representation *mr. “u_h. We need to apply C++ levelization idioms to reslove them.h” 1 class Matrix_Representation.. C. 9 U_h& operator+=(C0&).

In class U_h. } 31 U_h& U_h::operator+=(C0& a) { .... A less dramatic scenario of using a member pointer is that a developing process is iterative and the files always need to be compiled many times. Traditional C language (note that class can be viewed as a special case of struc) provides mechanism to break this mutual dependency relation by forward declaration such as in line 1 that the class name Matrix_Representation is introduced in the name scope of the translation unit “u_h. IIb. The changes in “.u_h(). we at most refer to a pointer of class Matrix_Representation. That is to include the “.h” 24 #include “matrix_representation.cpp” files of the class Matrix_Representation do not affect the object code of class U_h module. Certainly. The “make” command may trigger tens of hours in compile time to update all modules that are depending on it..u_h().. } The class U_h and class Matrix_Representation are actually depend on each other. “matrix_representation. Therefore we have seen a most primitive form of a compilation firewall been set to separate the compile-time dependency among source files. When we define the constructor of the class Matrix_Representation. Now a programmer in the developer team can compile and test “u_h.h” 25 void Matrix_Representation::__initialization(char *s) { 26 if(!(the_global_discretization. we can only find a way to get around it. They may have thousands of files..matrix_representation()) ) 27 the_global_discretization. which is demanded by the problem domain. the first line of the constructor is to call its private member function “__initialization(char*)”. the dependency relation of entrance number 7 exists.h” extension files. One scenario of using the forward declaration of a class and using a member pointer to it is after the entire product has been completed and sale to the customer. but how do we re-connect them as the problem domain required. Therefore. This private member function set up the current instance of Workbook of Applications in VectorSpace C++ Library 289 .h”.cpp”. In yet another scenario. 29 } 30 U_h& U_h::operator=(C0& a) { . } 32 U_h& U_h::operator-=(C0& a) { . 21 . Not for long you will refuse to do any change at all. In a huge project. not its member data or member functions are to be used in the definition of class U_h. We successfully break this particular dependency and make class U_h an independent software module. on the condition that only the name of class Matrix_Representation. the implementations of them in the “cpp” extension files will require the knowledge of their definitions. class U_h module does not need to be recompiled every time that class Matrix_Representation is changed. 28 . It will be ridiculous that when an unimportant change of a tiny file higher in the dependency hierarchy is made. not an actually instance of the class Matrix_Representation.. this same technique insulates end-users from accessing the class Matrix_Representation directly. because the translation unit has no knowledge yet of what class Matrix_Representation really is.. During developing cycles. without having to define class Matrix_Representation at all.. which is only an address in the computer memory..cpp” separately. when class Matrix_Representation is intended to be encapsulated from end-users.h” and “. 22 }. such as the one developed in Mentor Graphics we mentioned earlier.cpp” 23 #include “u_h.Basics of Finite Element Method 20 void __initialization(char *s). if we want to change the definition and implementation of class Matrix_Representation we do not need to recompile the file “u_h.matrix_representation() = this.

We break up the dependency relation using forward declaration now we reconnect them when an instance of class Matrix_Representation is initiated. see autonomous generic constructor in J.h”. nodes_per_element. Element_Type_Register element_type_register_instance. Therefore. because the independent module class U_h has no idea what is a class Matrix_Represenation.Chapter 4 Finite Element Method Primer Matrix_Representation as the pointer to Matrix_Representation in the class U_h. the linker will refuse to build the executable module and will complain that these three operators. Element_Formulation *next. “ Advanced C++: Programming styles and idioms”. O. that was broken at compile-time for making an independent module of class U_h. static T3 t3_instance(element_type_register_instance). had we not defined these three operators anywhere.cpp” with other member functions of class Matrix_Representation. They are not defined in “u_h. “+=”. This user interface design itself breaks the dependency of the definition of an element on element types. are push down the hierarchical levels. at link-time. declared in “u_h. public: static Element_Formulation *type_list. let alone to access its information for the mapping. The C++ technique to implement this design is the autonomous virtual constructor1. 1992. AddisonWesley. element_type_number.. node_number_array). Furthermore. Reading. Element Type Register: In page 281. are un-resolved external references. static Q4 q4_instance(element_type_register_instance). material_number. static Truss truss_instance(element_type_register_instance). Element_Formulation(Element_Type_Register) : 1. we have discussed the element type register from user’s code segment as registration by 1 2 3 4 5 Element_Fomulation* Element_Formulation::type_list = 0. at link-time.cpp” with other class U_h member functions. class Element_Formulation { Global_Discretization& the_global_discretization. Let’s first look at the definitions of the class Element_Formulation 1 2 3 4 5 6 7 8 class Element_Type_Register { public: Element_Type_Register() {} }. and then the number increases backwards to the first registered element in the “type_list”. The last registered element type number is “0”. This element type numbers are referred to when we define the element as Omega_eh *elem = new Omega_eh(element_number. 290 Workbook of Applications in VectorSpace C++ Library . Certainly. the definitions of three public member operators “=”.. . This closes the cyclic dependency relation. and “-=”. these three public member functions of class U_h are defined in “matrix_representation. Massachusetts. which map the equation number of solution vector back to global degree of freedoms for output. Coplien. // element type number “2” // element type number “1” // element type number “0” The element types are registered in a list data structure.

the four nodes are actually the software modules in “fe. if the product is intended to be open. is re-connected at the run-time by the late-binding technique supported by C++. and form an instance of Element_Formulation. Element_Formulation ef = element_type->create(element_no. which is a discretization made to both the domain and the variables. For example. i++) element_type = element_type->next. not just physical dependency relations and technical requirements to separate them. Abstraction is put down to a granularly lower level to facilitate the re-use of each composite class and therefore more flexibility for change. type_list = this. in line 1.lib” that large-scale change to the backbone structure of the program is to be permissible. One step is the finite element approximation. it makes all sense to combined the level 0 and level 1 together and called it a Global_Discretization. The class Element_Type_Register. On the contrary. This element_type_number information is used in “Matrix_Representation::assembly()” as 1 2 3 Element_Formulation *element_type = Element_Formulation::type_list. for(int i = 0. the process of abstraction). such as “fe. the choice of the composite class is somewhat more arbitrary than that of composite vertices in graph theory.lhs()” and “ef. the decision depends on the intent of the final product. The request in line 3 is dispatched to a user defined element class. it can be used as “ef.rhs()” to query information. Since “make()” is virtual and to be redefined in the derived class. and the other step is the solution in its matrix form.e. Globa_Discretization&). . say “ef”. Workbook of Applications in VectorSpace C++ Library 291 . as long as it is conceptual meaningful to emphasis the essential and eliminate the irrelevant (i.Basics of Finite Element Method 9 10 11 12 13 }. virtual Element_Formulation* make(int. In this way. the designer may want to emphasize that the finite element method is mainly consist of only two steps. A class dependency graph. Composite Class from a Dependency Graph In Figure 4•8 and Figure 4•9.. Line 3 is to compute the Element_Formulation. is a dummy one that is used like a signature in line 8 to indicate that the instance of class Element_Formulation generated is for element type identifier. For a product designed to be used as a canned program. deliberately broken for the software modulization. the cyclic dependence of an element on element formulation. } Element_Formulation& create(int. i < element_type_number.lib” which are consist of the classes. Global_Discretization&). In software design. The virtual function mechanism is usually referred to as the latebinding technique at run-time. The task of “create()” is to call “make()” forward by its delegate “rep_ptr->make()”.. the_global_discretization(Globa_Discretization()) { next = type_list. the abstraction can be put to a coherently higher level in which all the details are encapsulated from the end users as much as possible.. the_global_discretization). not including all classes. The definition of a compoiste class is similar to the partitioning of the graph to a (quotient) tree structure with sets of composite vertices as composite nodes. We can even combined the Global_Discretization class and Element_Formulation class to form a new conceptual class of “Finite_Element_Approximation”. Sometimes. In this case. The coalescence of several composite classes into yet higher level of composite class shows that the recognition of a composite class may depend on design decision on what conceptual abstraction the designer wants to emphasize (an art). and the static member type_list embedded in the Element_Formulation will be maintained automatically. is shown in Figure 4•10. The entire picture is much more complicated one.

h ∈ Γ h Global_Discretization Level 2 EF User Defined Elements Finite_Element_Approximation Level 3 MR Global Tensors Element Tensors Finite_Element_ Approximation MR Globla_Discretization EF MR Ωh uh EF MR Figure 4•10Composite class in the hierachical level structure. 292 Workbook of Applications in VectorSpace C++ Library .Chapter 4 Finite Element Method Primer Node h Ωe Level 0 Ωh Level 1 uh g ∈ Γ g.

lib” We summarize the Section 4. Global_Discretization&). oh). } UserEL::UserEL(int en. Omega_h& oh) { __initiialization(df. // define b. Global_Discretization& gd) { return new UserEL(en.4 A Program Template for Using “fe. Element_Formulation* UserEL::make(int en.1. } gh_on_Gamma_h::gh_on_Gamma_h(int df.lib is the server that provides the basic mechanisms in finite element method for user programs to implement their own design policies in the vast area of finite element problem domain. It is very much like we have an extended C++ language features that are specialized in finite element method.lib”. since finite element method requires a lot of user input to specified the problem. The fancy term client-server package may even more appropriate for “fe.c. gd).. On the other hand.lib to write finite element programs. . Global_Discretization&). Under such model. the fe. In GUI programming.lib” is a framework-based package very similar to if you are writing a graphic user interface (GUI) program.lib acts much like a database engine that you write a database language to define the database schema. the fe.Basics of Finite Element Method 4... The client-server packages for writing business applications provide a high-level library for routine database services and GUI interfaces. Global_Discretization& gd) : Element_Formulation(en.. manipulate the data and query its contents. // define elements . UserEL(int. gd) { Workbook of Applications in VectorSpace C++ Library 293 . }. A user program template is illustrated in the followings //========================================================================== // Step 1: Global_Discretization //========================================================================== 1 2 3 4 5 6 7 8 Omega_h::Omega_h{ // define nodes . there are some routine code that you need to incorporate with its framework to make the GUI kernel up and running. “fe. } // define discretizaed global domain // define boundary conditions // initialize internal data structure //========================================================================== // Step 2: Element_Formulation //========================================================================== 9 10 11 12 13 14 15 16 17 18 class UserEL : public Element_Formulation { // define user element public: UserEL(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation *make(int.1 with a template for using fe...

oh). oh).assembly(). 23 static UserEL userel_instance(element_type_register_instance). gh.gh_on_gamma_h().. 30 Matrix_Representation mr(gd). 31 mr. uh).1. 20 } 21 Element_Formulation* Element_Formulation::type_list = 0. 32 33 gd. return 0.u_h() = gd.rhs())) / ((C0)(mr.Chapter 4 Finite Element Method Primer // define element formulation constructor 19 . 27 gh_on_Gamma_h gh(ndf. 35 36 } // instantiation of Global_Discretization // assemble the global matrix // solution phase // update solution // output solution Many segments and their variations of this template have been discussed in 4. The rest of this Chapter consists of concrete examples of writing user programs using this template.lhs())). 34 cout << gd.2.h_h(). 29 Global_Discretization gd(oh. 22 Element_Type_Register element_type_register_instance. 28 U_h uh(ndf. gd. C0 u = ((C0)(mr.u_h(). = u. 294 Workbook of Applications in VectorSpace C++ Library .. 26 Omega_h oh. // register elements //========================================================================== // Step 3: Matrix_Representation and Solution Phase //========================================================================== 24 int main() { 25 int ndf = 1.

4•19 The last identity is obtained.2. McGraw-Hill.-------.One Dimensional Problems 4. j φe ) = ∫  ------- dx 0 i j dφ e dφ e -------- dx dx  Eq. In more general cases that they are not homoge- 1. Inc. and u’(1) = 0 The Galerkin weak formulation is a(φei. Dirichlet boundary conditions—u(0) = u(1) = 0 2.2 One Dimensional Problems We intent to go through many proto-type problems. f) = j d 2 φe ∫  φei ----------. Neumann boundary condition—u’(0) = u’(1) = 0 3.(φei . 4•10 we have the element stiffness matrix as 1 ij ke = i a( φe. f) + i ( φe . 0 < x < 1 Eq. 4•9 and Eq. in one dimension. h ) Γ and the third term – a ( φ e . Workbook of Applications in VectorSpace C++ Library 295 . 4•16 with three sets of different boundary conditions 1. 367-371 in J. Mixed boundary conditions—u(0) = 0. Dirichlet boundary conditions: From Eq. j j φ e )u e = ∫ ( φei cos πx )dx 0 Eq. 4. 3•55 of Chapter 3 in page 201)1 du dx 2 2 – = cos πx. since the essential and natural boundary conditions are all homogeneous the second j i i j term ( φ e . Reddy. 4•18 and the element force vector as 1 i fe = i ( φe . h )Γ – i a ( φe.-------. φ e )u e always vanish. φej) . p. “Applied functional analysis and variational methods in engineering”. 4•17 1.N. 1986. to demonstrate a wide mathematical variety in the finite element method.– φei cos πx dx =  dx 2  0 1 i j j dφ e dφ e dφ e ∫  – -------.1 A Second-Order Ordinary Differential Equation (ODE) Considering a second-order differential equation we have solved using Rayleigh-Ritz method (Eq.+ φei cos πx dx  dx dx  0 i j dφe dφ e = 0 Eq.+ φei cos πx dx + φei ------- dx dx  dx 0 1 1 1 = 0 ∫  – -------.

0 < x < 1 The elements are defined with global node number associated with the element as 1 2 3 4 5 6 7 int ena[2]. the default behaviors of “fe. ena). 296 Workbook of Applications in VectorSpace C++ Library . spatial_dimension_number. 4•1) and i x i (Eq. as 1 2 3 4 5 6 7 #if defined(__TEST_MIXED_BOUNDARY_CONDITION) gh_on_Gamma_h::gh_on_Gamma_h(int df. element_type_number.lib” will deal with these two terms behind the scene as long as you have not overwritten them as we have discussed in the previous section. We use the program template in the previous section. Omega_h& omega_h) { __initialization(df. the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet. matrial_type_number. The finite element program using VectorSpace C++ Library and “fe.( 1 – ξ ).Chapter 4 Finite Element Method Primer nous conditions. &v).add(elem).add(node). This constructor for the discretized global domain defines nodes with their node numbers and nodal coordinates as 1 2 3 4 5 double v = (double)i/(double)element_no. the_gh_array[node_order(node_no-1)][0] = 0. Omega_eh* elem = new Omega_eh(element_number. omega_h).e. the_omega_eh_array. 4•20 This is the linear interpolation functions we have used for integration of a line segment in Chapter 3 (Eq. and φ e = -.lib” to implement the linear element is shown in Program Listing 4•1. ena[0] = first_node_number. we define nodes and elements in “Omega_h::Omega_h()”. Three sets of boundary conditions are (1) Dirichlet (2) Neumann. ena[1] = ena[0]+1.. an isoparametric element as coordinate transformation rule x ≡ φ e e 1 1 0 1 φ e = -. Node *node = new Node(global_node_number. // nodal coordinates. 3•11 of Chapter 3). 3•10 and Eq. i.0. First.( 1 + ξ ) 2 2 Eq. Linear Line Element h i ˆi We can choose the linear interpolation functions for both variable interpolation u e ≡ φ e u e (Eq. The corresponding code segments can be turned on or off with a macro definitions set.0. number_of_node_per_element. the_gh_array[node_order(0)][0] = 0. and (3) Mixed. the_node_array. 4•6). at compile time. the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Neumann.

H1 X = N*xl. for(int i = 0. static const int spatial_dim_no = 1. cout << gd. the_node_array. } Definte discretizaed global domain define nodes define elements define boundary conditions u(0) = u(1) = 0 instantiate fixed and free variables and Global_Discretization Define user element “ODE_2nd_Order” 1d Gauss Quadrature N0 = (1-ξ)/2. 2). C0 u = ((C0)(mr. J dv(d(X)). }. Omega_h oh. the_omega_eh_array. static const int element_no = 8. spatial_dim_no. H1 Z(qp). Global_Discretization&).u_h() = u.-------- dx . int. N[1] = (1+Z)/2. static ODE_2nd_Order ode_2nd_order_instance(element_type_register_instance). qp).C. i++) { double v. for the differential equation . } int ena[2]. oh). ODE_2nd_Order(int. N1 = (1+ξ)/2 coordinate transformation rule N.x the Jacobian i j dφ e dφ e ij i i k e =∫  -------. Quadrature". gd. U_h uh(ndf.add(node). i < element_no.h" static const int node_no = 9. } static const double PI = 3. Omega_h& omega_h) { __initialization(df. Workbook of Applications in VectorSpace C++ Library 297 . i < node_no. ODE_2nd_Order::ODE_2nd_Order(int en. H0 Nx = d(N)(0)/d(X).gd). Global_Discretization gd(oh. ena[1] = ena[0]+1. Element_Formulation* ODE_2nd_Order::make(int en.rhs())) / ((C0)(mr.u_h() = gd. oh). N[0] = (1-Z)/2.add(elem). 0.dsw” (in case of MSVC) under directory “vs\ex\fe”). output Listing 4•1 Dirichlet boundary condition u(0) = u(1) = 0. Node* node = new Node(i. Matrix_Representation mr(gd). Omega_eh* elem = new Omega_eh(i. N=INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int.One Dimensional Problems #include "include\fe. Global_Discretization& gd) { return new ODE_2nd_Order(en. uh). 0. gd) { Quadrature qp(spatial_dim_no. ena). v = ((double)i)/((double)element_no). mr. Omega_h::Omega_h() { for(int i = 0. Global_Discretization&). Global_Discretization& gd) : Element_Formulation(en. } } gh_on_Gamma_h::gh_on_Gamma_h(int df. stiff &= (Nx * (~Nx)) | dv.u_h(). the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet.assembly().u” = f (project: “2nd_order_ode” in project workspace file “fe. and f e= ∫ φ e cos πx dx  dx dx  0 0 1 1 register element Matrix Form assembly all elements solve linear algebraic equations update solution and B.gh_on_gamma_h(). i++) { ena[0] = i. } Element_Formulation* Element_Formulation::type_list = 0. return 0. &v). 2. the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Dirichlet. gh_on_Gamma_h gh(ndf.lhs())). gh. omega_h). force &= ( ((H0)N)*cos(PI*((H0)X)) )| dv. 2. 1. static Element_Type_Register element_type_register_instance.14159265359. } class ODE_2nd_Order : public Element_Formulation { public: ODE_2nd_Order(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation *make(int. int main() { const int ndf = 1. gd.

The default constraint value is “0. } #endif The Dirichlet boundary conditions is taken as the default macro definition. the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet. The constraint type selector is the “operator () (int dof)”. The default constraint type is Neumann condition. } #else gh_on_Gamma_h::gh_on_Gamma_h(int df. the_gh_array[node_order(node_no-1)][0] = 0. lines12-15. the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Neumann.0. the_gh_array[node_order((node_no-1)/2)](0) = gh_on_Gamma_h::Dirichlet. and the results should be the same. }. In other words. 25.gd). Omega_h& omega_h) { __initialization(df. the_gh_array[node_order(node_no-1)][0] = 0. Element_Formulation* ODE_2nd_Order::make(int en. 298 Workbook of Applications in VectorSpace C++ Library . because the solution is not unique under such boundary conditions only. omega_h).0. the_gh_array[node_order(0)][0] = 0.0”. you can eliminate lines 5-7.Chapter 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 4 Finite Element Method Primer } #elif defined(__TEST_NEUMANN_BOUNDARY_CONDITION) gh_on_Gamma_h::gh_on_Gamma_h(int df. Global_Discretization&). 23. and lines 17. the_gh_array[node_order(0)][0] = 0. the_gh_array[node_order((node_no-1)/2)][0] = 0. We can assign type of constraint to the corresponding degree of freedom as “gh_on_Gamma_h::Neumann” or “gh_on_Gamma_h::Dirichlet”. Global_Discretization&). Omega_h& omega_h) { __initialization(df.0. Global_Discretization& gd) { return new ODE_2nd_Order(en. omega_h).0. The constraint value selector is the “operator [ ](int dof)”. the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Neumann.lib” requires the following codes to ochestrate the polymorphic mechanism of the Element_Formulation to setup the element type register. “fe. The added essential boundary conditions on the middle point of the problem domain (line 16. ODE_2nd_Order(int.0. } Element_Formulation* Element_Formulation::type_list = 0. the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Dirichlet. and 17) are necessary for the Neumann boundary conditions for this problem. For a user defined class of “ODE_2nd_Order” derived from class Element_Formulation we have 1 2 3 4 5 6 7 8 9 10 class ODE_2nd_Order : public Element_Formulation { public: ODE_2nd_Order(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation *make(int.

mr. the tensor product operator “H0& H0::operator%(const H0&)” in VectorSpace C++ can be used for expressing dφ e dφ e k e =∫  ------.lhs())). 8 N[0] = (1-Z)/2.One Dimensional Problems 11 static Element_Type_Register element_type_register_instance. // ke =∫  -------. uh). Quadrature". 2). Lines 10 and 11 setup the data for registration and Line 12 register the element formulation “ODE_2nd_Order”.assembly(). // the Jacobian. gh. // global discretizaed domain—Ω h // fixed variables — g ∈ Γ g. // 1d.14159265359. and   14 0 } i i f e= ∫ φ e cos πx dx dx dx 0 For the element stiffness matrix. dx  dx dx  0 1 Eq. 2/*nen*/. U_h uh(ndf. 2-pts Gauss quadrature 5 H1 Z(qp). qp).rhs())) / ((C0)(mr. 4•21 as stiff &= (Nx%Nx) | dv. C0 u = ((C0)(mr. oh). The instantiation of global discretized domain.x 11 J dv(d(X)). oh). and matrix representation and solution phase are taken directly from the template without modification 1 2 3 4 5 6 7 8 9 int main() { const int ndf = 1. fixed and free variables. Global_Discretization& gd) 3 : Element_Formulation(en. N[1] = (1+Z)/2. gh_on_Gamma_h gh(ndf. Matrix_Representation mr(gd). h ∈ Γ h // free variables— u h // the class Global_Discretization // assembly all elements // matrix solver Workbook of Applications in VectorSpace C++ Library 299 . instead of “stiff &= (Nx* (~Nx)) | dv. i j dφe dφ e ij 13 1 force &= ( ((H0)N)*cos(PI*((H0)X)) )| dv. Omega_h oh.”. // N0=(1-ξ)/2. 2 ODE_2nd_Order::ODE_2nd_Order(int en.-------- dx . 1/*nsd*/.⊗ ------. int. // coordinate transformation x ≡ N i x e 10 H0 Nx = d(N)(0)/d(X). // N. Line 5 is the constructor for class ODE_2nd_Order where we defined the user customized element formulation as 1 static const double PI = 3. N1 = (1+ξ)/2 i 9 H1 X = N*xl. 12 static ODE_2nd_Order ode_2nd_order_instance(element_type_register_instance). X. // natural coordinate—ξ 6 N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( // “shape functions” 7 "int.ξ 1 12 stiff &= (Nx * (~Nx)) | dv. gd) { 4 Quadrature qp(spatial_dim_no. Global_Discretization gd(oh.

( 1 – ξ ).add(node).6 0.05 0.02 Neumann 0.u_h(). “gh” and “uh”.8 1 -0.u_h() = gd.gh_on_gamma_h(). i < node_no.8 1 0. Node* node = new Node(i.u_h() = u. 4•1) and coordinate i x i (Eq.Chapter 4 Finite Element Method Primer // update free and fixed degree of freedom // output solution 10 gd.( 1 + ξ ) 2 2 Eq. &v).2 Figure 4•11 The results from eight linear elements for (1) Dirichelt (2) Neumann and (3) Mixed boundary condtions for the second-order ordinary differentail equation. gd. φ e = ( 1 – ξ ) ( 1 + ξ ) and φ e = -. 12 return 0.4 0. 13} The instances of global discretization.05 0. 3•22).6 0.1 -0.15 -0.6 0. “oh”. static const int spatial_dim_no = 1. The finite element program using VectorSpace C++ Library and “fe. and continuous curves are analytical solutions. 11 cout << gd. static const int element_no = 2. Line segments with open squares are finite element solutions. and fixed and free variables. respectively.1 -0. 4•22 These are the same quadratic interpolation functions in the Chapter 3 (Eq.01 0. 4•6) are transformation rule x ≡ φ e e –ξ ξ 0 1 2 φ e = ----. v = ((double)i)/((double)(node_no-1)). Quadratic Line Element h i ˆi The quadratic interpolation functions for both variable interpolation u e ≡ φ e u e (Eq.lib” to implement the quadratic line element is shown in Program Listing 4•2.2 -0.02 -0.05 0.4 0. Dirichlet 0. 300 Workbook of Applications in VectorSpace C++ Library . “gd”. are then all go to instantiate an instance of class Global_Discretization.2 -0. spatial_dim_no. The results of using the linear line element for the second order differential equation in finite element method are shown in Figure 4•11.01 0. the_node_array. The definitions of 5 nodes and 2 quadratic elements are 1 2 3 4 5 6 7 8 static const int node_no = 5.1 Mixed 0.8 1 0.4 0.2 -0. Omega_h::Omega_h() { for(int i = 0. i++) { double v.

Omega_h oh. qp). J dv(d(X)). N[0] = -Z*(1-Z)/2.14159265359.lhs())). N=INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int. int main() { const int ndf = 1.gh_on_gamma_h().rhs())) / ((C0)(mr. static ODE_2nd_Order_Quadratic ode_2nd_order_quadratic_instance(element_type_register_instance).h" static const int node_no = 5. the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet. Global_Discretization& gd) { return new ODE_2nd_Order_Quadratic(en.C. and fei= ∫ φei cos πx dx  dx dx  0 0 1 1 register element Matrix Form assembly all elements solve linear algebraic equations update solution and B. 3. N[2] = Z*(1+Z)/2. gd. 3. Global_Discretization&).assembly(). i < node_no. Quadrature". ODE_2nd_Order::ODE_2nd_Order_Quadratic(int en.add(elem). stiff &= (Nx * (~Nx)) | dv.u_h() = u. output Listing 4•2 Quadratic Element for Dirichlet boundary condition u(0) = u(1) = 0 of the differential equation . Omega_h& omega_h) { __initialization(df. omega_h).x the Jacobian ij ke = i j dφe dφ e ∫  -------. Omega_h::Omega_h() { for(int i = 0. C0 u = ((C0)(mr. Workbook of Applications in VectorSpace C++ Library 301 . N1=(1-ξ) (1+ξ). }.u_h() = gd. gd. cout << gd. i++) { ena[0] = i.add(node). static const int element_no = 2. ena[1] = ena[0]+1.-------- dx . 2). Global_Discretization& gd) : Element_Formulation(en. H0 Nx = d(N)(0)/d(X).One Dimensional Problems #include "include\fe. return 0. } static const double PI = 3. } class ODE_2nd_Order_Quadratic : public Element_Formulation { public: ODE_2nd_Order_Quadratic(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation *make(int. ODE_2nd_Order_Quadratic(int. &v). Element_Formulation* ODE_2nd_Order_Quadratic::make(int en. v = ((double)i)/((double)element_no). oh).u_h(). gd) { Quadrature qp(spatial_dim_no. spatial_dim_no. } int ena[3]. i++) { double v. Matrix_Representation mr(gd). } } gh_on_Gamma_h::gh_on_Gamma_h(int df. H1 X = N*xl. Omega_eh* elem = new Omega_eh(i. uh).u” = f (project: “quadratic_ode” in project workspace file “fe. ean[2] = ena[0] + 2. static Element_Type_Register element_type_register_instance. } Definte discretizaed global domain define nodes define elements define boundary conditions u(0) = u(1) = 0 instantiate fixed and free variables and Global_Discretization Define user element “ODE_2nd_Order” 1d Gauss Quadrature N0=-ξ (1-ξ) / 2. 0. gh_on_Gamma_h gh(ndf. N2 = ξ (1+ξ) / 2 coordinate transformation rule N. for(int i = 0. the_omega_eh_array.gd). int. N[1] = (1-Z)*(1+Z). oh). 1. } Element_Formulation* Element_Formulation::type_list = 0. Global_Discretization&). mr. ena). U_h uh(ndf. static const int spatial_dim_no = 1. Node* node = new Node(i.dsw” under directory “vs\ex\fe”). force &= ( ((H0)N)*cos(PI*((H0)X)) )| dv. Global_Discretization gd(oh. the_node_array. i < element_no. 0. H1 Z(qp). the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Dirichlet. gh.

02 0. Dirichlet 0. “Applied functional analysis and variational methods in engineering”.05 0. ena). ena[2] = ena[0]+2. Cylindrical Coordinates For Axisymmetrical Problem In cylindrical coordinates (r. Inc. 4•23 We consider an axisymmetrical heat conduction problem governing by the Laplace equation – ∇ 2 u = 0 shown in Figure 4•13. 4•22 in the constructor of the user defined element is 1 2 3 4 H1 Z(qp).6 0. Inc. i++) { ena[0] = i*2. see for example p. 2. 0. Eq (II.1 Neumann 0. int. 1969. 3/*nen*/.---.05 Mixed 0. ena[1] = ena[0]+1. McGraw-Hill.2 -0.4. 3.+ -------r ∂r  ∂r  r 2 ∂θ 2 ∂z 2 Eq. 302 Workbook of Applications in VectorSpace C++ Library . “Introduction to the mechanics of a continuous medium”. The inner and outer cylinder 1. 667.2 Figure 4•12 The results from two quadratic elements for (1) Dirichelt (2) Neumann and (3) Mixed boundary condtions for the second-order ordinary differentail equation. i < element_no. for(int i = 0.2 0.-------. Reddy. example in p.Chapter 9 10 11 12 13 14 15 16 } 4 Finite Element Method Primer } int ena[3]. Dashed curves with open squares are finite element solutions..6 0. // φ e = ----.4 0. Englewood Cliffs. z).02 -0. φe = ( 1 – ξ ) ( 1 + ξ ). Quadrature". N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int.05 0. the_omega_eh_array. qp). N. N[1]=(1-Z)*(1+Z).01 0.01 0. r ----. 1/*nsd*/.4 0. φ e = -.8 1 0. 364-367 in J.∇ 2 u = -. Malvern.8 1 -0. 0. the Laplace operator is written as1 1 ∂2u ∂2u 1 ∂ ∂u . and continuous curves are analytical solutions.N. } The interpolation functions for Eq.2 This is a cross-section of two coaxial hollow cylinders.E. –ξ ξ 0 1 2 N[0] = -Z*(1-Z)/2.add(elem).2 -0. + --. N[2]=Z*(1+Z)/2. Omega_eh* elem = new Omega_eh(i.C4) in L.8 1 -0.1 -0. Prentice-Hall.( 1 – ξ ). 1986.4 0.J.1 -0. θ.( 1 + ξ ) 2 2 The results of using only two quadratic elements are shown in Figure 4•12.6 0..15 -0.

4•9 and Eq. 4•10 is obtained by integration by parts of the weighted-residual statement with Eq. 100 80 T oC 60 40 20 25 30 35 40 45 50 r Figure 4•14The solution of heat conduction of an axisymmetrical problem with two hollow cylinders.⊗ -------- 2πrdr  dr dr  dφ e dφ e Eq. This is implemented in Program Listing 4•3. The Laplace equation becomes 1d du .6mm 20mm r 100oC κ=5 31.6mm κ=1 0oC 50mm 20mm κ=5 κ=1 Figure 4•13Cross-section of two hollow cylinder with diffusivity of k = 5.One Dimensional Problems 50mm 31. and k = 1 for the inner and outer cylinder. 4•24 ke = ∫ κ  -------. For this axisymmetrical problem u depends only on r. = 0 r dr  dr  Eq. 4•25 is “stiff &= (kapa[matrial_type_no] *(Nr%Nr)*2*PI((H0)r) ) | dr” where “Nr” is the derivative of shape functions “N” with respect to “r”.– -. The results are shown in Figure 4•14. have different thermal diffusivity “5” and “1”. Workbook of Applications in VectorSpace C++ Library 303 . 4•23 dropped out. κr ----. the second and the third terms in the left-hand-side of Eq.---. 4•25 The C++ code for Eq. the element stiffness matrix in Eq. respectively. respectively. 4•24 Replace dΩ = 2πr dr in the volume integral.

for(int i = 0. static const int element_no = 8. 39. static const int spatial_dim_no = 1.assembly().u” = 0 (project: “cylindrical_ode” in project workspace file “fe.C.add(node). gd) { Quadrature qp(spatial_dim_no. 28.0. }.Chapter 4 Finite Element Method Primer #include "include\fe. J dr(d(r)). Omega_h& omega_h) { __initialization(df. Global_Discretization& gd) : Element_Formulation(en. 2).0*PI*((H0)r) ) * (Nr%Nr) ) | dr.u_h() = u. qp). 35. 22. 1.x. the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet. stiff &= ( ( kapa[material_type_no]*2. 44.0. } int ena[2]. ena). U_h uh(ndf. } Definte discretizaed global domain define 9 nodes define 8 elements define boundary conditions u(20) = 100. r+i). H1 r = N*xl. gh_on_Gamma_h gh(ndf.⊗ -------- 2πrdr  dr dr  dφe dφ e register element Matrix Form assembly all elements solve linear algebraic equations update solution and B. 2. 1. static Element_Type_Register element_type_register_instance.0. int main() { const int ndf = 1. spatial_dim_no. static ODE_Cylindrical_Coordinates ode_cylindrical_instance(element_type_register_instance). N[0] = (1-Z)/2.lhs())). else material_type_no = 1. Global_Discretization gd(oh. } } gh_on_Gamma_h::gh_on_Gamma_h(int df.6. gd. cout << gd. Global_Discretization&). H0 Nr = d(N)(0)/d(r). u(50) = 0 instantiate fixed and free variables and Global_Discretization Define user element “ODE_2nd_Order” 1d Gauss Quadrature N0= (1-ξ) / 2. the_omega_eh_array. static const double kapa[2] = {5. 304 Workbook of Applications in VectorSpace C++ Library . N=INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int. } static const double PI = 3.rhs())) / ((C0)(mr. the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Dirichlet. and the Jacobian ke = ∫ κ  -------. i < node_no. for(int i = 0.0}.gd). uh). Global_Discretization& gd) { return new ODE_Cylindrical_Coordinates(en. output Listing 4•3 Axisymmetrical problem using cylindrical coordinates for the differential equation . if(i < element_no / 2) material_type_no = 0. Omega_h::Omega_h() { double r[9] = {20. N[1] = (1+Z)/2. material_type_no. H1 Z(qp).1. Element_Formulation* ODE_Cylindrical_Coordinates::make(int en. int. return 0. Global_Discretization&).9.gh_on_gamma_h(). ODE_Cylindrical_Coordinates::ODE_Cylindrical_Coordinates(int en. C0 u = ((C0)(mr. 25. }class ODE_Cylindrical_Coordinates : public Element_Formulation { public: ODE_Cylindrical_Coordinates(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation *make(int. i++) { ena[0] = i.8.u_h() = gd.7. Omega_eh* elem = new Omega_eh(i. omega_h).0.6.dsw” under directory “vs\ex\fe”). the_gh_array[node_order(0)][0] = 100. oh). Omega_h oh. 0.h" static const int node_no = 9.0}. gh. mr. ODE_Cylindrical_Coordinates(int.14159265359. 50. material_type_no. Quadrature".4. Matrix_Representation mr(gd). 2. i < element_no.add(elem).u_h(). i++) { Node* node = new Node(i. N1= (1+ξ) / 2 coordinate transformation rule N. the_gh_array[node_order(node_no-1)][0] = 0. the_node_array. } Element_Formulation* Element_Formulation::type_list = 0. gd. oh). ena[1] = ena[0]+1. 31.

1986.f and the shear force V is equal to the derivative of bending moment (M) as dM/dx =. 4•28 gives the fourth-order ordinary differential equation 2 d  d w  EI  = f.One Dimensional Problems 4. and (4) penalty function formulation. 4•26 Eq.2 A Fourth-Order ODE —the Beam Bending Problem We recall.2.V Therefore. and the curvature (d2w/dx2) of the beam is related to the bending moment “M” and the flexure rigidity “EI” as dw M = -------2EI dx2 2 Eq. that from balance of force. 4•30 is subject to the boundary conditions1 dw d  d w dw ( 0 ) = 0. 4•29 Substituting “M” in Eq. we solved this boundary value problem using Rayleigh-Ritz method with four weak formulations—(1) irreducible formulation. dM = f dx2 2 Eq. We use finite element method in this section to implement these four weak formulations. 4•30 We consider a boundary value problem that the Eq. d x2  d x2  2 0<x<L Eq. 1. McGraw-Hill. Workbook of Applications in VectorSpace C++ Library 305 . (3) Lagrange multiplier formulation. –  EI  (L) = V( L) = 0 dx d x2  dx dx 2 2 w( 0 ) = Eq. from the last chapter in the sub-section on fourth-order ODE (in page 205). Reddy. 4•29 into Eq. Inc.N. 4•27 Eq. “Applied functional analysis and variational methods in engineering”. the transverse loading (f) is equal to the derivative of shear force (V) as dV/dx =. J. (2) mixed formulation. EI 2 ( L ) = M. 4•28 The transverse deflection of the beam is denoted as w. 4•31 In the previous chapter.

 M Γ h  = 0  dx dx  dx  Ω    Dropping ε. 4•32 The last two terms are natural boundary conditions generated from integration by parts. 2. “Applied functional analysis and variational methods in engineering”. 3 are1. where ε is a small real number. 2 Eq. therefore. The node numbers are indicated by subscripts “0” and “1”. 1986. 383 in J. For example. -dw0/ dx. For this equation to be integrable through out Ω. i = 0. we consider a two nodes line element with two ˆ degrees of freedom associated with each notes. are defined as h i ˆi ue ≡ φe u e i where the piecewise cubic Hermit shape functions φ e .Chapter 4 Finite Element Method Primer Irreducible Formulation—Piecewise Cubic Hermite Shape Functions The Lagrangian functional is obtained from integrating by parts twice on the weighted residual statement from Eq. The variables.N. This is to satisfy the so-called continuity requirement. In other words. 4•34 1. Using δw = εv. 306 Workbook of Applications in VectorSpace C++ Library . Inc. McGraw-Hill. M Γ h = 0  dx dx  dx   2 2 Eq. 2  – δwf dx – δ wV Γh +  – ---------- M Γh  dx  dx 2  d x  2   2 2  d v   d w   dv = ε  ∫ EI  2   2  – vf dx – v V Γh +  – ----. 4•30 J( w ) = Ω ∫ EI  d w dw ----. If the first derivative of the variable is not continuous at any point on the integration domain and its boundaries.  – fw dx – w V Γh – M Γh 2  d x2  dx 2 2 Eq. defined in an element domain. Reddy. the second derivative of the variable on that point will be infinite. That is the nodal degrees of freedom are set to be u e = [w0. the first derivative of the variable at nodal points should be required to be continuous. 4•33 The integrand of Eq. 4•33 is not integrable. Eq. 4•33 contains derivative of variables up to second order. 1. Taking the variation of J and setting δJ(u) = 0 gives δJ ( w ) = Ω ∫ d 2 δw  d w dδw EI -----------. see derivation in p. since it is arbitrary. -dw1/dx] on the two nodes. we have to require that the first derivative of the variable be continuous through out the integration domain. we have Ω ∫  d v   d w dv EI  2   2  – vf dx – vV Γh +  – ----. w1.

Workbook of Applications in VectorSpace C++ Library 307 . or alternative form from p. Inc. Hughes. the error instead are shown in Figure 4•15.J. Note that the exact 2. f ) = dw dw . 4•43. 4•35 The element stiffness matrix is k e = a ( φ e. -ML}T is the natural boundary conditions on boundary shear forces and boundary bending moments. + 2  ---.M0.R. respectively. –  ---. φ e )u e Eq.  h e 2 ξ 2 ξ 3 2 φ e = 3  ---. 4•34. – 2  ---. The sign convention taken here for the bending moment is just the opposite. 4•38 where P = {V0. 4•37 where essential boundary conditions are u e = [w0. and dx 0 dx 1 Ωe ∫ φei i fdx. – i ( φe .One Dimensional Problems ξ 2 ξ 3 0 φ e = 1 – 3  ---. w1. h ) Γ = Γ ∫ φei Pdx Eq. and ( φe . f ) + ( φe . The solutions of the transverse deflection w and slope -dw/dx can be calculated from nodal values according to Eq.”The finite element method: Linear static and dynamic finite element analysis”.  h e  h e Eq. The natural boundary conditions are programmed to automatically taken care of in “Matrix_Representation::assembly()” where the lefthand-side is assumed to be a positive term instead of what happened in the left-hand-side of Eq. Notice that in the previous chapter we take counter clockwise direction as positive for bending moment. Therefore. VL.  h e  h e ξ 1 φ e = – ξ 1 –  ---. 49 in T. Prentice-Hall. They are almost identical to the exact solutions in Figure 3•16 and Figure 3•17 of the last chapter in page 208 and page 212.. – ]. h ) Γ – a ( φ e . This is the reason of take a minus sign in front of M for the definition of the vector P.  h e  h e ξ ξ 2 3 φe = – ξ  ---. The Program Listing 4•4 implemented the irreducible formulation for the beam bending problem. 4•36 The element force vector is j i i i i j f e = ( φ e . φ e ) = Ωe ∫  d φ e d φ e EI  2 ⊗ 2  dx dx  dx 2 2 Eq. 1987.

oh). } Beam_Irreducible_Formulation::Beam_Irreducible_Formulation(int en.  h e 2 ξ 2 ξ 3 2 φ e = 3  ---. static const int element_no = 4. Omega_h& omega_h) { __initialization(df. h_e. Omega_h::Omega_h() { for(int i = 0.u_h() = gd. N[1] = -Z*(1. class Beam_Irreducible_Formulation : public Element_Formulation { public: Beam_Irreducible_Formulation(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation *make(int. static const int spatial_dim_no = 1. Global_Discretization& gd) : Element_Formulation(en. qp).pow(2)-z). N[3] = -Z*(z. for(int i = 0. i < element_no. static Omega_h oh.pow(2)-2. Quadrature". 3). for(int i = 0. qp). static Matrix_Representation mr(gd). static Global_Discretization gd(oh. static gh_on_Gamma_h gh(ndf.add(node). Element_Formulation* Beam_Irreducible_Formulation::make(int en. h_e = fabs( ((double)(xl[0] . –  ---. the_node_array.gh_on_gamma_h(). H2 Z((double*)0.u_h() = u. the_gh_array[node_order(node_no-1)][1] = M_. omega_h). gd. N[0] = 1. static const double h_e = L_/((double)(element_no)).0).pow(2).pow(3).dsw” under directory “vs\ex\fe”). int. the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet. J d_l(h_e/2. cout << gd. &v). } Definte discretizaed global domain define nodes define elements define boundary conditions M(L) = -1 (positive clockwise) instantiate fixed and free variables and Global_Discretization “Beam_Irreducible_Formulation” Simpson’s rule Hermit cubics ξ 2 ξ 3 0 φ e = 1 – 3  ---. – 2  ---.0. gd.assembly(). uh).0*z. C0 u= ((C0)(mr.  h e  h e ξ 2 ξ 3 φ e = – ξ  ---. static const double E_ = 1. static const double I_ = 1. } Element_Formulation* Element_Formulation::type_list = 0. Beam_Irreducible_Formulation(int. the_omega_eh_array. H0 Nxx = INTEGRABLE_VECTOR("int.0.0*z. 4. 2. 0.add(elem).0.0*z. the_gh_array[node_order(0)](1) = gh_on_Gamma_h::Dirichlet. static const double M_ = -1. i < node_no.0*z.Global_Discretization& gd) { return new Beam_Irreducible_Formulation(en. force &= ( ((H0)N) * f_0) | d_l.  h e  h e ξ 1 φ e = – ξ 1 –  ---.0.Chapter 4 Finite Element Method Primer #include "include\fe.0/3. oh). Quadrature qp(weight. ena). N = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE( "int.0/3. ena[1] = ena[0]+1.h" static const int node_no = 5.u_h(). N[2] = 3. gd) { double weight[3] = {1.xl[1])) ). Node* node = new Node(i.0. Omega_eh* elem = new Omega_eh(i.0. 308 Workbook of Applications in VectorSpace C++ Library . Global_Discretization&).0. 4/*nen x ndf*/. Quadrature". z = Z/h_e. Global_Discretization&). 4. static const double L_ = 1.0. 0. spatial_dim_no. stiff &= ( (E_*I_)* (Nxx*(~Nxx)) ) | d_l. + 2  ---.rhs())) / ((C0)(mr.pow(2)+2. i++) Nxx[i] = dd(N)(i)[0][0]. static const double f_0 = 1.0-z). }. static U_h uh(ndf.pow(3). i < 4. } static const int ndf = 2. i++) { ena[0] = i.0-3.  h e  h e ke = Ωe i fe = ∫  d φ e d φ e EI  2 ⊗ 2  dx dx  dx fdx 2 2 Ωe ∫ φei Listing 4•4 Beam-bending problem irreducible formulation using Hermit cubics (project: “beam_irreducible_formulation” in project workspace file “fe.0/3. qp).gd). int main() { mr. 1/*nsd*/. } } gh_on_Gamma_h::gh_on_Gamma_h(int df. 0. 1. gh. static Beam_Irreducible_Formulation beam_irreducible_instance(element_type_register_instance). } int ena[2]. static Element_Type_Register element_type_register_instance.0}. i++) { double v = ((double)i)*h_e.lhs())). return 0.

6 0. 390 in J. The cubic approximation will not give solution identical to the exact solution. solution of the transverse deflection w is a polynomial of x up to fourth-order (see Eq. The flexure rigidity of the beam is 3. Node* node = new Node(0. Two cubic Hermict elements are used. “Applied functional analysis and variational methods in engineering”.0001 Error ∆w -6 8. 3•68 in page 207).00005 -0.finite element solution) of the irreducible formulation for beam bending problem. 6 the_node_array.0. 3 Omega_h::Omega_h() { // discritized global 4domain 5 double v = 0. Workbook of Applications in VectorSpace C++ Library 309 . 10 -6 4. Example problems from p. 2 1 0 1 Figure 4•16 Unit downward nodal loading on position x = 120.2 0. The definitions of the problem is now 1 static const int node_no = 3.0.0.add(node).0001 0.00005 -6 6. 10 -6 2. &v). static const int element_no = 2. P = -1.00001 0. 8 the_node_array. 10 Error ∆ dw dx 0.0 lb flexure rigidity (EI) = 3. node = new Node(1.8 1 x -0.6 0. static const int spatial_dim_no = 1. 1.add(node). 7 v = 120. node = new Node(2. spatial_dim_no. Reddy.8 1 x Figure 4•15 The error (= exact solution . 10 0. spatial_dim_no.2 0.0e6.N. 0 240 in. 9 v = 360. 10 the_node_array. spatial_dim_no. (Figure 4•16).4 0. static const double E_I_ = 144. &v).0*24.0. 2 static const double L_ = 360. 1986.2 The length of the beam is 360 in. &v). Inc.add(node).456x1010 lb in. The flexure rigidity of the beam is EI = 3.456x1010 lb in.2 120 in. McGraw-Hill.4 0. We divide the beam to two cubic Hermit elements. We consider two more examples for different types of boundary conditions and loads.1 The first example is to have unit downward nodal load on a simply supported beam at location of x = 120 in.456x1010.One Dimensional Problems 0.

The second example have distributed load x f ( x ) = f 0 -L Eq.00015 100 150 200 250 300 350 w dw dx -6 -1. 21 the_gh_array[node_order(0)][0] = 0. with macro definition. static const int element_no = 4. elements. 0. and set f0 = -1. 14 Omega_eh* elem = new Omega_eh(i. 24 the_gh_array[node_order(2)](0) = gh_on_Gamma_h::Dirichlet. to leave that line out. static const double L_ = 180. 20 the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet. and Young’s modulus is E = 29x106 psi. 10 Figure 4•17 Finite element solution for the nodal load problem for irreducible formulation of beam bending problem.0002 -6 -2. 12 for(int i = 0. static const double f_0 = -1. static const double I_ = 723. static const double E_ = 29. Omega_h& omega_h) { 19 __initialization(df. The results of this problem is shown in Figure 4•17. static const double element_size = L_/((double)(element_no)).0.Chapter 4 Finite Element Method Primer 11 int ena[2].4. 22 the_gh_array[node_order(1)](0) = gh_on_Gamma_h::Neumann.0. ena). 4•39 where L = 180 in. 15 the_omega_eh_array.0. 16 } 17 } 18 gh_on_Gamma_h::gh_on_Gamma_h(int df. 0.0. The problem definitions for nodes. 26 } // boundary conditions // w(0) = 0 // P(120) = -1. and boundary conditions are 1 2 3 4 static const int node_no = 5.00005 -0.add(elem). static const int spatial_dim_no = 1. Omega_h::Omega_h() { // discritized global domain x -6 1.0. with boundary conditions w(0) = w(L) = dw/dx (L) = 0. i++) { 13 ena[0] = i.0. ena[1] = ena[0]+1. 23 the_gh_array[node_order(1)][0] = -1. 310 Workbook of Applications in VectorSpace C++ Library . i < element_no.0. The moment of inertia is I = 723 in.0001 -0. We divide the beam into four equal size cubic Hermit elements. 2. 25 the_gh_array[node_order(2)][0] = 0.0 // w(360) = 0 Now in the computation for element force vector. 10 50 100 150 200 250 300 350 x -0.0e6.0. or use conditional compilation. you can either set f_0 = 0. omega_h). 10 50 -0. This distributed load is a linear downward loading increases from zero at the left to unity at the right.

0.One Dimensional Problems 5 for(int i = 0. 0. 10 25 50 75 100 125 150 175 x -0. // dw/dx(L) = 0 24 the_gh_array[node_order(node_no-1)][1] = 0. The distributed load is a linear downward loading increases from zero at the left to unit load at the right.00002 50 75 100 125 150 175 -6 1.0. 9 } 10 int ena[2].00004 -0. ena[1] = ena[0]+1. x 25 -0. 25 } In the constructor of the class Beam_Irreducible_Formulation the element force vector is computed as 1 2 3 H0 X = (1-((H0)z))*xl[0]+((H0)z)*xl[1]. 23 the_gh_array[node_order(node_no-1)](1) = gh_on_Gamma_h::Dirichlet. xl is the nodal coordinates // distributed load function The results of this distributed load problem using the irreducible formulation are shown in Figure 4•18. i++) { 6 double v = ((double)i)*element_size. omega_h). 0. 7 Node* node = new Node(i. 8 the_node_array.dsw” (in case of MSVC) under directory “vs\ex\fe”. 13 Omega_eh* elem = new Omega_eh(i. Omega_h& omega_h) { // boundary conditions 18 __initialization(df. ena). i < node_no. // global coordinates. &v).add(node). f = (f_0/L_)*X. They can be activated by setting corresponding macro definitions at compile time. 2.00012 -2. // element node number array 11 for(int i = 0. Workbook of Applications in VectorSpace C++ Library 311 .add(elem).0.00008 dw dx -6 -1. i++) { 12 ena[0] = i. // w(L) = 0 22 the_gh_array[node_order(node_no-1)][0] = 0. // w(0) = 0 20 the_gh_array[node_order(0)][0] = 0. 14 the_omega_eh_array. i < element_no. spatial_dim_no. 15 } 16 } 17 gh_on_Gamma_h::gh_on_Gamma_h(int df. These two extra problems are actually coded in the same project “beam_irreducible_formulation” in project workspace file “fe. 10 Figure 4•18 Finite element solution of the distributed load problem for the irreducible formulation of beam bending problem.0001 -6 -0. 10 w -0.00006 -0. 21 the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Dirichlet. 19 the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet. force &= ( ((H0)N) * f) | d_l.

4•28 and Eq. however.R. Hughes.Chapter 4 Finite Element Method Primer Mixed Formulation In the irreducible formulation the second derivative appears in the weak formulation. in order to have entire domain to be integrable. we should have Cn-1-continuity at the nodes. 4•40 Integration by parts on both equations. 4•42 w For the Bubnov-Galerkin method we use interpolation functions φ e for both w and vw. In the mixed formulation.x is included in the nodal variables in the irreducible formulation in the last section.J. 310 in T. In the cases of higher dimensions. which has second derivative in the weak formulation. e. plate and shell.1 Recall Eq. 4•41 where the boundary conditions on the shear force and slope are V = – The Euler-Lagrange equations are obtained by setting δJ(w. 4•29 M dM dw = -------. the first derivative w. We use the cubic Hermite functions.+ fw dx – M   Γ – w   Γ  dx  h dx  h 2EI dM dw and ψ = – dx dx Eq. we have the Lagrangian functional L J M ( w. M)= 0 (where δw = εw vw and δM = εM vM) L δ w J M = εw ∫ dx  0 L dv w dM dM + v w f dx – v w   Γ   d x  h  dx = 0 δ M J M = εM ∫ dx  0 dv M dw dw M + v M ----- dx – v M   Γ = 0 dx  h dx EI Eq. That is if “n” order derivative appears in the weak formulation. M ) = ∫ dx dx  0 dw dM M2 dM dw + -------. New Jersey. and interpolation funcM tions φ e for both M and vM.. PrenticeHall. 312 Workbook of Applications in VectorSpace C++ Library . For example. the irreducible formulations always lead to extremely complicated schemes. In matrix form finite element formulation from Eq.g. The current trend for these problems is to develop formulations that requires only C0-continuity.. p. these interpolation functions are quite formidable. “The finite element method: Linear static and dynamic finite element analysis”.. inc. 1987. Englewood cliffs. we trade somewhat more complicated variational formulations for reducing the order of derivative to satisfy the continuity requirement (stated earlier in page 268). and = f 2EI dx2 dx2 2 2 Eq. 4•42 is (dropping εw and εM) 1.

4•45 equally convenient. That may often cause serious confusion. The disadvantage of doing that is that we have put the burden on user to specify the program correctly.---------. Workbook of Applications in VectorSpace C++ Library 313 . We can re-arrange the degree of freedom.lib” to be automatically taken care of in “Matrix_Representation::assembly()” where the left-hand-side is assumed to be a positive term instead of what happened in the left-hand-side of Eq. b ij = – ∫ -------------. 4•43 to be consistent with what is done in the “assembly()” by changing sign as w M dφ e dφ e – ∫  --------.dx. 4•44 becomes ˆ 0 a 00 0 a 01 w 0 T a 00 T b 00 a 01 b 01 0 a 10 0 a 11 f0 ˆ r M0 = 0 ˆ1 f1 w r1 Eq. h and r i = φ iM ψ Γh Eq. 4•44 or Eq. 4•44 The Program Listing 4•5 implement the beam bending problem subject to boundary conditions in Eq.⊗ ---------- dx  dx dx  φM  e Ωe w dφ e M dφ e ˆ we ˆ Me Ωe ∫  ---------. int)” or regular increment selector “operator[](int)” in VectorSpace C++ library makes the coding in the formula of either Eq. M}T. ψ}T The Eq. 4•31. In finite element convention. and natural boundary conditions are {V.⊗ --------.dx. 4•43.One Dimensional Problems 0 dφ M  e w dφ e  Ωe ∫  ---------. for every node. 4•45 T T ˆ a 10 b 10 a 11 b 11 M 1 where subscripts indicate the element nodal number and each component in the matrix or vectors is defined as φ iM φ jM dφ iw dφ jM a ij = – ∫ --------. Therefore. the degree of freedoms for a node are packed together.dx – ∫  dx dx  Ωe φM  e Ωe ⊗ --------------------.dx  EI  M φe  = Ωe ∫ φew fdx + φew VΓ M φe ψΓh h Eq.⊗ ---------- dx  dx dx  Ωe w dφ e  0 dφ M  e ˆ we ˆ Me – ∫ ---------.dx  EI  M φe  = Ωe ∫ –φew fdx – φew VΓ M –φ e ψΓh h Eq.⊗ ---------- dx ∫ dx dx ⊗ --------------------. f i = dx dx EI Ωe Ωe Ωe ∫ φiw fdx + φiw VΓ . we prefer to make the sign of Eq. We can choose to take an opposite sign convention on the boundary condition as what we have done for the bending moment boundary condition in the irreducible formulation. 4•43 The natural boundary conditions specified through V is hard-wired in “fe. 4•44. corresponding to the essential boundary conditions as {w. 4•46 The submatrix/subvector component access through either continuous block selector “operator ()(int. using Eq.

2/*nen*/. static const double h_e = L_/((double)(element_no)).gd). gh.0. Definte discretizaed global domain define nodes define elements define boundary conditions M(L) = 1 instantiate fixed and free variables and Global_Discretization “Beam_Mixed_Formulation” w M φ e = φ e = {(1-ξ)/2. C0 u = ((C0)(mr. C0 force_sub = SUBVECTOR("int. 0. 2.assembly(). &v). the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet. gd. (1+ξ)/2}T w M dφe dφ e 10 01 k e = k e = – ∫  --------. static const double M_ = 1. Omega_eh* elem = new Omega_eh(i.0. Global_Discretization& gd) { return new Beam_Mixed_Formulation(en. } class Beam_Mixed_Formulation : public Element_Formulation { public: Beam_Mixed_Formulation(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation *make(int. Element_Formulation* Beam_Mixed_Formulation::make( int en. int main() { const int ndf = 2. static Element_Type_Register element_type_register_instance. Omega_h oh. i < node_no.0. Omega_h::Omega_h() { for(int i = 0. Global_Discretization&). N[0] = (1-Z)/2.add(node). Matrix_Representation mr(gd). J d_l(d(X)). stiff_sub[1][0] = stiff_sub[0][1]. oh). gh_on_Gamma_h gh(ndf. int.0. ena[1] = ena[0]+1. U_h uh(ndf.⊗ ---------- dx  dx dx  Ωe M M φe ⊗ φe 11 k e = – ∫  --------------------. i++) { double v = ((double)i)*h_e. static const double E_ = 1. force_sub[0] = ( (((H0)N)*f_0) | d_l ).u_h() = gd. N[1] = (1+Z)/2. H0 Nx = d(N)(0)/d(X). return 0. 314 Workbook of Applications in VectorSpace C++ Library .dsw” under directory “vs\ex\fe”). // M(L) = M_ the_gh_array[node_order(node_no-1)][1] = M_. (double*)0).u_h() = u. Omega_h& omega_h) { __initialization(df.rhs()))/((C0)(mr. stiff_sub[1][1] = -(1. static const double L_ = 1. spatial_dim_no. gd.Chapter 4 Finite Element Method Primer #include "include\fe. i++) { ena[0] = i. 2. static Beam_Mixed_Formulation beam_mixed_instance(element_type_register_instance). 1/*nsd*/. ena).add(elem). C0 stiff_sub = SUBMATRIX("int. } int ena[2]. Beam_Mixed_Formulation(int. 2. dx  EI  Ωe 0 fe = Ωe ∫ φew fdx Listing 4•5 Beam-bending problem mixed formulation using linear line element (project: “beam_mixed_formulation” in project workspace file “fe. int. stiff). static const double f_0 = 1.0. 2). H1 X = N*xl. Global_Discretization& gd) : Element_Formulation(en. qp).gh_on_gamma_h().u_h(). } Beam_Mixed_Formulation::Beam_Mixed_Formulation(int en. (double*)0). static const int element_no = node_no-1. 0. gd) { Quadrature qp(spatial_dim_no. Quadrature". the_omega_eh_array. Global_Discretization gd(oh. C0&". 2. 4. stiff &= C0(4. N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int. omega_h). Node* node = new Node(i. }. force). static const int spatial_dim_no = 1. oh).lhs())). C0&". H1 Z(qp). static const double I_ = 1. cout << gd. Global_Discretization&). force &= C0(4.0/E_/I_)* ( (((H0)N)*(~(H0)N)) | d_l ). stiff_sub[0][1] = -(Nx * (~Nx)) | d_l. i < element_no. for(int i = 0.h" static const int node_no = 5. } } gh_on_Gamma_h::gh_on_Gamma_h(int df. the_node_array. mr. uh). // w(0) = 0 the_gh_array[node_order(node_no-1)](1) = gh_on_Gamma_h::Dirichlet. } Element_Formulation* Element_Formulation::type_list = 0.

omega_h). } } gh_on_Gamma_h::gh_on_Gamma_h(int df.0e6. 2. wexact(x) = ((2M+fL2)/4EI) x2 . i < element_no. &v).0.5 0. and Mexact(x) = f/2 (x-L)2 + M Eq. for(int i = 0.8 1 x Figure 4•19 Transverse deflection “w” and bending moment “M” from mixed formulation.4 1. For the nodal loading case. i++) { ena[0] = i. the_node_array.2 0. 0.One Dimensional Problems 0.6 0.6 0. the_node_array. the code for the definition of the problem gives 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 static const int node_no = 3. The dashed line segments with open squares are finite element solutions.2 1.3 w 0. // w(0) = 0 the_gh_array[node_order(0)](1) = gh_on_Gamma_h::Dirichlet. the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet. ena). &v).0. Omega_eh* elem = new Omega_eh(i. spatial_dim_no. Omega_h& omega_h) { __initialization(df. static const double E_ = 24.4 0. static const int element_no = 2.2 0.add(node). shear force the_gh_array[node_order(1)][0] = -1.1 0.4 1.add(elem). static const double I_ = 144. That is. 0. // V(120) = -1.0. the_omega_eh_array. spatial_dim_no. static const double L_ = 360. static const int spatial_dim_no = 1. Node* node = new Node(0. The results are shown in Figure 4•19. The solutions at the nodal points match the exact solutions of the transverse deflection and the bending moment.fL/(6EI) x3 + f/(24EI) x4.0. and the solid curves are the exact solutions.add(node).4 0. Omega_h::Omega_h() { double v = 0. // M(360) = 0 } Workbook of Applications in VectorSpace C++ Library 315 .0.0.1 x 0.0. node = new Node(2. node = new Node(1. the_node_array. // w(360) = 0 the_gh_array[node_order(2)](1) = gh_on_Gamma_h::Dirichlet. spatial_dim_no. ena[1] = ena[0]+1. int ena[2].8 1 M 1.3 0.add(node).5 1.6 0. v = 120. v = 360. &v). 4•47 Now we proceed to the same (1) nodal loading and (2) distributed loading cases solved in the irreducible formulation. // M(0) = 0 the_gh_array[node_order(1)](0) = gh_on_Gamma_h::Neumann.2 0. the_gh_array[node_order(2)](0) = gh_on_Gamma_h::Dirichlet.

0. // dw/dx(L) = 0 } 316 Workbook of Applications in VectorSpace C++ Library . ena[1] = ena[0]+1. i++) { double v = ((double)i)*element_size. static const int spatial_dim_no = 1. static const double E_ = 29. the_omega_eh_array. ena). spatial_dim_no. } } gh_on_Gamma_h::gh_on_Gamma_h(int df.00015 M 40 20 -0. // w(L) = 0 the_gh_array[node_order(node_no-1)](1) = gh_on_Gamma_h::Neumann.0.0e6. The bending moment solution is exact for this case. Omega_h::Omega_h() { for(int i = 0. &v).0002 50 100 150 200 250 300 350 x Figure 4•20Transverse deflection w and bending moment M for the nodal loading problem using linear interpolation functions for both w and M. static const int element_no = 4.add(node).0001 -0. i++) { ena[0] = i. For the element force vector we can either set f_0 = 0 or just comment out the corresponding statement for efficiency. Omega_eh* elem = new Omega_eh(i.00005 100 150 200 250 300 350 60 w -0. 2. The result of the nodal loading case is shown in Figure 4•20. i < element_no.0. // M(0) = 0 the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Dirichlet. omega_h). Node* node = new Node(i. i < node_no. // w(0) = 0 the_gh_array[node_order(0)](1) = gh_on_Gamma_h::Dirichlet. } int ena[2]. the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet.0. Omega_h& omega_h) { __initialization(df. static const double element_size = L_/((double)(element_no)). static const double L_ = 180. static const double I_ = 723. the_node_array. 0.add(elem). static const double f_0 = -1. The problem definition in C++ code for the distributed loading case is 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 static const int node_no = 5. for(int i = 0.Chapter 4 Finite Element Method Primer x 80 50 -0.

Workbook of Applications in VectorSpace C++ Library 317 .00002 -0. In the irreducible formulation.One Dimensional Problems The element force vector in the constructor of class “Beam_Mixed_Formulation” is to define the loading function 1 2 3 4 H0 f = (f_0/L_)*((H0)X). force). C0 force_sub = SUBVECTOR("int. force_sub[0] = ( (((H0)N)*f) | d_l ). 2.00004 50 75 100 125 150 175 x 1000 500 M -500 -1000 -1500 25 50 75 100 125 150 175 x w -0. (double*)0). C0&". The results of this problem are shown in Figure 4•21. respectively) to be solved together with w. both the irreducible and the mixed formulation require one more variable (-dw/dx. This increases the number of degrees of freedom in the matrix solution process. This can be disadvantageous for a large-size problem.0001 -0. 25 -0. and M.00006 -0. However.00012 -2000 Figure 4•21Transverse deflection w and bending moment M for the distributed loading problem using linear interpolation functions for both w and M. In the mixed formulation this requirement is relaxed. we are required to include the higher-order derivatives be interpolated using the abstruse cubic Hermite functions.00008 -0. force &= C0(4.

2•11 of Chapter 2 in page 118 as ( ψ. the finite element formulation as 318 Workbook of Applications in VectorSpace C++ Library . w.into Eq. εw. dx – w VΓ h + ψM Γ h  2 dx  dx  Ω Eq. dx = 0  dx  Ω Eq. and ελ and use interpolation functions for each of the variables {ψ. w ) = Ω ∫ EI  dψ 2 dw ----– fw dx + ∫ λ  ψ + -----. 4•50 The minimization of Eq. we have J ( ψ.λ dx – v w V Γh = 0 dx Ω Ω dw = ε λ ∫ v λ  ψ + -----. w ) = dw dx Eq. w. λ}T we have.Chapter 4 Finite Element Method Primer Lagrange Multiplier Formulation Recall Eq. in matrix form. λ ) ≡ J ( ψ. 4•32 that the Lagrangian functional for the irreducible formulation is J( w ) = Ω ∫ EI  d w dw ----. 4•50 subject to constraint of Eq. 4•52 Dropping the arbitrary constants of εψ. 4•48. w ) ≡ ψ + -----. 4•48 Now. w ) + λ C ( ψ. 4•49 Ω ∫ EI  dψ 2 ----– fw dx – w V Γh + ψM Γh 2 dx Eq.  – fw dx – w V Γh – M Γh 2  d x2  dx 2 2 Eq. 4•49 using Lagrange multiplier method (with the Lagrange multiplier λ) leads to the Lagrangian functional in the form of Eq.= 0 dx Substituting ψ = – -----. in the context of constrained optimization discussed in Chapter 2. 4•51 The Euler-Lagrange equations are obtained from δL = 0 as (where δψ = εψ vψ. and δλ = ελ vλ) δψ δw δλ = εψ Ω ∫ dv ψ dψ EI --------dx + ∫ v ψ λ dx + v ψ M Γh = 0 dx d x Ω dv w = ε w – ∫ v w f dx + ∫ --------. δw = εw vw. we define constraint equation for negative slope ψ that dw C ( ψ.

x 2 – -------. the Eq. 4•55 ψ and λ is obtained by differentiating the exact solution of w(x) in the first line from the corresponding definitions. 4•45. the bending moment boundary conditions appears on the right-hand-side of the first equation is negative. The Program Listing 4•6 implemented the Eq.. 2M + fL 2 fL f w ( x ) =  --------------------.⊗ φeλ dx dx 0 w dφ e ˆ ψe ˆ we = ˆ λe Ωe ψ φ e MΓh ∫ φew f dx + φew VΓ 0 h Eq. Workbook of Applications in VectorSpace C++ Library 319 . the lagrange multiplier λ per se. λ}T. 4•53 Ωe ∫ φeλ ⊗ φeψ dx Ωe ∫ w dφ e λ φ e ⊗ --------. coincides with the exact solution. 4•53 as ψ ψ dφ e dφ e – E I ∫ --------. w. With the aid of the regular increment selector “operator[](int). x 2 – -------.dx dx dx Ωe 0 Ωe ∫ φeψ ⊗ φeλ dx w dφ e 0 0 Ωe ∫ ---------.dx dx dx Ωe 0 ψ λ – ∫ φ e ⊗ φ e dx Ωe 0 0 Ωe ∫ ---------.x 3 6EI 2EI 2EI λ( x) = f ( L – x ) Eq. 4•54 Ωe ∫ φeλ ⊗ φeψ dx Ωe ∫ w dφe λ φ e ⊗ --------. φ e .dx dx Again. the degree of freedoms for each node can be packed together just as in Eq. 0}T The results are shown in Figure 4•22 which are compared to the exaction solutions. 4•54 is sufficient clear without really needing to rewrite to the form of Eq.⊗ --------. This is in conflict with the nodal loading input is positive on the right-hand-side assumed in the implementation of the “Matrix_Rxpresentation::assembly()”. V. In order to keep the convention of counter clock-wise rotation as positive. and the natural boundary conditions are {M.x 4  4EI  6EI 24EI f – ( 2M + fL 2 ) fL ψ ( x ) = ----------------------------.⊗ φeλ dx dx 0 ˆ ψe ˆ we = ˆ λe Ωe ψ – φe M Γh ∫ φew f dx + φew VΓ 0 h Eq.x 3 + ----------.One Dimensional Problems ψ ψ dφ e dφ e EI ∫ --------. φ e }T for all three variables. The shear force solution. The essential boundary conditions are {ψ.⊗ --------.x + -------. we can change sign on the first row of Eq. 4•54 with linear interpolation functions ψ w λ { φ e .dx dx Again. 4•45.

N[0] = (1-Z)/2. stiff). force &= C0(6.⊗ --------- dx  dx dx  Ωe ψ λ dφ e dφe 02 20 ke = – ( k e ) T = – ∫  --------. static Element_Type_Register element_type_register_instance. Global_Discretization gd(oh. Omega_h& omega_h) { __initialization(df.0. uh).cout << gd.0. C0 u = ((C0)(mr. stiff &= C0(6. 2/*nen*/. U_h uh(ndf.assembly(). static const double h_e = L_/((double)(element_no)). omega_h). ena[0] = i. static Beam_Lagrange_Multiplier_Formulation lagrange(element_type_register_instance). i < element_no. dx  dx dx  Ωe w dφe 12 21 ke = ( ke )T = Ωe 1 fe = ∫ ---------.0. gd) { Quadrature qp(spatial_dim_no. qp). C0&". Quadrature". } Beam_Lagrange_Multiplier_Formulation::Beam_Lagrange_Multiplier_Formulation(int en. gh. Node* node = new Node(i. i++) { double v = ((double)i)*h_e. // psi(0) = -dw/dx(0) = 0 the_gh_array[node_order(0)](1) = gh_on_Gamma_h::Dirichlet.0. force). // lambda(L) = 0. gd.Chapter 4 Finite Element Method Primer #include "include\fe. Beam_Lagrange_Multiplier_Formulation(int. } Element_Formulation* Element_Formulation::type_list = 0. C0 force_sub = SUBVECTOR("int. &v). // M(L) = M_ the_gh_array[node_order(node_no-1)][0] = M_. H1 Z(qp). } for( int i = 0. C0 stiff_sub = SUBMATRIX("int. the_omega_eh_array.gd). }. static const double E_ = 1.u_h() = gd. int.⊗ φeλ dx dx Ωe ∫ φew fdx Listing 4•6 Beam-bending problem Lagrange multipler formulation using linear line element (project: “beam_lagrange_multiplier” in project workspace file “fe. static const int element_no = 4. H0 Nx = d(N)(0)/d(X). force_sub[1] = (((H0)N)*f_0) | d_l. Omega_h oh. stiff_sub[1][2] = (Nx % ((H0)N)) | d_l. Omega_eh* elem = new Omega_eh(i. gh_on_Gamma_h gh(ndf. 2). 0. C0&". static const double L_ = 1.u_h() = u. return 0. Element_Formulation* Beam_Lagrange_Multiplier_Formulation::make(int en.h" static const int node_no = 5. (double*)0). 0. static const double I_ = 1. (1+ξ)/2}T ψ ψ dφ e dφe 00 k e = – ∫ EI  --------.gh_on_gamma_h(). 6. 3. oh). stiff_sub[0][2] = -(((H0)N) % ((H0)N)) | d_l. (double*)0). 2.int main() { const int ndf = 3. Omega_h::Omega_h() { for( int i = 0. spatial_dim_no. Global_Discretization&). // end bending moment } class Beam_Lagrange_Multiplier_Formulation : public Element_Formulation { public: Beam_Lagrange_Multiplier_Formulation(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation *make(int. J d_l(d(X)). the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Neumann.⊗ -------. 3. } } gh_on_Gamma_h::gh_on_Gamma_h(int df. H1 X = N*xl. Matrix_Representation mr(gd).rhs()))/((C0)(mr. the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet. ena[1] = ena[0]+1. static const int spatial_dim_no = 1. 1/*nsd*/. the_node_array. 320 Workbook of Applications in VectorSpace C++ Library . oh). N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int. 3.add(node). } Definte discretizaed global domain define nodes define elements define boundary conditions M(L) = 1 instantiate fixed and free variables and Global_Discretization “Beam_Lagrange_Multiplier_Formulati on” w ψ λ φ e = φe = φ e = {(1-ξ)/2. stiff_sub[0][0] = -((E_*I_) * Nx * (~Nx)) | d_l. Global_Discretization&). N[1] = (1+Z)/2.dsw” under directory “vs\ex\fe”). stiff_sub[2][0] = -( ~stiff_sub[0][2] ). i++) { int ena[2]. mr.add(elem). stiff_sub[2][1] = ~stiff_sub[1][2]. i < node_no. Global_Discretization& gd) : Element_Formulation(en.u_h(). // w(0) = 0 the_gh_array[node_order(node_no-1)](2) = gh_on_Gamma_h::Dirichlet. gd.lhs())). ena). static const double M_ = 1.0. static const double f_0 = 1. int. Global_Discretization& gd) { return new Beam_Lagrange_Multiplier_Formulation(en.

5 ψ -0. &v).2 0. On the other hand. static const double P_ = 1.add(node).4 0.0. 0. // w(360) = 0 } Again.0. node = new Node(3.8 1 x Figure 4•22 Lagrange multiplier formulation for beam bending problem using linear interpolation function for all three variables. The exact solution shear force is constant within each element. static const double I_ = 144. spatial_dim_no. Node* node = new Node(0. spatial_dim_no.4 0.2 0.8 -1 w 0. spatial_dim_no.6 0. The solution for this boundary condition case is not acceptable. spatial_dim_no.4 -0.add(node).One Dimensional Problems x 1 0. 0.0. &v).2 0. &v). &v). static const int spatial_dim_no = 1. } } gh_on_Gamma_h::gh_on_Gamma_h(int df. node = new Node(1.4 0. Omega_eh* elem = new Omega_eh(i. static const int element_no = node_no-1. static const double E_ = 24.P.8 1 0.0. The choice of different order of interpolation functions and the number of nodes per variable/per element to obtain a meaning- Workbook of Applications in VectorSpace C++ Library 321 .0. we can just comment out the element force vector computation in the constructor of class Beam_Lagrange_Multiplier for efficiency. the_gh_array[node_order(0)](1) = gh_on_Gamma_h::Dirichlet. v = 120.4 0. v = 360. the_node_array.0e6. The problem is overly constrained.add(node). the_node_array. the slope and transverse deflection require higher order of interpolation functions than the linear functions.add(node). static const double L_ = 360.1 0. Omega_h& omega_h) { __initialization(df. ena[0] = i.3 λ0.0.8 1 x 0.6 0. node = new Node(2. ena[1] = ena[0]+1. shear force the_gh_array[node_order(1)][1] = -P_. i < element_no. the_omega_eh_array. for(int i = 0.6 0.4 0.2 0. The results are shown in Figure 4•23. i++) { int ena[2].8 0. ena). 2. v = 240. // w(0) = 0 the_gh_array[node_order(1)](1) = gh_on_Gamma_h::Neumann. while we use linear interpolation functions for the shear force. // f(120) = .add(elem).6 0. Omega_h::Omega_h() { double v = 0. The problem definitions for the nodal load case can be coded as the followings 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 static const int node_no = 4.6 -0. omega_h).6 0. the_gh_array[node_order(node_no-1)](1) = gh_on_Gamma_h::Dirichlet.2 0. the_node_array.2 -0. the_node_array.0.

ena[0] = i. i++) { double v = ((double)i)*element_size. Omega_h& omega_h) { __initialization(df.00005 50 100 150 200 250 300 350 -0. &v). i < element_no.00015 -0.add(elem).0. i++) { int ena[2]. static const int spatial_dim_no = 1. ena). C0 force_sub = SUBVECTOR("int.5 -2 Figure 4•23 The Lagrange multiplier method with all three variables interpolated using linear element for the nodal load problem does not produce satisfactory result. spatial_dim_no. Node* node = new Node(i. static const double element_size = L_/((double)(element_no)).5 10 -6 2. Omega_eh* elem = new Omega_eh(i.0002 -1.4 The distributed load case is defined as 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 static const int node_no = 5. ful result depends on the so-called LBB-condition in finite element method that we will discussed in details in Section 4. 10 50 -7 -5. force &= C0(6.5 x -0. static const double I_ = 723. 0. omega_h).0. (double*)0).5 10 -6 1.5 -1 λ 100 150 200 250 300 350 exact soln. force_sub[1] = (((H0)N)*f) | d_l. } for(int i = 0. ena[1] = ena[0]+1. the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Neumann.0e6. // w(L) = 0 } The element force vector is implemented as 1 2 3 322 H0 f = (f_0/L_)*((H0)X). } } gh_on_Gamma_h::gh_on_Gamma_h(int df. the_omega_eh_array. static const double L_ = 180. // psi(L) = -dw/dx(L) = 0 the_gh_array[node_order(node_no-1)](1) = gh_on_Gamma_h::Dirichlet.0. 10 4 Finite Element Method Primer w 50 -0. 10 -6 1. // w(0) = 0 the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Dirichlet. x 0. 0.Chapter ψ -6 2.0001 100 150 200 250 300 350 x -0. the_node_array. Omega_h::Omega_h() { for(int i = 0. 10 -6 -1. 3.add(node). // M(0) = 0 the_gh_array[node_order(0)](1) = gh_on_Gamma_h::Dirichlet. 10 -7 5. static const int element_no = 4. static const double f_0 = -1. 2. Workbook of Applications in VectorSpace C++ Library . force). i < node_no. static const double E_ = 29. C0&".

5 10 0. ψ -6 1. while transverse deflection w is partially “locking” which systematically underestimates the magnitude of the exact solution (see Figure 4•24). 10 -6 -1.2 -25 -50 0.00002 -0.0001 x Figure 4•24 The results of the distrubted loading case using Lagrange multiplier formulation for the beam bending problem. 10 0.4 0.00006 -0. Workbook of Applications in VectorSpace C++ Library 323 .00004 0.2 -0. 10 -6 -1. 10 -7 5.6 0.8 1 x -0.One Dimensional Problems The results of the distributed load case are un-acceptable that the solution of λ and ψ show oscillation.6 0.6 0.2 -7 -5.4 0.8 1 w 0.8 1 λ x 100 75 50 25 0.00008 -0.4 0.5 10 -6 1.

dx dx dx Ω ψ φ e MΓh = Ωe ∫ φew f dx + φew VΓ Eq.⊗ --------. w ) + -.Chapter 4 Finite Element Method Primer Penalty Function Formulation From Eq. and {vψ.dx + ρ ∫ φ e ⊗ φ e dx ρ ∫ φe ⊗ --------. we have w ψ ψ dφ e dφ e dφ e   ψ ψ ψ –  EI ∫ --------.⊗ --------. 4•56 where the popular quadratic form of the penalty function is taken. 4•59 h As discussed in a sub-section “Penalty Methods” on page 153 in Chapter 2. the penalty parameter ρ should be initially set to a small number.⊗ --------. vw}.⊗ --------. 4•49 and Eq. Starting out with a small ρ means we are to weight more on the minimization of the objective functional (for this problem the minimum energy principle in mechanics). the Euler-Lagrange equations.dx + ρ ∫ φ e ⊗ φe dx – ρ ∫ φ e ⊗ --------.dx dx dx  Ω dx  Ω Ω e ˆ ψe ψ –φ e MΓh w dφ e ψ ρ ∫ --------. Eq.dx dx dx ˆ  Ω dx  ψe Ω Ω e w dφ e ψ ρ ∫ --------. w ) = 2 Ω ∫ EI  dψ 2 ρ dw 2 ----– fw dx + -. then gradually increase its values in subsequent iterations. Subsequently increasing the penalty parameter enforces the constraint 324 Workbook of Applications in VectorSpace C++ Library .⊗ φ e dx dx Ω w w ˆ dφ e we dφ e ρ ∫ --------. dx + v ψ M Γh = 0  dx d x dx  Ω δw p dw dv w = ε w – ∫ v w f dx + ρ ∫ --------. the Lagrangian functional for the penalty function formulation can be written in the form of Eq. 2•61 of Chapter 2 in page 153 p ( ψ. 4•57 Dropping the arbitrary constants εψ and εw and substituting interpolation functions for {ψ.ρ ) ≡ J ( ψ. dx – v w V Γh = 0 dx  dx  Ω Ω Eq.dx dx dx Ω = Ωe ∫ φew f dx + φew VΓ Eq. ψ + -----.⊗ φ e dx dx Ω w w ˆ dφe we dφ e ρ ∫ --------. are re-written for the element formulation in matrix form as w ψ ψ dφe dφ e dφ e   ψ ψ ψ  EI ∫ --------. The Euler-Lagrange equations obtained from setting δ p = 0 are (where δψ = εψ vψ.C 2 ( ψ. dx – w VΓ h + ψM Γh dx   2 2 dx  Ω Eq. δw = εw vw ) = εψ δψ p Ω ∫ dv ψ dψ dw EI --------dx + ρ ∫ v ψ  ψ + -----. 4•58 h Changing the sign of the first equation to keep the right-hand-side positive. ρ w . w}. 4•57. 4•50.∫  ψ + -----.

The solution will be corrupted.4 0. Workbook of Applications in VectorSpace C++ Library 325 . The Program Listing 4•7 implements Eq.1 to this issue with some canonical formulations in two-dimension are discussed in details. x 0.6 0.4 0.One Dimensional Problems gradually.5 0. 4•59 with an ad hoc penalty iterative procedure which find a local minimum solution with respect to “w” by monitoring the convergence of “∆w”.6 -0.8 1 x Figure 4•25 The solutions of end-bending moment case with penalty formulation.1 0.3 0. In general. When the divergence of ∆w first occurs we terminate the penalty loop The choice of this termination criterion is that we do not have the value of the original objective functional available for determining the convergence of this problem.2 0.6 0. the two constrained cases using lagrange multiplier formulation and penalty function formulation do not work well.2 -0. Sometimes.2 0.2 -0. the results are even disastrous. The conditions to obtain an accurate formulation in constrained formulations were area of intensive interest in the development of the finite element method. However.6 0. We devote entire Section 5. exact solution is obtained at ρ → ∞ . when ρ is too large the left-hand-side matrix in Eq. 4•59 becomes ill-conditioned.8 -1 w 0. The penalty method is also not very efficient.8 1 ψ -0. The solutions are shown in Figure 4•25. In principle the.4 0.4 0.

(double*)0). Omega_eh* elem = new Omega_eh(i. force &= C0(4. return 0.add(elem). k_optimal. Global_Discretization& gd) : Element_Formulation(en. C0 stiff_sub = SUBMATRIX("int. stiff_sub[0][1] = -k_* ( (((H0)N) * (~Nx)) | d_l ).0. Quadrature". k_optimal = k_. oh). 0. 0. 326 Workbook of Applications in VectorSpace C++ Library . static Beam_Penalty_Function_Formulation beam_penalty_function_formulation_instance( element_type_register_instance). N[1] = (1+Z)/2.u_h() = gd.0. } gd. C0&". C0 force_sub = SUBVECTOR("int.0. gd) { Quadrature qp(spatial_dim_no. (double*)0). H0 Nx = d(N)(0)/d(X).u_h()[j][1].lhs())).e20. u_optimal.h" static const int node_no = 5. Matrix_Representation mr(gd). 2.add(node). C0 u = ((C0)(mr. C0 w(node_no. i++) { double v = ((double)i)*h_e. } Element_Formulation* Element_Formulation::type_list = 0. i < element_no. N[0] = (1-Z)/2.u_h() << endl. qp). for( int i = 0. the_node_array. } Beam_Penalty_Function_Formulation::Beam_Penalty_Function_Formulation( int en.dsw” under directory “vs\ex\fe”). static const double h_e = L_/((double)(element_no)). static double k_ = 1.0. Omega_h::Omega_h() { for(int i = 0.assembly(). int.u_h() = gd. Omega_h& omega_h) {__initialization(df. int main() { const int ndf = 2. stiff_sub[1][1] = k_* ( (Nx * (~Nx)) | d_l ). N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int. the_gh_array[node_order(node_no-1)][0] = M_.u_h() << endl. 2/*nen*/. k_ *= 2. &v). Element_Formulation* Beam_Penalty_Function_Formulation::make(int en. static const double E_ = 1. H1 X = N*xl. stiff_sub[1][0] = -(~stiff_sub[0][1]).⊗ --------- dx –  dx dx  Ωe ψ ψ ρ ∫ φ e ⊗ φ e dx Ω ψ λ dφ e dφ e 01 10 k e = – ( k e ) T = – ρ ∫  --------. gd. j < node_no. Beam_Penalty_Function_Formulation( int.u_h() = u_optimal. u_optimal = u. (1+ξ)/2}T ψ ψ dφ e dφe 00 k e = – ∫ EI  --------. static const double f_0 = 1. cout << "penalty parameter: " << k_optimal << endl << gd. i < node_no. static const int spatial_dim_no = 1. force_sub[1] = (((H0)N)*f_0) | d_l. int.gh_on_gamma_h(). Global_Discretization gd(oh.gd). dx  dx dx  Ωe w w dφ e dφ e 11 k e = ρ ∫ --------. gd. 2. }class Beam_Penalty_Function_Formulation : public Element_Formulation { public: Beam_Penalty_Function_Formulation(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation *make( int. gd. H1 Z(qp). the_gh_array[node_order(0)](0)=the_gh_array[node_order(0)](1)=gh_on_Gamma_h::Dirichlet.⊗ -------.0. (double*)0). 2. j++) w[j] = gd. Node* node = new Node(i. omega_h).rhs()))/((C0)(mr. gh_on_Gamma_h gh(ndf. spatial_dim_no.dx dx dx Ω 1 fe = Ωe ∫ φew fdx monitor convergence with norm(∆w) Listing 4•7 Beam-bending problem with penalty function formulation using linear line element (project: “beam_penalty_function_formulation” in project workspace file “fe. i++) { mr. static const double M_ = 1. stiff &= C0(4. } cout << "penalty parameter: " << k_ << " energy norm: " << norm(delta_w) << endl << gd. w_old = w. static const double I_ = 1. i++) { int ena[2].⊗ --------. Global_Discretization&). } for(int i = 0. J d_l(d(X)). double min_energy_norm = 1. 4. oh).u_h() = u.Chapter 4 Finite Element Method Primer #include "include\fe. the_omega_eh_array.0. 2. stiff_sub[0][0] = -( (E_*I_) * Nx * (~Nx) + k_ * (((H0)N)*(~(H0)N)) ) | d_l. ena[0] = i. static const int element_no = 4. } } gh_on_Gamma_h::gh_on_Gamma_h(int df. delta_w(node_no. for( int j = 0. Global_Discretization&). static const double L_ = 1. U_h uh(ndf. i < 10. delta_w = ((i) ? w-w_old : w). }. (double*)0). C0&". (double*)0). gh. stiff). static Element_Type_Register element_type_register_instance. Global_Discretization& gd) { return new Beam_Penalty_Function_Formulation(en. ena[1] = ena[0]+1. 1/*nsd*/. } Definte discretizaed global domain define nodes define elements define boundary conditions M(L) = 1 instantiate fixed and free variables and Global_Discretization w ψ φ e = φ e = {(1-ξ)/2. w_old(node_no. Omega_h oh.gh_on_gamma_h().0. if((double)norm(delta_w) < min_energy_norm) { min_energy_norm = norm(delta_w). uh). ena). 2). force).

-------. with u’( 0 ) = 0. with u’( 0 ) = 0.3 Nonlinear ODE Consider the nonlinear problem in Chapter 3 (page 236) du du 2 u 2 +   = 1. 4•65 An iterative algorithm is employed for this non-linear problem with uh interpolated at the element level as h i ˆi u e ≡ φ e u e . 4•60 can be rewritten as. Workbook of Applications in VectorSpace C++ Library 327 . and u ( 1 ) = 2 Eq.One Dimensional Problems 4. and (2) least squares formulation.– v h dx = 0 dx dx Eq. the weighted residuals statement gives 1 1 I ( uh ) ≡∫ 0 vh R ( u h ) dx = ∫ vh 0 d  h du h u -------. 4•62 Parallel to the development in Chapter 3. d du u = 1. 4•63 With Galerkin weightings vh . 4•64 Integrating by parts on the first term gives the weak formulation 1 I(uh) = ∫ 0 dv h du h – u h -------. we solve this problem in finite element with (1) Galerkin formulation. 4•61 Eq. and uh = vh + uΓg . dx dx 0 < x < 1. Galerkin Formulation Define the residuals of the problem as R( uh ) ≡ du h d h -------–1 u dx dx Eq. 4•60 with exact solution u exact ( x ) = 1 + x2 Eq. where uΓg is the essential boundary conditions.2.  d x dx 2 0 < x < 1. where “hat” denotes the nodal values.– 1 dx = 0 d x  dx  Eq. u ( 1 ) = 2 Eq. which is homogeneous at the boundaries.

The values of ue itself can be computed at the element level as 328 Workbook of Applications in VectorSpace C++ Library . The approximation in this equation is the Taylor expansion to the first-order derivatives. ˆ ˆ I( uk ) = vA ∀e Ωe ∫ k dφ e du e k . -  – ∫ ------. then.+ u e ------.0. therefore.⊗  φ e -------.u e ------. 4•67.+ u e -------- dx  = v A dx dx dx ∀e  Ω  e k dφ e du e dφe   k . dx  dx dx dx  Ω  e Eq. the initial values are set to unity. u0 = 1. 4•67. ˆ That is the increment of the solution δuk can be solved by ∂I ˆ δu k = ˆ ∂u –1 ˆ uk ˆ –I( uk ) ˆ I ( u k ) = --------------IT Eq. 4•66 ˆ ˆ ˆ where u k + 1 ≡ u k + δu k .element_fixed_variable(en).– u e ------. u0 = 0.-------. Therefore. 4•69 ˆ v is an arbitrary constant of global nodal vector and appears on both the nominator and denominator of Eq. uses an iterative algorithmic solve for the increment of the solution ( δu h )k with Eq. can be defined as IT ≡ ∂I ˆ ∂u = ˆ uk A ∀e j ˆ i d ( vi φe ) du k dφ e    j e k ˆ -  – ∫ -----------------. } The line 3 in the above assigns nodal free degree of freedom values plus nodal fixed degree of freedom values to “ul”.+ ------.k dx  dx dx  re ≡ Ωe ∫ k dφ e du e k .0. if(!initial_newton_flag) gl = 0.Chapter 4 Finite Element Method Primer ˆ ˆ ˆ ˆ I ( u k + 1 ) = I ( u k + δu k ) ≅ I ( u k ) + ∂I ˆ ∂u ˆ δu k = 0 ˆ uk Eq. 4•68 and. void Non_Linear_ODE_Quadratic::__initialization(int en) { ul &= gd. IT. 4•70. We define the element tangent stiffness matrix and element residual vector as ke T ≡ Ωe ∫– k dφ e du e dφ e ------.+ φ e dx dx dx Eq.⊗  φ e -------. and .element_free_variable(en) + gd. will lead to singular lefthand-side matrix.– φ e dx dx dx Eq. An initial values of zero. 4•67 where the tangent stiffness matrix. 4•70 The Program Listing 4•8 implements element formulation in Eq. φ e -------. In the element level the nodal value of ue is supplied by a private member function __initialization(int) of class Non_Linear_ODE_Quadratic as “ul” 1 2 3 4 5 static int initial_newton_flag.-------.u e  dx . it can be dropped.

static int initial_newton_flag. static gh_on_Gamma_h gh(ndf. static const int spatial_dim_no = 1. H0 Ux = d(U)/d(X). Omega_h::Omega_h() { for(int i = 0. } while((double)norm(du) > EPSILON). oh). void __initialization(int). Global_Discretization&).rhs()) = 0.+ ------. gd.u_h().⊗  φ e -------.0. the_gh_array[node_order(node_no-1)][0] = sqrt(2.u_h() = u. } for(int i = 0. static Non_Linear_ODE_Quadratic non_linear_ode_quadratic_instance (element_type_register_instance). static const double EPSILON = 1. initial_newton_flag = TRUE. initial_newton_flag = FALSE. (double*)0). if(!(u. static U_h uh(ndf. Workbook of Applications in VectorSpace C++ Library 329 .lhs())). } Non_Linear_ODE_Quadratic::Non_Linear_ODE_Quadratic(int en.gd).dsw” under directory “vs\ex\fe”). the_node_array. (C0)(mr. static Matrix_Representation mr(gd). } Element_Formulation* Element_Formulation::type_list = 0. Quadrature". } Element_Formulation* Non_Linear_ODE_Quadratic::make(int en.u_h() = gd. N[2] = Z*(1+Z)/2. } Definte discretizaed global domain define nodes define elements define boundary conditions du ( 0 ) = 0.element_fixed_variable(en). ena[2] = ena[0]+2.+ φ e dx dx dx ˆ = ∂I δu k ˆ ∂u –1 ˆ uk ˆ [ –I( uk ) ] ˆ = IT [ –I ( uk ) ] –1 ˆ ˆ ˆ u k + 1 ≡ u k + δu k reset left-hand-side and right-hand-side Listing 4•8 Solution of nonlinear ordinary differential equation using Galerkin formulation for finite element (project: “nonlinear_ode” in project workspace file “fe. ena[0] = i*2. 3/*nen*/. (C0)(mr. spatial_dim_no. i < node_no. public: Non_Linear_ODE_Quadratic(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation *make(int. Global_Discretization& gd) { return new Non_Linear_ODE_Quadratic(en. cout << gd. N[1] = (1-Z)*(1+Z). 0. v = ((double)i)/((double)(node_no-1)). H1 Z(qp). omega_h). N[0] = -Z*(1-Z)/2.u_h(). cout << norm((C0)(mr. gh.u e  dx dx  dx dx  re ≡ Ωe ∫ k dφ e du e k . stiff &= -(Nx * ~( ((H0)N)*Ux + Nx * ((H0)U) ) ) | d_l. class Non_Linear_ODE_Quadratic : public Element_Formulation { C0 ul. static const int element_no = 2.element_free_variable(en) + gd. u = 1. } } gh_on_Gamma_h::gh_on_Gamma_h(int df. } static const int ndf = 1. du. force &= ( ((H0)U) * Nx * Ux + ((H0)N) ) | d_l. unit = 1. H1 X = N*xl.0).-------. }.assembly().add(node).rhs())) / ((C0)(mr. the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Dirichlet. Omega_eh* elem = new Omega_eh(i. gd.add(elem). 3). int main() { C0 u. Non_Linear_ODE_Quadratic(int.lhs()) = 0. static Omega_h oh. ena[1] = ena[0]+1.rhs())) << " . i < element_no.rep_ptr())) { u = du.0. void Non_Linear_ODE_Quadratic::__initialization(int en) { ul &= gd.total_node_no(). uh). Quadrature qp(spatial_dim_no. u ( 1 ) = dx 2 instantiate fixed and free variables and Global_Discretization k eT ≡ Ωe ∫ k dφ e du e dφ e . i++) { double v. static Element_Type_Register element_type_register_instance. gd) { __initialization(en).e-12. ena). do { mr. 3. the_omega_eh_array. du = ((C0)(mr. N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int.0.0. 1/*nsd*/. H1 U = N*ul. } u += du. unit(gd. qp). Global_Discretization&).u e ------.One Dimensional Problems #include "include\fe. if(!initial_newton_flag) gl = 0. Node* node = new Node(i. Global_Discretization& gd) : Element_Formulation(en. gd.u_h(). H0 Nx = d(N)(0)/d(X). int.h" static const int node_no = 5. return 0.gh_on_gamma_h(). " << norm(du) << endl << gd. oh). 0.u_h() = unit.0. i++) { int ena[3]. static Global_Discretization gd(oh. Omega_h& omega_h) { __initialization(df. &v).k – ------. J d_l(d(X)).

. is shown in Figure 4•26.0008 0.0006 -0.2 0. as in line 4. The default behavior of the class Element_Formulation is that essential boundary conditions “gl” will be included in the computation of reaction. } while (.6 0.. to prevent the reaction to be subtracted out of the right-hand-side at every iteration.4 0. 330 Workbook of Applications in VectorSpace C++ Library . The error of this computation.0006 0. defined as the difference of the exact solution ( u ex ( x ) = 1 + x 2 ) and finite element solution.. the initial_newton_flag is set to FALSE (line 5).. For k > 0.Chapter 4 Finite Element Method Primer // u h ≡ φ i u e ˆi e e // due/dx H1 U = N*ul. at the element level the reaction can be prevent from subtracting out of the right-hand-side again.. only at the initial loop (k = 0) ˆ 0 .0004 -0. which is “stiff * gl”. and to be subtracted out from the right-hand-side ˆ vector.. . initial_newton_flag = TRUE. intial_newton_flag = FALSE. H0 Ux = d(U)/d(X). solution -0. This ad hoc mechanism is incorporated by a “initial_newton_flag” in the main() function as 1 2 3 4 5 6 7 int main() { .0002 Error = exact . Therefore. the reaction need to be subtracted out of the right-hand-side once for all.8 1 x Figure 4•26 Nonlinear finite element method using Galerkin formulation.e. 0. For the iterative algorithm which solves the increment of solution.0004 0. )..assembly().The nodal solutions are almost identical to the exact solution. do { mr. δu k . } // Newton iteration loop The “initial_newton_flag” is set to TRUE initially (line 2). “gl” when we compute δu is set to zero. .0002 -0.f.. After the global matrix and global vector have been assembled for the first time (line 4).

1•26 of Chapter 1 in page 35. R ( u h ) = 0 h  ∂u h  ∂u Eq. R ( u h ) +  ----------------. is Eq.+  ------. 4•75 The second derivatives is ˆi dφe dφ e d 2 φe d 2 φ e ∂ 2 R ( ue ) -------------------. and I T ≡ h  ∂u h  ∂u ∂2 R( uh ) ∂R ( u h ) ∂R ( u h ) =  -------------------. 4•71 Comparing to the weighted-residual statement ( w. with respect to the nodal variables ( u e ≡ φ e u e ).----------------. R ( u h ) . the residual. -----------------  ∂u h 2   ∂u h ∂u h  Eq. at the element level. the weighting function w is ∂R ( u h ) w = ----------------∂u h Eq. 4•72 For a non-linear problem.+ ---------..One Dimensional Problems Least Squares Formulation The basic idea of the least squares method is introduced in Eq.= φ e ⊗ ---------.= 2  ----------------.------ˆi dx dx dx 2 dx 2 ∂u e Eq. we define ∂R ( u h ) ∂I I ( u h ) ≡  ----------------. 4•73. R ( u h ) ) = 0 . The first-order condition for the minimization of the squares of the residual (Euclidean-) norm is 2 ∂ R( uh ) 2 ∂R ( u h ) -----------------------. – 1 2  dx  dx i ˆi and the first derivative of the residual.⊗ φ e + 2 ------.. 4•73 uh For the non-linear problem in the previous section. the element tangent stiffness matrix and the element residual vector are Workbook of Applications in VectorSpace C++ Library 331 .= φe ---------.+ 2 -------.. is d 2 ue du e 2 R ( u e ) ≡ u e ---------. 4•76 From Eq..+ u e ---------.⊗ ------2 2 i ˆe2 dx dx dx dx ∂u Eq. 4•74 i i ˆi dφ e du e d 2 ue d2 φe ∂R ( u e ) i .

Chapter 4 Finite Element Method Primer ke T ≡ Ωe ∫ ˆ ˆ ˆ ∂R ( u e ) ∂R ( u e ) ∂ 2 R ( u e ) ----------------. 4/*nen x ndf*/. a 2-node element can be used with the Hermite cubics discussed previously.pow(3).0/3. qp). 1.pow(2)-z). 0. we have 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 double weight[3] = {1. // du0/dx N[2] = 3.0*z.0/3. i < 4. qp). // Hermite cubics N[0] = 1.0*z. // tangent stiffness force &= -(Ru*uR) | d_l .0).+ ------------------. // R(u) Ru = ((H0)N)*Uxx + ((H0)U)*Nxx + 2. 4•77 ˆ ∂R ( u e ) r e ≡ – ∫ ----------------. Otherwise. // u1 N[3] = Z*(z. for(int i = 0. 3). H0 uR = ((H0)U)*Uxx + Ux.0}.0. Nx = d(N)(0). H2 Z((double*)0. 4. 332 Workbook of Applications in VectorSpace C++ Library .R ( u e ) dx ˆ ∂u e Ωe The Program Listing 4•9 implements Eq. An immediate difficulty associates with the least squares formulation is the presence of the second derivatives. Quadrature". qp). int. At the element level.pow(3). and Eq.0.0. Quadrature".0*(Nx%Nx). qp). 1/*nsd*/. J d_l(h_e/2. h_e = fabs( ((double)(xl[0] .0*z.pow(2) . } H2 U = N*ul. d2ue/dx2 Ux = d(U)(0).0. N = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE( "int. the second derivative on node will be infinite. Quadrature qp(weight. // dR/du Ruu = (((H0)N)%Nxx) + (Nxx%((H0)N)) + 2. h_e. Uxx = dd(U)[0][0].1. i++) { Nxx[i] = dd(N)(i)[0][0]. As we have discussed in the irreducible formulation for beam bending problem in page 306.⊗ ----------------. // u0 N[1] = Z*(1.pow(2).xl[1])) ). H0 Ux.0-z). 4. // residual The Hermite cubics (lines 9-12) are the same as those in the irreducible formulation except that we have positive signs for both du0/dx and du1/dx variables (which is taken as negative in bending problem conventionally to improve the symmetry of the formulation). the C1-continuity on node is required for the entire problem domain to be integrable. Nxx = INTEGRABLE_VECTOR("int. // due/dx. Quadrature". 4•77. As in the irreducible formulation for beam bending problem.pow(2)-2.0*z. and the entire problem domain is not integrable. if first derivative is not continuous on node. // du1/dx H0 Nx = INTEGRABLE_VECTOR("int.0/3. z = Z/h_e.pow(2)+2.0*Nx*Ux. 4. // d2R/du2 stiff &= ( (Ru%Ru + Ruu*uR) ) | d_l. This means that we need to have du/dx in the set of nodal variables to ensure the first derivative is continuous on the nodes. Uxx.R ( u e ) dx 2 ˆ ˆ ˆ ∂u e ∂u e ∂u e .0-3.

ena). &v). void Non_Linear_Least_Squares::__initialization(int en) { ul &= gd.element_fixed_variable(en). qp). } Definte discretizaed global domain define nodes define elements define boundary conditions du ( 0 ) = 0. Quadrature". static Global_Discretization gd(oh. Global_Discretization&). 4. Omega_eh* elem = new Omega_eh(i. the_gh_array[node_order(node_no-1)][0] = sqrt(2.h" static const int node_no = 5.0. 4. 0. Global_Discretization& gd) : Element_Formulation(en. N[2] = 3. Quadrature". J d_l(h_e/2.0. 0. uh). Node* node = new Node(i.0/3. i++) { int ena[2]. 3). void __initialization(int). } static const int ndf = 2. N = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE( "int. class Non_Linear_Least_Squares : public Element_Formulation { C0 ul. u ( 1 ) = dx 2 instantiate fixed and free variables and Global_Discretization Hermite cubics Workbook of Applications in VectorSpace C++ Library 333 . qp). h_e = fabs( ((double)(xl[0] .0. i < node_no. H0 Nx = INTEGRABLE_VECTOR("int. v = ((double)i)/((double)(node_no-1)). gh.0*z. gd) { __initialization(en). 1.gd).pow(2)-2. Global_Discretization&). double weight[3] = {1. static U_h uh(ndf. ena[1] = ena[0]+1. }. i++) { Nxx[i] = dd(N)(i)[0][0]. the_node_array. static Omega_h oh. Quadrature". for(int i = 0. 4/*nen x ndf*/. i < 4. } for( int i = 0.pow(3). the_omega_eh_array. spatial_dim_no. Quadrature qp(weight. Non_Linear_Least_Squares(int. 0.One Dimensional Problems #include "include\fe. N[1] = Z*(1.element_free_variable(en) + gd. } } gh_on_Gamma_h::gh_on_Gamma_h( int df. static int initial_newton_flag.0/3.pow(3).0*z.0-z). 1/*nsd*/. static gh_on_Gamma_h gh(ndf. } Element_Formulation* Non_Linear_Least_Squares::make(int en. omega_h). } Non_Linear_Least_Squares::Non_Linear_Least_Squares(int en.pow(2)-z). i < element_no. h_e.xl[1])) ).0). 2. qp).0*z. static const int element_no = 4.pow(2)+2. the_gh_array[node_order(0)](1) = gh_on_Gamma_h::Dirichlet. Omega_h::Omega_h() { for( int i = 0. int. i++) { double v.pow(2). if(!initial_newton_flag) gl = 0.0/3. Omega_h& omega_h) { __initialization(df. ena[0] = i. z = Z/h_e. H2 Z((double*)0. Global_Discretization& gd) { return new Non_Linear_Least_Squares(en. oh). public: Non_Linear_Least_Squares(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation *make(int. qp). oh). Nx = d(N)(0).add(elem).add(node).0*z. the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Dirichlet.0.0). N[0] = 1. Nxx = INTEGRABLE_VECTOR("int. N[3] = Z*(z.0}. static const int spatial_dim_no = 1.0-3. 4.

382 * length. (double*)0). du = (left + 0.+ ˆ ˆ ∂u e ∂u e ˆ ∂2 R( ue ) ------------------.0. (C0)(mr. double residual_golden_left = norm((C0)(mr.u_h() = unit.left. length = right . u.618 * length) * p. } Element_Formulation* Element_Formulation::type_list = 0. Matrix_Representation::Assembly_Switch = Matrix_Representation::ALL. static Non_Linear_Least_Squares non_linear_least_squares_instance(element_type_register_instance). Ruu = (((H0)N)%Nxx) + (Nxx%((H0)N)) + 2.assembly(). return 0. if(!(u.dsw” under directory 334 Workbook of Applications in VectorSpace C++ Library .e-2). } double left = 0. cout << "residual norm: " << norm((C0)(mr.u_h() << endl.⊗ φ e + ˆ dx 2 dx 2 ∂u e 2 dφ e dφe 2 ------. Uxx = dd(U)[0][0]. } ˆ d2 φe d2 φe ∂2 R ( ue ) ------------------.----------------. du. gd. length = right-left.0. H0 Ux. mr.0.lhs())). cout << "bracket: (" << left << ".618*length. cout << gd. force &= -(Ru*uR) | d_l .e-12. (C0)(mr. do { mr. double residual_golden_right = norm((C0)(mr. gd.pow(2) .0.rhs())) << " search direction norm: " << norm(p) << endl << “solution: “ << gd. du = (left + 0. u += du. H0 uR = ((H0)U)*Uxx + Ux. else right = left+0. } while(length > 1. " << right << ")" << endl.rhs()) = 0.+ ---------. static Matrix_Representation mr(gd).u_h() = u + du.gh_on_gamma_h().0*(Nx%Nx).0.⊗ ---------------.u_h() = gd.u_h().+  ------. gd.total_node_no()*ndf.assembly().rhs()) = 0.0.R ( u e ) dx ˆe ∂u Ωe line search golden section ˆ ˆ ˆ u k + 1 ≡ u k + δu k Listing 4•9 Solution of nonlinear ordinary differential equation using least squares formulation for finite element (project: “nonlinear_least_squares_ode” in project workspace file “fe.rhs())) / ((C0)(mr.0. int main() { C0 p.rhs())). Ru = ((H0)N)*Uxx + ((H0)U)*Nxx + 2. static const double EPSILON = 1. C0 unit(gd.Chapter 4 Finite Element Method Primer d2ue du e 2 R ( u e ) ≡ u e ---------.rhs())). gd. if(residual_golden_right < residual_golden_left) left = left + 0.rhs())=0.------dx dx ˆi dx 2 dx 2 ∂u e H2 U = N*ul. u = 1.0*Nx*Ux. Ux = d(U)(0). (C0)(mr.R ( u e ) dx ˆ ∂u e 2 ˆ ∂R ( u e ) r e ≡ – ∫ ---------------. } while((double)norm(p) > EPSILON). stiff &= ( (Ru%Ru + Ruu*uR) ) | d_l. (C0)(mr.1. Uxx. p = ((C0)(mr.u_h(). unit = 1.⊗ ------dx dx k eT ≡ Ωe ∫ ˆ ˆ ∂R ( u e ) ∂R ( u e ) ---------------. mr.0.assembly().+ 2 -------.382 * length)* p. right = 1. – 1 dx 2  dx  i i ˆi dφ e du e d 2 ue d2 φe ∂R ( u e ) i . static Element_Type_Register element_type_register_instance.= φ e ⊗ ---------.+ u e ---------.lhs()) = 0.rep_ptr())) { u = p.0. do { Matrix_Representation::Assembly_Switch = Matrix_Representation::RHS.= φ e ---------.u_h() = u + du.

mr.618*length. In the loop for the golden section line search.rhs()) = 0. the finite element method is to minimized the residuals of the problem.3 0.u_h() = u + du. du = (left + 0. Workbook of Applications in VectorSpace C++ Library 335 . with golden section. double residual_golden_right = norm((C0)(mr.rhs())=0. right = 1. du = (left + 0.2 0.0. which is implemented to tame the wild search path of the classical Newton’ method as introduced in Chapter 2 (see page 125). } while(length > 1.4 0. A quick fixed is to add line search algorithm. In place of evaluating the objective functional value in Chapter 2.0. (C0)(mr.0.u_h() = u + du.assembly().4 0. (C0)(mr. do { Matrix_Representation::Assembly_Switch = Matrix_Representation::RHS.4 0.1 x 0.6 1.One Dimensional Problems The nonlinear iterative algorithm with classical Newton’s method shows difficulty in getting convergence.2 du/dx0.7 0. The results are shown in Figure 4•27.0.rhs())). 1.4 0. length = right . double residual_golden_left = norm((C0)(mr. the assembly flag is reset back to assembly both the left-hand-side matrix and the right-hand-side vector (line 19).6 0. length = right-left. on top of the classical Newton’s method. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 double left = 0..2 0.618 * length) * p.left.382 * length)* p. Matrix_Representation::Assembly_Switch = Matrix_Representation::ALL.5 u 1.8 1 x Figure 4•27 Nodal solutions (open squares) comparing to the exact solutions (solid curves) for the nonlinear least squares formulation. At outer loop where Newton’s formula is used to compute the next search direction p.8 1 1.assembly(). else right = left+0.6 0. the assembly flag is set to only assemble the right-hand-side vector (line 3). . mr. if(residual_golden_right < residual_golden_left) left = left + 0.382 * length. The norm of the right-hand-side vector is used as the criterion for the line search minimization.rhs())).2 0.1 0.3 0.e-2).. gd. gd.

And. The protected member functions of the base class.dx ∂x ∂x ∂φ e ∂φ e Eq. The Program Listing 4•10 implements Eq. 4•80 and Eq.( 1.4. 4•78 where C is the heat capacity matrix.4 Transient Problems The transient problem is introduced in Section 3. ( C + ∆tθK )u n + 1 = ( C – ∆t ( 1 – θ )K )u n – ˆ f Eq. } Workbook of Applications in VectorSpace C++ Library // C + ∆tθK . 4•82. and f is heat source vector.2. return the_lhs.3. 3•191 of Chapter 3 (in page 253). the second time derivative of the displacement. 4•81 The finite element formulation for C and K is ce = Ωe ∫ φe ⊗ φe dx. the hyperbolic equation for structural dynamics ·· Mu + Ku + f = 0 Eq. ----. 4•79 where M is the consistent mass matrix. and u ( x. 0 < x < 1 subject to ∂t ∂x 2 ∂u u ( 0. The variable u is the dis·· placement and u . K the stiffness matrix and f the force vector.⊗ -------. gives the acceleration. K is the conductivity matrix. t ) = 0. the heat capacity matrix ce is the additional term to the static case as mass &= ((H0)N)%((H0)N) | dv. t ) = 0. and k e = Ωe ∫ -------. need to be overwritten in the derived class Parabolic_Equation as 1 2 3 4 336 and C0& Parabolic_Equation::__lhs() { the_lhs &= mass + theta_* dt_*stiff. Parabolic Equation From Eq. The variable u is · the temperature and u is the time derivative of temperature.Chapter 4 Finite Element Method Primer 4. 4•82 θ is a scalar parameter and ∆t is the time step length.– -------. 0 ) = 1 ∂x Eq. We consider the parabolic equation for heat conduction · Cu + Ku + f = 0 Eq. “Element_Formulation::__lhs()” “Element_Formulation::__rhs()”.= 0. At the element level. 4•80 Considering the initial-boundary value problem in page 253 ∂u ∂ 2 u ----.

void Parabolic_Equation::__initialization(int en. uh). static U_h uh(ndf. 2). }. i++) { double v. void __initialization(int. Quadrature".u_h()[node_no-1][0] << ")" << endl. gd. Global_Discretization& gd) : Element_Formulation(en. Node* node = new Node(i. Omega_h::Omega_h(){ for(int i = 0. return the_lhs. Global_Discretization& gd) { ul &= gd. the_omega_eh_array.assembly(). } } gh_on_Gamma_h::gh_on_Gamma_h(int df. } Parabolic_Equation::Parabolic_Equation(int en. N[0] = (1-Z)/2. Omega_h& omega_h) { __initialization(df. omega_h).add(node). i < node_no.rhs())). 2.rhs()) = 0. oh). the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Neumann. i++) { int ena[2]. Parabolic_Equation(int. static double theta_ = 0. J dv(d(X)). Global_Discretization&). static Matrix_Representation mr(gd). } if(i < 27) { (C0)(mr.u_h() = gd. " << gd. the_rhs += (mass .0. i++) { C0 u = decomposed_LHS*((C0)(mr. ena[0] = i.0. the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet. } int main() { for(int i = 0. ul. N[1] = (1+Z)/2.element_free_variable(en).05. Global_Discretization&). 0. (C0)(mr. public: Parabolic_Equation(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation *make(int. H0 Nx = d(N)(0)/d(X). Quadrature qp(spatial_dim_no.0. N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int.(1.dx ∂x ∂x C + ∆tθK ˆ ( C – ∆t ( 1 – θ )K )u n – f initial conditions ˆ ( C – ∆t ( 1 – θ )K )u n – f u n + 1 = -------------------------------------------------------( C + ∆tθK ) Listing 4•10 Solution of hyperbolic equation using center difference scheme in time dimension (project: “hyperbolic_equation” in project workspace file “fe. for(int i = 0. 1. } Element_Formulation* Element_Formulation::type_list = 0. gd) { __initialization(en. Global_Discretization&).0-theta_)*dt_*stiff)*ul.lhs())).0.gd). mr.assembly(). 0. ena[1] = ena[0]+1. static const int element_no = 4. C0& __rhs().⊗ ------. gd). 1. mass &= ( ((H0)N)%((H0)N) ) | dv. static Parabolic_Equation parabolic_equation_instance(element_type_register_instance). static Global_Discretization gd(oh. u = (" << gd. qp). static Omega_h oh. C0& __lhs(). double iptr. gd.u_h() = u. the_node_array. &v). if(modf( ((double)(i+1))/4.add(elem). } Definte discretizaed global domain define nodes define elements define boundary conditions instantiate fixed and free variables and Global_Discretization overwrite protected member functions heat capacitance c e = conductivity ke = Ωe ∫ φ e ⊗ φ e dx Ωe ∫ ∂φ e ∂φ e ------.u_h()[(node_no-1)/2][0] << ". } static const int ndf = 1.lhs()) = 0. i < element_no. return the_rhs. class Parabolic_Equation : public Element_Formulation { C0 mass.5. spatial_dim_no.0). 2. i < 28. mr. static gh_on_Gamma_h gh(ndf. } } return 0. H1 Z(qp). Workbook of Applications in VectorSpace C++ Library 337 . } for(int i = 0.h" static const int node_no = 5. } C0& Parabolic_Equation::__rhs() { Element_Formulation::__rhs().gh_on_gamma_h(). &iptr)==0) { cout << "time: " << (((double)(i+1))*dt_) << ".v=((double)i)/((double)element_no). stiff &= (Nx % Nx) | dv. static const int spatial_dim_no = 1. int. static double dt_ = 0.dsw” under directory “vs\ex\fe”). H1 X = N*xl. C0& Parabolic_Equation::__lhs() { the_lhs &= mass + theta_* dt_*stiff. gh. Global_Discretization& gd) { return new Parabolic_Equation(en. } Element_Formulation* Parabolic_Equation::make(int en. ena). Omega_eh* elem = new Omega_eh(i.One Dimensional Problems #include "include\fe. at (0. oh). i++) uh[i][0] = 1. C0 decomposed_LHS = !((C0)(mr. i < node_no. static Element_Type_Register element_type_register_instance.5.

4 0.2 t0.Chapter 5 6 7 8 9 4 Finite Element Method Primer f // –ˆ f // ( C – ∆t ( 1 – θ )K )u n – ˆ C0& Parabolic_Equation::__rhs() { Element_Formulation::__rhs(). return the_rhs.2 0. The results of this program are shown in Program Listing 4•10.8 1 x Figure 4•28Finite element solutions for the hyperbolic equation for heat conduction.4 0.6 t0 t0. the_rhs += (mass . which is outside of the time integration loop.(1. } In the main() function the decomposition of the left-hand-side matrix is done only once.8 0.4 0.0 t1. 1 0.6 t0.8 t1.6 0.2 u t0.0-theta_)*dt_*stiff)*ul.4 0.2 t1. 0 338 Workbook of Applications in VectorSpace C++ Library .

93 and p. 0 < x < 1. static U_h ddu_new(ndf. a 1 = -------. 1.. static U_h du_new(ndf. Workbook of Applications in VectorSpace C++ Library 339 . oh). 4•84 Consider the initial boundary value problem in page 258. oh).a 0 = -----------.= -----------------.– 2 .= 0 Eq.. -. for two-node element the Hermite cubics are required for the stiffness matrix as in the irreducible formulation of beam bending problem. oh).One Dimensional Problems Hyperbolic Equation ˆ ˆ From Eq.omega_h(). “Numerical methods in finite element analysis”. a 5 = ---. 3•211 of Chapter 3 (see page 257).dx ∂x 2 ∂x 2 Eq. t ) ∂u ( 1. and R n + 1 = – f n + 1 + ( a 0 u n + a 2 u n + a 3 u n )M + ( a 1 u n + a 4 u n + a 5 u n )C Eq. 339 in K-J Bathe and E. and k e = Ωe ∫ ∂ 2 φe ∂ 2 φe ---------. static U_h u_new(ndf. t > 0 2 ∂x 4 ∂t boundary conditions ∂u ( 0. and ------------------. oh). ∂4 u ∂2u -------. t ) = -----------------. t ) u ( 0. t ) = u ( 1. Global_Discretization& gd) { Omega_h& oh = gd.L. New Jersey. 4•86 The damping matrix ce is either in the form of me times damping parameter or in the form of Raleigh damping as a linear combination of me and ke. a 7 = γ∆t β∆t β∆t 2β β 2 β  β∆t 2 Eq. un + 1 at tn+1 need to be registered as 1 2 static U_h u_old(ndf. oh). the Newmark coefficients ai are 1 γ 1 1 γ ∆t γ .⊗ ---------. a 3 = ----. The Program Listing 4•11 implements the · ·· · ·· hyperbolic equation. 4•83 · ·· · · ·· ·· ·· where u n + 1 = a 0 ( u n + 1 – un ) – a2 u n – a 3 un and u n + 1 = u n + a 6 u n + a 7 u n + 1 . un at tn and un + 1 . K u n + 1 = R n + 1 is defined as ˆ ˆ · ·· · ·· K = K + a 0 M + a 1 C. Global_Discretization&) as 1 2 void Hyperbolic_Equation::__initialization(int en. 4•85 The finite element formulation for consistent mass matrix and stiffness matrix is me = Ωe ∫ φ e ⊗ φ e dx. static U_h ddu_old(ndf. by a private member function These variables are supplied to the element constructor Hyperbolic_Equation::__initialization(int. a 4 = -. inc. static U_h du_old(ndf. a 2 = -------.Wilson.= – --------.1 Again.. a 6 = ∆t ( 1 – γ ). 0) = sin(πx)-πx(1-x). oh).– 1. Prentice-Hall. 1976. 0 ) and initial conditions u(x. u n + 1 . Now variables un . Englewood Cliffs. p.= 0 ∂x ∂x ∂t ∂u ( x.– 1. u n .

the_omega_eh_array. Node* node = new Node(i. oh). static const int spatial_dim_no = 1. ul &= gd_u_old. static Global_Discretization gd(oh.ddu_old).gh. du_old). the_gh_array[node_order(0)](0) = the_gh_array[node_order(0)](1) = the_gh_array[node_order(node_no-1)](0) = the_gh_array[node_order(node_no-1)](1) = gh_on_Gamma_h::Dirichlet. C0& __rhs(). Global_Discretization&). Global_Discretization& gd) { return new Hyperbolic_Equation(en. i++) { double v = ((double)i)*h_e.h" static const int node_no = 5.element_free_variable(en). i++) { int ena[2]. class Hyperbolic_Equation : public Element_Formulation { C0 mass. gh. static U_h du_new(ndf. i < element_no. }. &v). } Element_Formulation* Hyperbolic_Equation::make(int en. Global_Discretization&). Omega_eh* elem = new Omega_eh(i. oh). void __initialization(int. static U_h u_old(ndf. public: Hyperbolic_Equation(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation *make(int. gh_on_Gamma_h& gh = gd.0. u_old). Omega_h& omega_h) { __initialization(df.omega_h(). Global_Discretization& gd) { Omega_h& oh = gd. gh. ddul &=gd_ddu_old. dul &= gd_du_old.add(elem). } Definte discretizaed global domain define nodes define elements define boundary conditions instantiate fixed and free variables and Global_Discretization overwrite protected member functions un · un ·· u n 340 Workbook of Applications in VectorSpace C++ Library . 2. static const int element_no = node_no-1.gh_on_gamma_h().gd).add(node). } } gh_on_Gamma_h::gh_on_Gamma_h(int df. 0. Hyperbolic_Equation(int. void Hyperbolic_Equation::__initialization(int en. uh). gh.element_free_variable(en). Global_Discretization&). oh). static U_h u_new(ndf. dul. Global_Discretization gd_ddu_old(oh. static U_h uh(ndf. spatial_dim_no.Chapter 4 Finite Element Method Primer #include "include\fe. static U_h ddu_new(ndf. oh). static const double h_e = L_/((double)(element_no)). ena[0] = i. Global_Discretization gd_du_old(oh. static const double L_ = 1. the_node_array. oh). ena[1] = ena[0]+1. oh). Global_Discretization gd_u_old(oh. oh). ena). i < node_no. ul. } static const int ndf = 2. } for(int i = 0. C0& __lhs(). static gh_on_Gamma_h gh(ndf. static U_h du_old(ndf. ddul. Omega_h::Omega_h() { for(int i = 0. omega_h).element_free_variable(en). static Omega_h oh. oh). static U_h ddu_old(ndf. 0.

0*z. a[4] = gamma_/beta_-1. } gd. the_rhs += mass * (a[0]*ul+a[2]*dul+a[3]*ddul).pow(3). static const double dt_ = 0. (C0)(mr.0. i < node_no. 4/*nen x ndf*/.0. i < 4. a 2 = -------β∆t β∆t β∆t 2 1 γ ∆t γ . J d_l(h_e/2. Global_Discretization& gd) : Element_Formulation(en. C0 decomposed_LHS = !((C0)(mr.lhs())). N[1] = -Z*(1.2)).0. static const double gamma_ = 0.0. i < 28. N[2] = 3. u: " << u_new[(node_no-1)/2][0] << endl. i++) { C1 x( ((double)i)*h_e ).dx ∂x 2 ∂x 2 K + a0 M + a1 C · ·· – f n + 1 + ( a 0 u n + a 2 u n + a 3 u n )M + · ·· ( a u + a u + a u )C 1 n 4 n 5 n u(x. gd. C0& Hyperbolic_Equation::__lhs() { the_lhs &= stiff + a[0]*mass.0*z.0-x).0*(gamma_/beta_-2. } if(i < 27) { (C0)(mr.pow(2)-z). -. a[1] = gamma_/(beta_*dt_). stiff &= (Nxx % Nxx) | d_l. a 4 = -.0. static Matrix_Representation mr(gd). j < ndf.rhs()) = 0. return the_rhs. i++) Nxx[i] = dd(N)(i)[0][0].0. a[7] = gamma_*dt_.pow(2).⊗ ----------.u_h()) ). int. gd) { __initialization(en. du_new = ((C0)du_old) + a[6]*((C0)ddu_old)+a[7]*((C0)ddu_new). qp). a[0] = 1.xl[1])) ). for(int j = 0.0*z.0*beta_)-1.0*z. Quadrature". a 5 = ---..pow(3). } } return 0. 0 ) and ------------------. a 1 = -------.5. &iptr)==0) { cout << "time: " << (((double)(i+1))*dt_) << ". mass &= ( ((H0)N)%((H0)N) ) | d_l.0.– 1.0).pow(2)+2. qp). mr. ddu_old = ((C0)ddu_new). u_old[i][1] = -d(w_0). for(int i = 0. 4.gh_on_gamma_h().0/(2. return the_lhs. } int main() { for(int i = 0.0-z).lhs()) = 0.25. 1/*nsd*/.u_h() = u. 0.0.0. ddu_new = a[0]*(((C0)u_new)-((C0)u_old))-a[2]*((C0)du_old)-a[3]*((C0)ddu_old).rhs())). u_new = ( (C0)(gd. } Element_Formulation* Element_Formulation::type_list = 0.0/3. N = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE( "int. qp). u_old = ((C0)u_new).– 1.0/3. i++) { C0 u = decomposed_LHS*((C0)(mr.– 2 2β β 2 β  a 6 = ∆t ( 1 – γ ).a 3 = ----.0/3. du_old = ((C0)du_new). z = Z/h_e.pow(2)-2. Quadrature qp(weight. double weight[3] = {1.0-3. N[0] = 1. a 7 = γ∆t ·· · ·· un + 1 = a0 ( un + 1 – u n ) – a2 un – a 3 u n · ·· ·· · u n + 1 = un + a6 u n + a 7 u n + 1 Listing 4•11 Newmark scheme for hyperbolic equation using finite element method. u_old[i][0] = ((C0)w_0). a[5] = dt_/2. h_e.0-gamma_). for(int i = 0. 4.0/(beta_*dt_). mr. } Hermite cubics me = Ωe ∫ φe ⊗ φe dx ∂ 2 φe ∂ 2 φe ke = Ωe ∫ ----------.u_h() = gd.0/(beta_*pow(dt_. H2 Z((double*)0. static Hyperbolic_Equation hyperbolic_equation_instance(element_type_register_instance).One Dimensional Problems Hyperbolic_Equation::Hyperbolic_Equation(int en. w_0 = sin(PI*x)-PI*x*(1. ∂u ( x. Quadrature". H0 Nxx = INTEGRABLE_VECTOR("int. 3). 1. N[3] = -Z*(z.0}.0). static Element_Type_Register element_type_register_instance.assembly(). double iptr. 0) = sin(πx)-πx(1-x). Workbook of Applications in VectorSpace C++ Library 341 .assembly().01. h_e = fabs( ((double)(xl[0] . } C0& Hyperbolic_Equation::__rhs() { Element_Formulation::__rhs(). static const double beta_ = 0. a[3] = 1. if(modf( ((double)(i+1))/2. j++) du_old[i][j] = ddu_old[i][j] = 0. gd). a[6] = dt_*(1. a[2] = 1. static double a[8].= 0 ∂t 1 γ 1 a 0 = -----------.

2 0.1 0. the time integration algorithm is to update variables un . when the variable “u_new”.28 t = 0. such as 1 2 ddu_new = a[0]*(((C0)u_new)-((C0)u_old))-a[2]*((C0)du_old)-a[3]*((C0)ddu_old). un . The results of this computation are shown in Figure 4•29. un + 1 . ·· · ·· This is implemented according to the formula for acceleration un + 1 = a 0 ( un + 1 – u n ) – a 2 u n – a 3 u n (line 1) and · · +a u +a u ·· ·· velocity un + 1 = un 6 n 7 n + 1 (line 2).06 0. u n n n + 1 is solved from back-substitution of glo· ·· bal stiffness matrix and global residual vector.4 0.element_free_variable(en).0 to 0.24 t = 0.1 x -0. Global_Discretization gd_u_old(oh.02 t = 0.04 t = 0. The finite element solutions of downward deflection are piece-wise cubic ˆ ˆ ˆ ˆ functions of nodal deflection “ u ” and nodal negative slope “ ψ ≡ -du/dx” (i.08 t = 0. initial condition t = 0 0.12 -0.gh.10 0. du_old). u n . and u ·· time tn+1.4 0. gh.2 t = 0.6 0.8 1 t = 0. Global_Discretization gd_ddu_old(oh. un + 1 at · .14 Figure 4•29 Beam vibration using finite element method with Newmark scheme to solve the hyperbolic equation. gh. respectively. un + 1 .20 0..2 t = 0.28 t = 0. The velocity and acceleration u n + 1 and u n + 1 at time tn+1 are computed at the global level in the main() program. u n at time tn to u n + 1 . · ·· · ·· Basically.16 t = 0. ul &= gd_u_old.26 t = 0.14 t = 0. At the beginning of time tn+1.element_free_variable(en). u_old).1 u u 0. du_new = ((C0)du_old) + a[6]*((C0)ddu_old)+a[7]*((C0)ddu_new).2 0.e.element_free_variable(en). 342 Workbook of Applications in VectorSpace C++ Library . u are given.8 1 0.Chapter 3 4 5 6 7 8 9 10 } 4 Finite Element Method Primer gh_on_Gamma_h& gh = gd. u = f( u .6 x -0.2 t = 0.18 t = 0. dul &= gd_du_old.1 t = 0. ddul &=gd_ddu_old. ψ ) for two-node Hermite cubic element).gh_on_gamma_h().ddu_old). Solutions of every four time steps are shown. Global_Discretization gd_du_old(oh. is available.16 to 0.2 t = 0.22 -0.

we collect degree of freedoms w and M together for each node.⊗ ---------- dx  dx dx  Ωe M M φe ⊗ φe  --------------------. the stiffness matrix size could be critical for limited computer memory space . the matrix form is 0 T a 00 a 00 b 00 a 10 b 10 … … a ( n – 1 )0 0 T a 01 a 01 b 01 a 11 b 11 … … a ( n – 1 )1 … … … … … … … … … … … … … … 0 T a0( n – 2) a0 ( n – 1 ) b0 ( n – 1 ) a1 ( n – 1 ) b1 ( n – 1 ) … … ˆ w0 ˆ M0 ˆ w1 ˆ M1 … … = f0 r0 f1 r1 … … f( n – 1 ) r( n – 1 ) 0 T a 10 0 T a 11 0 T a1( n – 2) … … 0 … … 0 … … 0 Eq. 4•89 T T T a ( n – 1 )0 b( n – 1 )0 a ( n – 1 )1 b( n – 1 )1 … … a ( n – 1 ) ( n – 1 ) a( n – 1 ) ( n – 1 ) w ˆ ( n – 1) b( n – 1 ) ( n – 1 ) ˆ M( n – 1 ) For large-size problem. dx – ∫  dx dx  = Ωe ∫ φew fdx + φew VΓ M φe ψΓh h Eq. 4•88 where the stiffness matrix has the size of 4x4 and the solution and force vectors have the sizes of 4. and even more seriously for the computation time. Following the finite element convention. At the global level.2. this is achieved with ˆ ˆ the expense of add nodal variables M in addition to w . we have ˆ 0 ae we T ae ˆ be M e = fe re Eq.⊗ --------. 4•44 for the element formulation w M dφ e dφ e – ∫  --------.One Dimensional Problems 4. Recall Eq. One approach to reduce the number of degree of freedom in the global ˆ ˆ matrix solution process is to separate the variables w and M in Eq. However.5 The Mixed Formulation Revisited—Matrix Substructure Method In the irreducible formulation for the beam bending problem (see page 306) the unwieldy piece-wise Hermit cubic functions are used for the C1-continuity on the nodes since the second-order derivatives appeared in the element stiffness matrix. dx  EI  0 –∫ ˆ we ˆ Me Ωe M w dφ e dφ e  ---------. 4•90 where B is symmetric negative definite. The mixed formulation for the same problem (see page 312) reduces the element stiffness matrix to have the first-order derivatives and only C0-continuity is required.1 Workbook of Applications in VectorSpace C++ Library 343 . 4•89 in global submatrix form as ˆ 0 A w f = T B ˆ r A M Eq. Rewriting the element formulation of Eq. 4•87. 4•87 Ωe Assign symbols for submatrices and subvectors in Eq. 4•89 at the global level.

4•90 is AM = f . the matrix solver in substructuring deals with B-1 and AB-1AT which are smaller matrices than the left-hand-side matrix in Eq. (2) shear force (V). 4•90. That is only a quarter of computation time is needed for the matrix solver using substructuring.multiplication of the symmetric positive definitive matrix B-1 such as “AB-1AT” is a similarity transformation which preserves the symmetric positive definitive property. Now. if necessary. Secondly. The cost for a matrix solver can be a function of cubic power of size. Observing that the first equation of Eq. 4•90. only A and B need to be stored in memory space that is only half of the memory space comparing to the entire left-hand-side matrix in Eq. 4•91 Therefore. Eq. 4•92 ˆ Note we have use the property that B is invertible. 4•90.1 1. 4•90. which is not without trouble for the matrix solver. The term r includes (1) negative slope (ψ). M can be recovered according to Eq. pre-and post. 4•90 with “matrix substructuring” (or static condensation). This is only possible because of the special properties of the submatrices in Eq. Note that the term f includes (1) the distributed load term. Firstly. ˆ ˆ M = B –1 ( r – A T w ) Eq. and (2) the off-diagonal matrices are transpose of each other. 4•94 ˆ ˆ We have relied on the property that AB-1AT is invertible. 4•92. let’s proceed with the substructuring. 4•93 Eq. the “nodal loading boundary condition” ψ Γ(treated as natural boundary condition specified corresponding to “M”-dof).Chapter 4 Finite Element Method Primer Matrix Substructuring We can solve the Eq. The solution using the substructuring technique has two major advantages. 4•89 has a lot of zero diagonals. 4•87. For the present case. With w solved. we have ˆ ˆ A T w + BM = r Eq. Therefore. Moreover. such that ˆ ˆ ˆ f = AM = AB –1 ( r – A T w ) = AB – 1 r – AB –1 A T w ˆ Now we can solve for w as ˆ w = ( AB –1 A T ) – 1 ( AB –1 r – f ) Eq. 4•92 with A. From second equation of Eq. MΓ} by subtracting “AT wΓ+BMΓ” out of r . 4•90 that (1) the diagonal submatrix B is symmetric positive definite (or negative definite) which is invertible. each of the inverse of B and the inverse of AB-1AT requires about one-eighth of computation time comparing to that of the solution of the left-hand-side matrix in Eq. it can also be inverted. 344 Workbook of Applications in VectorSpace C++ Library . and (2) the essential boundary conditions of {wΓ. 1. and (3) essential boundary condition of MΓ by subtracting “AMΓ” out of f. the term contains “f” in Eq. we pre-multiply Eq. We either need to use modified Cholesky decomposition with the diagonal pivoting or we need to ignore the symmetry and use LU decomposition with complete pivoting. the “nodal loading boundary condition” VΓ(treated as natural boundary condition specified corresponding to “w”-dof). Therefore.

the_gh_array[node_order(0)][0] = 0. mh). for the definition of submatrix A. the_gh_array[node_order(0)][0] = 0. mgh. 4•90. w h} and {Ωh. if(i == 0) { // wΓ(0) = 0. Omega_h& omega_h) : gh_on_Gamma_h() { gh_on_Gamma_h::__initialization(df. The objectoriented modeling of the finite element method in fe. 4•90. wh). int df. Global_Discretization wgd(oh. bending moment essential boundary condition the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Dirichlet. } else if(i == 1) { // MΓ(L) = M_. gh_on_Gamma_h_i wgh(0. // -dw/dx(0) = ψΓ(0) = 0. ndf. U_h mh(ndf. the_gh_array[node_order(node_no-1)][0] = M_.lib has four strong components.One Dimensional Problems Object-Oriented Modeling for Matrix Substructuring With the understanding that we want to implement finite element in terms of Eq. gh_on_Gamma_h_i mgh(1. and (4) matrix representation “MR”. 1 2 3 4 5 6 7 8 const int ndf = 1. oh). omega_h). However. rotation natural boundary condition the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Neumann. (1) discretized global domain Ωh. Omega_h oh. we may immediately recognize that the “fe. ˆ ˆ Firstly. M i} ˆ }. U_h wh(ndf. // Ωh // w ∈ Γ g. wgh. ndf. oh). // VΓ(L) = d/dx M(L) = d/dx (EI d2w/dx2) = 0. The rows ˆ -dof. and ψ ∈ Γ h ˆ // M h ˆ ˆ The two sets of boundary conditions “gh_on_Gamm_h_i” corresponding to two set of variables { w i} and { M i}.lib” has no provision to define and to solve a problem described by Eq. we and columns of submatrix B corresponding both to the M Workbook of Applications in VectorSpace C++ Library 345 . and V ∈ Γ h ˆ // w h // M ∈ Γ g. The combination of Ωh and uh now yield two difˆ now we have two (separate) sets of variables { w i} and { M i ˆ ˆ ferent Global_Discretizations with {Ωh. The constructor of this class is defined as 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 gh_on_Gamma_h_i::gh_on_Gamma_h_i(int i. The new class “gh_on_Gamma_h_i” is derived from the”gh_on_Gamma_h” with a subscript index included. the_gh_array[node_order(node_no-1)][0] = 0. Global_Discretization mgd(oh. for the definition of submatrix B we need the “Global_Discretization” with {Ωh.0. deflection essential boundary condition the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet.0. shear force natural boundary condition the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Neumann. oh). (2) discretized variables uh.0. M h}. M h} as their constituents. (3) element formulation “EF”. instead of one set of nodal variables combined together in the irreducible formulation as { w i. oh). } } ˆ Secondly.

8 }.gd). Global_Discretization& gd) : 2 Element_Formulation_Couple(en. Global_Discretization_Couple&). 7 N[0] = (1-Z)/2. Quadrature". Thirdly. 10 J d_l(d(X)). M M φe ⊗ φe 11 stiff &= -(1. 5 Beam_Mixed_Formulation(int. but also need to define the off-diagonal submatrix A. M }. N[1] = (1+Z)/2. 9 Element_Formulation* Beam_Mixed_Formulation::make(int en. which is the same as what is already available in fe. in the element formulation “EF”. Therefore. } For the diagonal submatrix B. 2). 7 Beam_Mixed_Formulation(int. Global_Discretization& gd) { return new Beam_Mixed_Formulation(en. We have the declaration of the “deflection-and-bending moment” coupled global discretization as static Global_Discretization_Couple gdc(wgd.gdc). the constructor of element formulation is 346 Workbook of Applications in VectorSpace C++ Library . M h}. Global_Discretization&). and columns of submatrix A corresponding to the w -dof. int. The user defined element formulation is derived from this “Element_Formulation_Couple” instead of the “Element_Formulation” such as 1 2 3 class Beam_Mixed_Formulation : public Element_Formulation_Couple { public: Beam_Mixed_Formulation(Element_Type_Register a) : Element_Formulation_Couple(a) {} // diagonal block formulation. w h} and h ˆ h {Ω . the definition of submatrix B ˆ to the M h ˆ h reference only to the single Global_Discretization of {Ω . mgd). submatrix B 4 Element_Formulation *make(int. 2/*nen*/. the constructor of element formulation is 1 Beam_Mixed_Formulation::Beam_Mixed_Formulation(int en. 8 H1 X = N*xl.0/E_/I_)* ( (((H0)N)*(~(H0)N)) | d_l ). submatrix A 6 Element_Formulation_Couple *make(int. w h} and {Ωh. The newly defined class is the “Element_Formulation_Couple” to handle this additional complexity. Global_Discretization&).Chapter 4 Finite Element Method Primer ˆ need both “Global_Discretization” with {Ωh. 2 Gaussian integration points 4 H1 Z(qp). 12 Global_Discretization_Couple& gdc) { return new Beam_Mixed_Formulation(en. // off-diagonal block formulation. } 10 11 Element_Formulation_Couple* Beam_Mixed_Formulation::make(int en. 9 H0 Nx = d(N)(0)/d(X).lib. // 1-dimension. For the definition of submatrix A it needs to refer to a newly defined class of ˆ “Global_Dsicretization_Couple” which is consists of “dual” Global_Discritization with both {Ωh. gd) { 3 Quadrature qp(spatial_dim_no. Global_Discretization_Couple&). we not only need to define the diagonal submatrix B. dx  EI  12 } Ωe For the off-diagonal submatrix A. qp). Since the row of submatrix A corresponding ˆ -dof. M }. // B = – ∫  --------------------. 5 N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( 6 "int. 1/*nsd*/.

0. The class “Matrix_Representation_Couple” is declared instead as Matrix_Representation_Couple mrc(gdc. 2 integration points 4 H1 Z(qp). “MR”. This matrix representation instance “mr” can be called to assemble and instantiate the submatrix B and the subvector r. The second argument of this constructor is reserved for instantiation sparse matrix. 9 H0 Nx = d(N)(0)/d(X). the third and the fourth arguments of this constructor referencing to right-hand-side vectors corresponding to the principal and the subordinate discretization of submatrix A. C0 B = ((C0)(mr. the null pointer. 2). int. 10 J d_l(d(X)).lhs())).rhs())). 0. 8 H1 X = N*xl. 7 N[0] = (1-Z)/2. // f = ∫ φ e fdx Ωe 13 } Ωe Finally.rhs()” in this case.One Dimensional Problems 1 Beam_Mixed_Formulation::Beam_Mixed_Formulation(int en. the principal right-hand-side is supplied with a “0”.⊗ ---------- dx  dx dx  w 12 force &= ( (((H0)N)*f_0) | d_l ). 2/*nen*/. w M dφ e dφ e 11 stiff &= -(Nx * (~Nx)) | d_l. N[1] = (1+Z)/2. such as the subordinate right-hand-side is reference to “mr. &(mr. Global_Discretization_Couple& gdc) 2 : Element_Formulation_Couple(en. qp). // diagonal submatrix B // r The rows of submatrix A corresponding to “w”-dof.assembly(). and the columns of submatrix A corresponding to “M”-dof. we recall the global submatrix form in Eq. 4•95 The matrix representation. 1/*nsd*/. In this case. for the diagonal submatrix B and its corresponding right-hand-side r is declared as standard class of “Matrix_Representation” Matrix_Representation mr(mgd). the principal discretization. 5 N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( 6 "int. Quadrature". the principal right-hand-side vector f will be instantiated.rhs()) ). When the argument is not null. r = ((C0)(mr. They can be retrieved by 1 2 3 mr. // A = – ∫  --------. The subordinate right-hand- Workbook of Applications in VectorSpace C++ Library 347 . In the above example. the subordinate discretization. 4•90 ˆ 0 A w f = T B ˆ r A M Eq. // 1-dimension. gdc) { 3 Quadrature qp(spatial_dim_no.

mh = mgd.assembly(). 6 C0 B = ((C0)(mr. return 0. 14 cout << "deflection:" << endl << wh << endl << "bending moment:" << endl << mh.lib” intact. However. 7 r = ((C0)(mr.inverse().lib” has been modified to deal with the new problem. the problem of “mixed formulation with separate variables” brings the greatest impact of change to fe. 5 mr. // M ˆ 11 wh = w.lib. The nonlinear and transient problems bring only marginal changes to the “fe. 15 16 } The complete listing of the substructure mixed formulation is in Program Listing 4•12. 348 Workbook of Applications in VectorSpace C++ Library . ˆ 9 w = (A*B_inv*r .lib” to be extended to vastly different area of problems.rhs()”. 8 C0 B_inv = B.2. We need to change all four strong components of the “fe. wh = wgd. discussed in the mixed formulation of Section 4. we are not only able to reuse the code in “fe. the beginner of the fe. The object-oriented programming provides the basic mechanisms for a smooth code evolution of “fe. The results of the present computation are completely identical to those of the previous section on mixed formulation.lib still only need to learn the unscrambled basic set of “fe.lib”. 13 mh = M.lhs())). After the “fe. This is similar to the class “Element_Formulation_Couple” in the present example is created for user to derived a user defined element formulation from it.lib” by deriving from it. which has already been instantiated. // w = ( AB –1 A T ) – 1 ( AB –1 r – f ) ˆ = B –1 ( r – A T w ) 10 M = B_inv*(r-(~A)*w).rhs())). but also are able to keep the simplicity of the “fe.gh_on_gamma_h(). can be turn on by setting macro definitions “__TEST_NODAL_LOAD” and “__TEST_DISTRIBUTED_LOAD” . Now we can solve Eq. The cases for nodal loading and distributed loading.assembly().lib” to implement this problem. For Fortran/C programmers who are already familiar with a couple of existing full-fledged finite element programs. With mechanisms of the object-oriented programming. We can even create a multiple inheritance (an advanced but controversial C++ feature) of class Nonlinear_Element_Formulation and class Transient_Element_Formulation to capture both the nonlinear and the transient capabilities.gh_on_gamma_h(). this advantage of using object-oriented programming to accommodate vastly different problems would be most immediately apparent.f)/(A*B_inv*(~A)).rhs())).lib” without to confront all kinds of more advanced problems in finite element at once.. 4 A = ((C0)(mrc.lhs())). 4•90 with the matrix substructuring such as 1 int main() { 2 mrc. 3 C0 f = ((C0)(mrc.Chapter 4 Finite Element Method Primer side will not be instantiated but will be referring to “mr.2. We certainly can create new classes of “Nonlinear_Element_Formulation” and “Transient_Element_Formulation” for a user defined element to derived from.

Omega_h& omega_h) : gh_on_Gamma_h() { gh_on_Gamma_h::__initialization(df.gd). the_node_array.add(elem). static gh_on_Gamma_h_i mgh(1. static Omega_h oh. static const double f_0 = 1. int df. static const int node_no = 5.One Dimensional Problems #include "include\fe. ena[1] = ena[0]+1. Element_Formulation* Beam_Mixed_Formulation::make(int en. static const double M_ = 1.add(node). Global_Discretization_Couple&). oh). static const double I_ = 1. static U_h mh(ndf. Beam_Mixed_Formulation(int. static const double L_ = 1. static Global_Discretization mgd(oh. wgh. if(i == 0) { the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Dirichlet.0.h" Matrix_Representation_Couple::assembly_switch Matrix_Representation_Couple::Assembly_Switch = Matrix_Representation_Couple::ALL.0. mgd). static Global_Discretization_Couple gdc(wgd. oh). w h} ˆ {Ωh. ndf. 0.0. i++) { int ena[2]. static gh_on_Gamma_h_i wgh(0. 0. oh). &v). static Global_Discretization wgd(oh. Global_Discretization& gd) { return new Beam_Mixed_Formulation(en. class Beam_Mixed_Formulation : public Element_Formulation_Couple { public: Beam_Mixed_Formulation(Element_Type_Register a) : Element_Formulation_Couple(a) {} Element_Formulation *make(int. static const int spatial_dim_no = 1. ena[0] = i. omega_h). Omega_eh* elem = new Omega_eh(i. mgh. } for(int i = 0. the_omega_eh_array. ena). ndf. M h} Global_Discretization_Couple Workbook of Applications in VectorSpace C++ Library 349 . i < element_no. 2. }. i < node_no. } } gh_on_Gamma_h_i::gh_on_Gamma_h_i(int i. i++) { double v = ((double)i)*h_e. } } static const int ndf = 1. wh). static const double h_e = L_/((double)(element_no)). mh). } initialize static member of class “Matrix_Representation_Couple” Definte discretizaed global domain define nodes define elements define boundary conditions instantiate fixed and free variables and Global_Discretization ˆ {Ωh.h" #include "include\omega_h_n. oh). Omega_h::Omega_h() { for(int i = 0. spatial_dim_no. Element_Formulation_Couple *make(int. Global_Discretization&). static const double E_ = 1. Node* node = new Node(i. Global_Discretization&). static const int element_no = 4. } else if(i == 1) { the_gh_array[node_order(node_no-1)](0) = gh_on_Gamma_h::Dirichlet. Beam_Mixed_Formulation(int.0.0. the_gh_array[node_order(node_no-1)][0] = M_. Global_Discretization_Couple&). static U_h wh(ndf.

} Element_Formulation_Couple* Beam_Mixed_Formulation::make(int en. J d_l(d(X)). N[1] = (1+Z)/2. return 0.f)/(A*B_inv*(~A)). N[0] = (1-Z)/2.assembly().2. H1 Z(qp). } Beam_Mixed_Formulation::Beam_Mixed_Formulation(int en. &(mr.lhs())). wh = w. H1 X = N*xl.0/E_/I_)* ( (((H0)N)*(~(H0)N)) | d_l ).rhs())). gdc) { Quadrature qp(spatial_dim_no. C0 f = ((C0)(mrc. N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int. wh = wgd. } Element_Formulation* Element_Formulation::type_list = 0. static Matrix_Representation_Couple mrc(gdc. Global_Discretization_Couple& gdc) : Element_Formulation_Couple(en.gh_on_gamma_h(). mr. qp). J d_l(d(X)). 350 Workbook of Applications in VectorSpace C++ Library .1. N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int. gd) { Quadrature qp(spatial_dim_no. H0 Nx = d(N)(0)/d(X). int. Global_Discretization& gd) : Element_Formulation_Couple(en. N[1] = (1+Z)/2. 0. int.⊗ ---------- dx  dx dx  Ωe w dφ e M dφ e f= Ωe ∫ φew fdx ˆ w = ( AB –1 A T ) – 1 ( AB –1 r – f ) ˆ = B –1 ( r – A T w ) ˆ M Listing 4•12 Substructure solution for the mixed formulation of the beam bending problem. Quadrature".qp). Global_Discretization_Couple& gdc) { return new Beam_Mixed_Formulation(en. H1 X = N*xl. w = (A*B_inv*r .Chapter 4 Finite Element Method Primer Beam_Mixed_Formulation::Beam_Mixed_Formulation(int en.rhs())). } B = – ∫  ---------------------- dx  EI  Ωe M M φe ⊗ φe A = – ∫  --------. C0 B_inv = B.inverse(). static Matrix_Representation mr(mgd). force &= ( (((H0)N)*f_0) | d_l ). 2). 2). static Element_Type_Register element_type_register_instance. H0 Nx = d(N)(0)/d(X).gdc). cout << "deflection:" << endl << wh<< endl << "bending moment:" << endl << mh. stiff &= -(Nx * (~Nx)) | d_l. static Beam_Mixed_Formulation beam_mixed_formulation_instance(element_type_register_instance).lhs())). H1 Z(qp). 2/*nen*/. 1/*nsd*/. 0.gh_on_gamma_h(). C0 B = ((C0)(mr. stiff &= -(1. M = B_inv*(r-(~A)*w). N[0] = (1-Z)/2. r = ((C0)(mr.assembly(). Quadrature".rhs()). &mr). mh = M. A = ((C0)(mrc. int main() { mrc. mh= mgd.

1 Heat Conduction Basic Physics and Element Formulation For heat conduction problem the divergence of heat flux of a body is equal to the heat generated from the source contained within the body as ∇•q = f Eq. 4•100 Since the “w” is homogeneous at Γg. and – q • n = h on Γ h . This is subject to Dirichlet and Neumann boundary conditions u = g on Γ g . We can solve these problems with the classical variational method as in Chapter 3. the boundaries with Dirchlet boundary conditions. we may argue that it is somewhat un-necessary to use finite element method for the 1-D problems. 4•96 with the Fourier law gives Ω ∫ w ( ∇•q – f ) dΩ = Ω ∫ w ( – κ ∇2u – f ) dΩ = 0 Eq. The weighted residual statement of Eq. We use “u” for temperature and n as the outward unit surface normal.3 Two Dimensional Problems We went through various 1-D proto-type problems in finite element method. Eq. 4•97 respectively. 4•99 Integration by parts and applying divergence theorem of Gauss to transform the volume integral into a boundary integral gives Ω ∫ ∇w ( κ ∇u ) dΩ + ∫ w ( – κ ∇u ) • n dΓ – ∫ wf dΩ Γ Ω = 0 Eq. However. the finite element method offers a systematic treatment of the complicated geometry where the use of the finite element method becomes essential. The Fourier law assumes that the heat flux can be related to temperature gradient as q = – κ ∇u Eq.Two Dimensional Problems 4. However. 4•98 where κ is the thermal diffusivity. 4. for more complicated geometry with the dimension greater than or equal 2-D.3. 4•96 where q is the heat flux and f is the heat source. 4•101 Workbook of Applications in VectorSpace C++ Library 351 . the second term of the boundary integral becomes Γ ∫ wq • n dΓ = – ∫ wh dΓ Γh Eq.

4•105 ˆa where u e is the nodal variables. φ e )u e h Eq.( 1 + ξ a ξ ) ( 1 + η a η ) 4 Eq. u e ≡ φ e u e . ηa) . and the coordinate transformation rule becomes a h x e ≡ N a ( ξ. where the Jacobian matrix of the coordinate transformation rule. and the third term – a ( φe .. the bilinear shape functions are taken for both the variable a h a a ˆa interpolation. η ) ≡ N a ( ξ. and (ξa. the element force vector is a a a a b b f e = ( φ e . η )x e Eq. 1). x ≡ φ e x e . -1).Chapter 4 Finite Element Method Primer the element stiffness matrix. η ) = -. 3 are four nodal coordinates {(-1. The variable interpolation becomes h ˆa u e ( ξ. (1. under finite element approximation. f ) + ( φ e . 1)} of the referential element. for a = 0. 4•102 and first term of Eq. 4•105 as 352 Workbook of Applications in VectorSpace C++ Library . For an isoparametric bilinear 4-nodes element.e. φ e ) = Ωe ∫ ( ∇φea κ∇φeb )dx Eq. the default behaviors of “fe. 4•107 The Gaussian quadrature requires the integration domain to be transformed from the physical element domain “Ωe” to the referential element domain “Ωe” with the Jacobian of the coordinate transformation as “J ≡ d et ( ∂x ⁄ ∂ξ ) ” (i. (1. 2. the determinant of the Jacobian matrix). Again. (-1. -1). h ) Γ is the Neumann boundary conditions. 4•104 index “a” indicates element node number. 4•106 a where x e is the element nodal coordinates. and  ∂ξ Ωe ∂x fe = Ωe ∫ ( Nf )dx = ∫ ( Nf )det  ------ dξ  ∂ξ Ωe ∂x Eq. “ ∂x ⁄ ∂ξ ”. 4•102 and. 4•103 gives ke = Ωe ∫ ( ∇N ⊗ κ∇N )dx = ∫ ( ∇N ⊗ κ∇N )det  ------ dξ . 1. that is 1 a φ e ≡ N a ( ξ. is ab a b k e = a ( φe . and the coordinate transformation rule. η )u e Eq. The derivatives of the variables are taken from Eq. φ e )u e accounts for the Dirichlet boundary conditions. is computed from the definition of the coordinate transformation rule in Eq. which is most easily specified in the problem h a b b definition as equivalent nodal load. h ) Γ – a ( φ e . The integration in Eq. 4•103 a The second term ( φ e .lib” will deal with these two terms automatically. 4•106.

4•109 That is.Two Dimensional Problems ˆ0 ue h ˆa ∇u e ( ξ. Eq. 4•104. 4•98. 4•108 The derivative of shape functions with respect to natural coordinates ∂N ⁄ ∂ξ . 4•96 and Eq. 4•111 reduces to Workbook of Applications in VectorSpace C++ Library 353 . 4•106 as ∂ξ ⁄ ∂x = ( ∂x ⁄ ∂ξ ) –1 Eq. Eq. η ) ) T u e = ∇N 0 ∇N 1 ∇N2 ∇N 3 ˆ1 ue ˆ2 ue ˆ3 ue ∂N 0 --------∂x = ∂N 0 --------∂y ∂N 1 --------∂x ∂N 1 --------∂y ∂N 2 --------∂x ∂N 2 --------∂y ∂N3 --------∂x ∂N3 --------∂y ˆ0 ue ˆ1 ue ˆ2 ue ˆ3 ue ∂ξ ----∂x = ∂ξ ----∂y ∂η -----∂x ∂η -----∂y ∂N 0 --------∂ξ ∂N 0 --------∂η ∂N 1 --------∂ξ ∂N 1 --------∂η ∂N2 --------∂ξ ∂N2 --------∂η ∂N 3 --------∂ξ ∂N 3 --------∂η ˆ0 ue ˆ1 ue ˆ2 ue ˆ3 ue ∂Na ∂ξ =  ---------  ----. 4•111 Since there is no heat source in the square area “f = 0”.  ----. η ) = ( ∇N a ( ξ. respectively. 4•110 An Example with Bilinear 4-Node Element We now consider an example of a 3 × 3 unit square insulated from the two sides with the top boundary and the bottom boundary set at 30 oC and 0 oC. is computed from the definition of the shape functions in Eq. 4•107 ∂N ∂x –1 ∇N =  -----.  ∂ξ   ∂ξ Eq. The term ∂ξ ⁄ ∂x is computed from the inverse of the derivative of the coordinate transformation rule from Eq. Combinding Eq. 4•108 gives the formula to compute the derivatives of shape functions matrix (nen × dof = 4 × 2) for the element stiffness matrix in Eq. The thermal diffusivity is assumed to be isotropic with κ = 1 (see Figure 4•30).  ∂ξ   ∂x T ˆa ue Eq. and due to symmetry of the boundary conditions no temperature gradient can be generated in x-direction. we have ∇•( – κ ∇u ) = – κ ∇2u = f Eq.

for(int i = 0. j++) { int nn = i*row_node_no+j. v). 4. and the row next to the top is u = 20 oC. ena). 4•112 That is the temperature gradient in y-direction is 10 (oC per unit length). the_node_array.add(node).add(elem). 2. d2u -------. int en = i*row_element_no+j. j < row_element_no. } 354 Workbook of Applications in VectorSpace C++ Library . ena[0] = nn. for(int i = 0. j++) { int nn = i*row_node_no+j. 4•107 for this simple problem. Node* node = new Node(nn.Chapter 4 Finite Element Method Primer utop= 30oC q•n=0 q•n=0 ubottom= 0oC Figure 4•30 Conduction in a square insulated from two sides.= 0 dy 2 ⇒ ( u top – u bottom ) du ----. the nodal solutions at the row next to the bottom is u = 10 oC. ena[3] = nn + row_node_no. 0. The nodes and elements can be generated as 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int row_node_no = 4. v[1] = (double)i. i < row_element_no. ena[1] = ena[0]+1. Omega_eh* elem = new Omega_eh(en. } int ena[4]. v[0] = (double)j. j < row_node_no. double v[2]. i++) for(int j = 0. the_omega_eh_array.1. ena[2] = ena[3]+1. row_element_no = row_node_no .= constant = -------------------------------------3 dy = 10 Eq. The Program Listing 4•13 implements element formulation for the stiffness matrix and force vector in Eq. i++) for(int j = 0. i < row_node_no. 0. In other words.

stiff &= (Nx * k * (~Nx)) | dv. Global_Discretization& gd) : Element_Formulation(en. j < row_node_no. gh. oh). v). } } gh_on_Gamma_h::gh_on_Gamma_h(int df. the_gh_array[node_order(first_top_node_no+i)](0) = gh_on_Gamma_h::Dirichlet. return 0. Node* node = new Node(nn. HeatQ4(int. i++) for(int j = 0. N[2] = (1+Zai)*(1+Eta)/4.add(elem). cout << gd. ena). top boundary u = 0oC bottom boundary u = 30oC define element 1 N a ( ξ. double v[2].dsw”. N[3] = (1-Zai)*(1+Eta)/4. j < row_element_no.gd). gd. gh_on_Gamma_h gh(ndf. Eta &= Z[1].add(node). } } class HeatQ4 : public Element_Formulation { public: HeatQ4(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation *make(int.C. }. 4. Omega_h& omega_h) { __initialization(df. ena[1] = ena[0]+1. ena[0] = nn. Global_Discretization&). the_gh_array[node_order(first_top_node_no+i)][0] = 30. N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int. (double*)0.  ----. Omega_eh* elem = new Omega_eh(en. Global_Discretization gd(oh. 0. project “2d_heat_conduction”.h" Omega_h::Omega_h() { int row_node_no = 4. i < row_node_no.1. gd) { Quadrature qp(2. i++) for(int j = 0. void output(Global_Discretization&). } define nodes define elements define B. } Element_Formulation* Element_Formulation::type_list = 0. en = i*row_element_no+j.u_h() = u.( 1 + ξ a ξ ) ( 1 + η a η ) 4 ∂N ∂x – 1 ∇N =  -----.gh_on_gamma_h(). row_element_no = row_node_no .0. N[1] = (1+Zai)*(1-Eta)/4. Omega_h oh. Global_Discretization& gd) { return new HeatQ4(en.inverse(). H1 Z(2. C0 u = ((C0)(mr. ena[3] = nn + row_node_no. ena[2] = ena[3]+1. i < row_node_no. omega_h). v[0] = (double)j. i++) { the_gh_array[node_order(i)](0) = gh_on_Gamma_h::Dirichlet. double k = 1.det()). } for(int i = 0.assembly(). Workbook of Applications in VectorSpace C++ Library 355 . for(int i = 0. 4. gd. int. N[0] = (1-Zai)*(1-Eta)/4.rhs())) / ((C0)(mr. H1 X = N*xl. mr. 2. qp). Zai. uh). qp). H0 Nx = d(N) * d(X).0.u_h(). the_node_array. j++) { int nn = i*row_node_no+j. J dv(d(X).Two Dimensional Problems #include "include\fe. 2. int main() { int ndf = 1. Element_Formulation* HeatQ4::make(int en. Eta. Matrix_Representation mr(gd). Global_Discretization&). v[1] = (double)i.  ∂ξ   ∂ξ ke = ∫ ( ∇N ⊗ κ ∇N )det  ------ dξ  ∂ξ Ωe ∂x assembly matrix solver update free and fixed dof output Listing 4•13 Two-dimensional heat conduction problem (project workspace file “fe. i < row_element_no. 4). } HeatQ4::HeatQ4(int en. Element_Type_Register element_type_register_instance. first_top_node_no = row_node_no*(row_node_no-1). oh). Quadrature". Zai &= Z[0]. int row_node_no = 4. η ) = -.u_h() = gd. for(int i = 0. int ena[4]. j++) { int nn = i*row_node_no+j. 0. the_omega_eh_array. static HeatQ4 heatq4_instance(element_type_register_instance).lhs())). U_h uh(ndf.

9 H0 Nx = d(N) * d(X). Zai.0}. 4. 8 H1 X = N*xl. The coordinate transformation rule in line 8 is from Eq. 6 N[0] = (1-Zai)*(1-Eta)/4. 4. 10 11 double k = 1. We observe that defining the nodes and the elements for a two dimensional problem may become a very complicated task. 0. 5 Zai &= Z[0]. 4). TRUE}. 4•104. the four control nodes are located at node numbers “0”. 3. {0. 3. 4•109 and Eq. Global_Discretization& gd) : Element_Formulation(en. 3 H1 Z(2. which is “4”. 4 N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int.0}. 0. 12 stiff &= (Nx * k * (~Nx)) | dv. int control_node_flag[4] = {TRUE. The second integer argument specifies the number of nodes generated column-wise. control_node_flag. A 2-D Geometrical Tool — “block()” Even with the above extremely simple problem. qp). block(this. respectively. 4•106. int*)”). 4. 4•110. In line 6 and 7. Eta. we use the member functions “Node::add(Node*)” and “Omega_eh::add(Omega_eh*)” to add to the “database” the information on nodes and elements. 2. The derivative of shape function are calculated according to Eq. the shape functions “N” is defined according to Eq. In this example. 4. TRUE. int.0}}. J dv(d(X). and “12” 356 Workbook of Applications in VectorSpace C++ Library . TRUE.. Eta &= Z[1]. gd) { 2 Quadrature qp(2. {3. double*)” and “Omega_eh::Omega_eh(int.det()).0}. After node and element are created by their own constructors (i. “3”.0.0. N[1] = (1+Zai)*(1-Eta)/4.inverse(). The first example constructs a set of nodes and elements with a single “block()” function call as (see Figure 4•31) 1 2 3 double coord[4][2] = {{0. the increasing difficulty in specifying geometry is exposed.e. The following integer is the number of control nodes. int. int. 13 } We use a 2-D 2 × 2 Gaussian quadrature for all integrable objects (line 2). The first integer argument specifies in “block()” the number of nodes generated row-wise. int. The rest of the code is not very different from that of a 1D problem.Chapter 4 Finite Element Method Primer This code generates 16 nodes and 9 bilinear 4-node elements in the constructor “Omega_h::Omega_h()”. 7 N[2] = (1+Zai)*(1+Eta)/4. At the heart of the code is the element constructor “HeatQ4::HeatQ4()” which implements a 4-nodes bilinear quadrilateral element 1 HeatQ4::HeatQ4(int en.0. qp). (double*)0. We will discussed this issue later with a simple 2-D tool—“block()” function that has already been introduced in Chapter 3 (see page 192) to enhance the capability to handle increasingly complicated geometry. Line 10 on “the Jacobian” and line 12 on stiffness matrix is the first part of the Eq. N[3] = (1-Zai)*(1+Eta)/4. 4•107. coord[0]). We use a few examples to demonstrate the tool “block()” function that facilitates the definition of 2-D geometry. Quadrature". {3.0. “Node:Node(int. “15”. int.0.

3. in which the database integrity is accomplished by checking the uniqueness of the node number. 1. {3. coord2[0]. the “block()” function use “Omega_h::set()” instead of “Omega_h::add()”. 0. “17” and “24” has been defined. the next int argument is the first element generated. 3. The row-wise node number and element number both skip “3”. The last two int arguments are “row-wise node number skip” and “row-wise element number skip”. Therefore.lib”. 4. these four nodes will be defined again. {0. 3.0}. 0. 3. control_node_flag. int control_node_flag[4] = {1.141592653509. The code for generating these eight blocks is 1 2 const double PI = 3. 1}. {6. 0.0}. ordered counter-clockwise starting from the lower-left corner. 4. 4. s4 = sin(PI/4. 3). This is followed by the pointer to double array “coord[0]”. 3.0). For example. 1.0}. “10”.0. 3.0}}. it will not be added to the database again. The first int argument after the coordinates of type double* is the first node number generated. the node number is the key of the database tabulae in this case.0. If a node number exist. 4. {3. Using the terminology of relational database. the first node number of the second row is “10” and the first element number of the second row is “9”.0}.Two Dimensional Problems 12 6 8 3 4 0 5 1 9 4 6 2 13 7 10 5 7 14 8 11 15 0 1 2 3 Figure 4•31 16 nodes and 9 elements generated by a single “block()” function call. block(this.0.0}. in line 5 the second block definition has both its first node and first element numbered as “3”. An example with two “block()” function calls has the potential of being more adaptive to deal with complicated geometry (see Figure 4•32) 1 2 3 4 5 double coord1[4][2] = {{0. 3. 0.0.0).0.0. The components in the int array of the “control_node_flag” are all set as TRUE (=1). Notice that in the semantics of C language (“pointer arithmatics”). 3). control_node_flag. coord1[0]. the coordinates of the control nodes are given as rectangles for simplicity. 4.0}. {6. On line 5.0}}. In this example. 0.0. In “fe. 3. When we define the first block in line 4 the nodes numbered “3”.0. when the “block()” function is called again. {3. Workbook of Applications in VectorSpace C++ Library 357 . 4. while the index “0” means with an off-set of zero from the first memory address of the double*. 0. A third example shows a cylinder consists of eight blocks (see Figure 4•33) which is even much more challenging. block(this. c4 = cos(PI/4. the expression of the symbol “coord” with “[]” means casting the double** to double*. coord2[4][2] = {{3.

131-99. 358 Workbook of Applications in VectorSpace C++ Library . 163 ui=100oC 130 ri = 0.5 97 64 uo = 0oC ro = 1 164 131 98 65 132 99 66 33 133 100 67 34 31 32 0 1 tie nodes Figure 4•33 A cylinder consists of eight blocks. Tie nodes 164-132.Chapter 4 Finite Element Method Primer 21 12 14 6 7 0 0 21 12 14 6 7 0 0 1 8 1 2 15 7 9 2 3 3 3 4 22 13 16 8 10 1 23 14 17 common nodes 10 11 11 4 5 12 5 6 13 17 18 9 10 19 11 8 1 2 24 15 7 9 22 13 16 8 23 14 17 24 15 17 9 18 10 25 16 19 11 12 5 5 24 15 25 16 6 26 17 20 27 13 26 17 20 27 10 10 11 11 2 3 4 3 4 Figure 4•32 A contiguous block generated by two “block()” function calls. 98-66. 65-33. and 32-0 are shown in the righthand-side. Open circles in the left-hand-side are control nodes.

Workbook of Applications in VectorSpace C++ Library 359 . -s8*r1}.5. flag. 5. 28. For the present case of the hollow cylinder made of one material.lib the nodes that are generated later is “tied” to the nodes that are generated earlier. coord5[7][2] = {{0. -s8*r2}}. -s4*r1}. {-c8*r1. The tie nodes are formed when the coordinates are found to be the same as that of any node generated previously. s83*r2}}.. s8*r1}.0}.0.0. 0.0.{-c4. 28). -r2}. 28). 4.0. 5.{-c83*r2.0}. 0.{-c8*r2. 5. s4*r1}. s83 = sin(3. 28).0). block(this. s4}. coord1[0].{-c4*r1. 5.{c8*r2. Oxford University Press.0}. and “132” are generated when the first “block()” function call is made. int flag[7] = {1.{0.0}. block(this. 28. When the eighth “block()” function call is made later.{-c4. 28).{0. 12. 0. 1}. 7. 0. r2}. s4*r1}. -s8*r2}}. 0.0. “98”. -s4*r1}.{0.0). 5. flag.0. “131”. coord3[0]. “Conduction of heat in solids”.{0.0. r1}. 0.{r2. -s4*r1}. s8*r2}}. 7.S. 12.{c4.0. 28. s8*r1}. In this example nodes “0”. {-c8*r1. s8*r2}}.0}. 7.{-c4*r1.{0. “65-33”. flag.{-c83*r2. 0. H. flag.{c8*r2. coord3[7][2] = {{r1. block(this. 24.0). The tie nodes are generated when different node number with same coordinates occurs. r1}. coord7[0].Two Dimensional Problems 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 c8 = cos(PI/8.{r1. 0. 5. 5.{-r1. 28).{c4. 4•33) are generated when the “tail” of the eighth block comes back to meet the “head” of the first block. “65”.0}.0. the Eq. 20. In fe. -r1}. flag.0). 0. block(this. For heat conduction problem. coord2[0]. {c83*r1. coord6[7][2] = {{-c4*r1. coord8[7][2] = {{-c4*r1. 7. “66”. 0. “33”. 4. 7. 28.{c4*r1.0}.{-c4.{-c4. 5. r1 = 0. 5. 28. 28).0}. s4}. it can well be written with axisymmetrical formulation and solve as an one dimension problem such as in the subsection under the title of “Cylindrical Coordinates For Axisymmetrical Problem” on page 302. flag.0. c83 = cos(3. coord7[7][2] = {{-r1. 1. 8. -s83*r2}}. 5.0*PI/8. s83*r2}}. 28. -s4}. 28. coord2[7][2] = {{c4*r1.0}. Jaeger. {c8*r1. 5. 4•111 expressed in cylindrical coordinates is1 1.{0. Five tie nodes “164-132”. Oxford. “99”. 0. and “32-0” (see right-hand-side of Eq. block(this. -s83*r1}. flag.{-r2. 0. 0. coord5[0].0}. coord6[0]. 0. 5. s4}.0.{c83*r2. UK.{0. block(this. double coord1[7][2] = {{0. flag. s4*r1}.0. 7.{0. -s83*r2}}. 28.{0. and J. 24.0}.0*PI/8. -r1}.{0.{c4.{0. 28). 0. 5. 189 in Carslaw. 1. nodes “32”.C. “131-99”. r2 = 1. {-c83*r1. 8. 1959. 0. and “164” will be generated.{r2.{-c8*r2. 2nd ed. 5. {c83*r1.0}.{0.0}. 1. -s4}. {c8*r1. if the boundary condition is symmetrical with respect to the center axis. 5. block(this. {-c83*r1. -s4*r1}. -r2}. 28. s4*r1}. 28).0}. 20. s8 = sin(PI/8.0.{c4*r1. coord4[0].0}. 7. p. 28. 1. 7.0. 0. 0. s83*r1}. coord8[0]. 0. -s8*r1}.{c4.{0.{c83*r2. -s4}. 5. 16.0. block(this. 16.0. coord4[7][2] = {{c4*r1.0}.{0. “98-66”. s4}. -s4}.0.{-r2. s83*r1}. -s83*r1}. r2}.

s}.0}. 0. 4•114 The finite element computation can be turned on using the same project “2d_heat_conduction” in project workspace “fe. The constants A and B are determined by imposing the boundary conditions. r ----. we approximate a quarter of a circle with three “block()” function calls. 6. 5. 5.141592653509. 4•114 for the heat conduction of the cylinder.0. 5.9 r Figure 4•34 Finite element nodal solutions in the radial direction comparing to the analytical solution in Eq.  ri  u exact Eq.5}}.dsw” by setting macro definition “__TEST_CYLINDER” at compile time. s2}. block(this. 1. {0. flag2. 4. flag3[7] = {1. 1. 0.{0. 5.0. 5. coord2[6][2] = {{0. 1. d du ---.0.0. 1}.0. 7.0. 1.0}.0). coord3[0]. double coord1[4][2] = {{0. 4•114 and shown in Figure 4•34.{0. 4.0}. Workbook of Applications in VectorSpace C++ Library . 4. block(this.0}.0}.8 0. c2 = c/2.{c. flag1. In the present case. we try to minimize the number of the tie nodes by the following code 1 2 3 4 5 6 7 8 9 360 const double PI = 3.For an additional exercise for function “block()”. we proceed with the fourth example of using three blocks to approximate a quarter of a circle. if at inner side of the cylinder of ri the temperature is kept at ui. flag2[6] = {1.0.0.{0. 1. 4).Chapter 4 Finite Element Method Primer 100 80 60 o C 40 20 0. coord3[7][2] = {{0.0). 4. s = sin(PI/4. flag3. 1}.0. 5.5.0}.0.5.0).6 0. sin(3.{1. we have the solution as ro r u i ln  --.0*PI/8. {cos(3. {0. 0. coord1[0]. s}. = 0 dr  dr  Eq.0). 4). 1. In that case we do not have provision of repeated definitions of nodes. + u o ln  -. block(this.{c2. 0. The finite element solution in the radial direction is compared to the analytical solution of Eq.0}. c = cos(PI/4.0.  r  ri = ---------------------------------------------ro ln  --. 0. and at outer side of the cylinder of ro the temperature is kept at uo.0)}}. 1}. 45. 4•113 The general solution is u = A+B ln r. 1. 0. coord2[0]. 0. 0. s2}. 0).0.{cos(PI/8. 4. {0. int flag1[4] = {1. 0.5}.{c2.0)}}. 0. s2}. For example.0}.7 0.{c.sin(PI/8. 1.0*PI/8.5 0. 0. 32. s2 = s/2. In Chapter 3 on page 195.1.{c2.

Element_Formulation* HeatQ9::make(int en. 9 tie-nodes will be generated including “45-36”. and “69-44”. } HeatQ9::HeatQ9(int en.Two Dimensional Problems 65 60 55 50 67 61 68 62 56 57 63 51 69 52 58 45 53 64 46 59 47 36 54 44 37 48 43 49 42 38 39 41 35 27 40 34 33 28 32 29 30 25 26 18 31 24 19 20 23 21 22 17 16 9 10 14 15 11 12 13 0 1 2 3 4 5 67 8 node numbering Figure 4•35 Three block function calls to approximate a quarter of a circle. “46-37”. “64-43”. Lagrange 9-nodes Element for Heat Conduction The element formulation “HeatQ4” implemented the bilinear 4-node element for heat conduction. “48-39”. Global_Discretization& gd) { return new HeatQ9(en. Global_Discretization&). HeatQ9(int. qp). Global_Discretization& gd) : Element_Formulation(en. “47-38”. }. // 2-d 3 × 3 Gaussain quadrature H1 Z(2. After the third block has been generated. The right-handside shows the element numbering scheme and the left-hand-side shows the node numbering scheme. (double*)0. Global_Discretization&). 66 44 45 40 41 46 36 42 47 32 37 38 43 33 39 31 24 25 34 35 30 29 26 23 2728 16 17 22 18 19 2021 15 8 9 10 13 14 11 12 0 1 2 3 4 5 6 7 element numbering The numbering of the elements and nodes for the first two blocks are similar to that of the second example. “59-42”. N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( Workbook of Applications in VectorSpace C++ Library 361 . “54-41”. 9). gd) { Quadrature qp(2. “49-40”. We introduce a Lagrangian 9-node element “HeatQ9” as follows 1 2 3 4 5 6 7 8 9 10 11 12 13 class HeatQ9 : public Element_Formulation { public: HeatQ9(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation *make(int.gd).

coord3[0]. 9 block(this. 5 block(this. coord6[0]. N[1] -= (N[4]+N[5])/2. 1).dsw”. 14.pow(2)). 1). 1). int. 5. 5.pow(2))*(1-Eta. 14. coord7[0]. 5.pow(2))*(1+Eta)-N[8])/2. N[4] = ((1-Zai. 7 block(this.pow(2))*(1-Eta)-N[8])/2. flag. 5. Zai &= Z[0]. 5. N[0] = (1-Zai)*(1-Eta)/4. 6 block(this. H1 X = N*xl. 14. 28. double k_ = 1. 6. coord4[0]. flag.pow(2))*(1-Zai)-N[8])/2. 0. // element type # 1 // element type # 0 Lines 17-24 are shape function definition for Lagragian 4-to-9-node element that we have already used in Chapter 3. 10 block(this. 14. 4. 16. 1). coord8[0]. Zai. 1). Omega_h::Omega_h() { . 14. 12. N[0] -= N[8]/4. 28. Element_Type_Register element_type_register_instance. 4 block(this. 28. 28. static HeatQ9 heatq9_instance(element_type_register_instance). 8. flag.pow(2))*(1+Zai)-N[8])/2. 28. 2/*nsd*/. 14. static HeatQ4 heatq4_instance(element_type_register_instance). 11 } Line 1 specified the elements generated are Lagragian 9-nodes elements. 7. 5. // {9 × 2}*{2 × 9}={9 × 9} } Element_Formulation* Element_Formulation::type_list = 0. 1). 0. 5. 14. N[7] = ((1-Eta. 5. 3 block(this. 7. 1). N[2] -= (N[5]+N[6])/2. 5. N[3] -= N[8]/4. N[3] = (1-Zai)*(1+Eta)/4. 5. 28. 5. 5. 8. 5. H0 Nx = d(N) * d(X). flag. flag. 20. N[1] -= N[8]/4. 362 Workbook of Applications in VectorSpace C++ Library . N[1] = (1+Zai)*(1-Eta)/4. 4. 7. 7. Eta &= Z[1]. and 34 register the element formulations.0. This number increases backwards to element(s) registered earlier. 7.. 12. N[5] = ((1-Eta. Eta. 8 block(this. // 4-9 node shape functions N[2] = (1+Zai)*(1+Eta)/4. 14. The last integer argument in line 3 to line 10 indicate the element type number is 1. coord1[0]. stiff &= (Nx * k_ * (~Nx)) | dv. 5. The last element formulation register has the element type number “0”. N[8] = (1-Zai. J dv(d(X). 5. 28. The computation of the Lagragian 9-node elements can be activated by setting macro definition “__TEST_QUADRATIC_CYLINDER” for the same project “2d_heat_conduction” in the project workspace file “fe.inverse(). 5. We can also use the “block()” function call to define Lagrangian 9-node element as (see Figure 4•36) 1 2 EP::element_pattern EP::ep = EP::LAGRANGIAN_9_NODES. 9/*nen*/. 28. Lines 33. flag. 24.Chapter 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 4 Finite Element Method Primer "int. which corresponding to the “HeatQ9” element that we just registered. coord2[0]. Quadrature". 2. 1). flag.. coord5[0]. N[3] -= (N[6]+N[7])/2. 7. N[6] = ((1-Zai. flag. 7. 14. 7. N[0] -= (N[4]+N[7])/2.det()). 10. qp). 28. N[2] -= N[8]/4.

h ˆa q e = – κ ( ∇N a ( ξ. Post-Processing—Heat Flux on Gauss Points After the solutions on temperature distribution is obtained. η )u e ) Eq. heat flux can be computed from Fourier law of heat conduction of Eq. are obtained.e. on Gaussian integration points are available at the constructor of class “Element_Formulation”. η ) ≡ ∇N a ( ξ. after the solutions of nodal values. 4•98. q = – κ ∇u This step is often referred to as post-processing in finite element method. such as.. u e .Two Dimensional Problems Figure 4•36 9-node Lagrangian quadrilateral elements generated by eight “block()” function calls. The derivatives of shape function. η )u e Eq. ∇N a ( ξ. Workbook of Applications in VectorSpace C++ Library 363 . 4•115 Therefore. i. η ) . The gradients of temperature distribution are approximated by h ˆa ∇u e ( ξ. we can loop over each element to calculate the heat flux on its Gaussian integration points. 4•116 ˆa Therefore.

.0.Chapter 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 4 Finite Element Method Primer HeatQ4::HeatQ4(int en. The information on the coordinates of the Gauss points and their corresponding heat flux values are reported element-by-element.. cout << setw(9) << " elem #. if(Matrix_Representation::Assembly_Switch == Matrix_Representation::FLUX) { H0 flux = INTEGRABLE_VECTOR("int. gd) { . 2.quadrature_point_value(i) << setw(14) << (flux[0].assembly(FALSE).quadrature_point_value(i)) << setw(14) << (flux[1]." << setw(14) << " q_x." << setw(14) << "y-coor. qp). Matrix_Representation::Assembly_Switch = Matrix_Representation::FLUX. 4•117 364 Workbook of Applications in VectorSpace C++ Library . The rest of lines is just the run-of-the-mill C++ output formatting. } Line 27 in the main() program is to call “Matrix_Representation::assembly()” with an argument “FALSE” to indicate that the nodal loading on the right-hand-side is not to be performed... mr. // q = – κ ∇u flux[1] += .k * Nx[i][1]*(ul[i]+gl[i]).flush(). This function invokes element formulation with a flag in class “Matrix_Representation” set to “Matrix_Representation::Assembly_Switch = Matrix_Representation::FLUX”. i++) { flux[0] += . flux = 0. requires much more elaborated postprocessing.. cout.ios::adjustfield).quadrature_point_value(i) << setw(14) << ((H0)X[1]). " << setw(14) << "x-coor. } } else stiff &= .no_of_quadrature_point().. cout. nodal heat flux. Post-Processing—Heat Flux Nodal Projection Method Since the solutions of finite element computation are the temperatures on nodes. The heat flux on an element can be interpolated from the nodal heat flux as h ˆa q e ≡ N a ( ξ.. i < nen. Global_Discretization& gd) : Element_Formulation(en. i < nqp. cout << "Heat flux on gauss integration points: " << endl. } int nqp = qp. η )q e Eq. for(int i = 0. } int main() { . " << setw(14) << " q_y. for(int i = 0.setf(ios::left. i++) { cout << setw(9) << en << setw(14) << ((H0)X[0]). Quadrature". where these two lines simply implemented the Fourier law of heat conduction. we may also interested in ˆa having the heat flux to be reported on nodes. The real computation is done at lines 7-8. However.k * Nx[i][0]*(ul[i]+gl[i]).quadrature_point_value(i)) << endl. " << setw(14) << endl. q e ..

4•116 into Eq. Taylor. 1989. Inc. “The finite element method: basic formulation and linear problems”. Englewood Cliffs. McGraw-Hill. 4•119. 3•105 of Chapter 3 on page 352) with Galerkin weighting that w = Na Ω ∫ Na ( qeh – qeh ) dΩ = 0 Eq.. London. 1. 4•120 which alleviates the need for matrix solver is to define lumped mass matrix as   ≡   ML ∑ ∫ Na Nb dΩ. and R.. 346 in Zienkiewicz. M. 1.Two Dimensional Problems Since the shape function is an integrable object. qe . b Ω a=b a≠b Eq.2 An alternative thinking on Eq. Taylor. T. 4•119 Ω We identify.1 An approximation on Eq. see appendix 8 in Zienkiewicz.C. “The finite element method: basic formulation and linear problems”. McGraw-Hill. 4•117 with qe of Eq. see also p. 4•118 Substituting Eq. the consistent mass matrix (with unit density).. 4•117 and Eq. O. and R. 4•118 of Galerkin weighting of the weighted-residual statement is that we can write least-squares approximation of error as Ω ∫ ( qeh – qeh ) 2 dΩ = 0 Eq. PrenticeHall. UK.C. 226 in Hughes. New Jersey. 4•122 1.L. its value is actually evaluated and stored only at the Gauss inteh h gration points. 4•120 ˆa The nodal heat flux. This nodal solution procedure is described as smoothing or projection in finite element. can be solved from Eq..L. vol. 4•116. 4•119.. This error is then distributed over the element domain by making a weighted-residual statement (as Eq. in Eq. vol. we have   b ˆ  ∫ Na N b dΩ q e = Ω  ˆ ∫ ( Na ( – κ ∇Nb ueb ) ) dΩ Eq. 1989. 4•121 0. Now we can define error as the difference of qe of Eq. London. 2. O. 4•118. 4the ed. “The finite element method: linear static and dynamic finite element analysis”. Workbook of Applications in VectorSpace C++ Library 365 . UK.R. 4the ed. This is the row-sum method among many other ways of defining a lumped mass matrix.. as M ≡ ∫ N a N b dΩ Ω Eq. J. p.

. Therefore. 28 cout << "{ " << node_no << "| " 29 << (mr. 6 C0 projected_nodal_flux = SUBVECTOR("int.k * Nx[i][0]*(ul[i]+gl[i]). i++) { 27 int node_no = oh. k < nen. 4•118. C0&". and using its interpolation relation of Eq. or to invoke matrix solver to solve for Eq. Global_Discretization& gd) : Element_Formulation(en.. cout << "nodal heat flux:" << endl. 1 HeatQ4::HeatQ4(int en. it will be more desirable to have a simplified approximation that can be performed element-byelement without even to assemble the global mass matrix. for(int i = 0. 7 H0 flux = INTEGRABLE_VECTOR("int. 11 flux[1] += . 31 } 32 . 24 mr. q e .node_array()[i].Chapter 4 Finite Element Method Primer ˆa Minimization by taking derivative with respect to the nodal heat flux. 17 projected_nodal_flux(i) = ( ((H0)N[i])*flux | dv ) / lumped_mass. 4•119 can be solved with a full-scale finite element method. 8 flux = 0. 3 if(Matrix_Representation::Assembly_Switch == Matrix_Representation::NODAL_FLUX) { 4 int flux_no = 2. 20 } 21 int main() { 22 . k++) 15 16 lumped_mass += (((H0)N[i])*((H0)N[k])) | dv.assembly(FALSE). for(int k = 0.k * Nx[i][1]*(ul[i]+gl[i]).global_nodal_value()[i][1]) << "}" << endl. i < nen. flux_no. the nodal flux can be considered as obtained through least squares approximation too. 33 } 366 Workbook of Applications in VectorSpace C++ Library ..0). Eq. 18 } 19 } else stiff &= (Nx * k * (~Nx)) | dv. i < oh.global_nodal_value()[i][0]) << ". such as.. as post-processing procedure.. gives back to Eq. 23 Matrix_Representation::Assembly_Switch = Matrix_Representation::NODAL_FLUX..node_no(). qp). flux_no. Quadrature". 4•117. (double*)0). 12 } 13 for(int i = 0. " 30 << (mr. However. i++) { 9 10 flux[0] += . 4•119. 25 26 for(int i = 0. the_element_nodal_value). 5 the_element_nodal_value &= C0(nen*flux_no.0.total_node_no(). gd) { 2 . direct or iterative. i < nen. i++) { 14 C0 lumped_mass(0.

4•119.6 0.Two Dimensional Problems Lines 14-16 are the row-sum lumped mass matrix (in Eq. 4•114.8 0. Heat flux on Gauss points are shown in open circles. (b) projected radial heat flux on nodes are shown in open squares. The computation is done with macro definitions “__TEST_CYLINDER” and “__TEST_FLUX”. The projected nodal heat fluxes on the boundaries are significantly less accurate than those in the interior. 280 260 240 220 200 180 160 0. since the nodal heat fluxes (open squares) are just least squares fit of a set of piece-wise line segments of the Gauss point heat fluxes (open circles). which is obtained from differentiation with respect to r on Eq.9 q r (b) (a) Figure 4•37 (a) Nodal heat flux shown in vectors. The solid curve is the analytical solution qr = du/dr = 100/(r ln 2). The assembly() function keeps and internal count on how many evaluations are performed on a particular node. Line 17 is the element-by-element solution of approximated Eq. The reason can be easily deduce by studying Figure 4•37b. Workbook of Applications in VectorSpace C++ Library 367 . Nodal values are shared by various number of elements.5 0. In the main() program “NODAL_FLUX” switch is set (line 23) and assembly() invokes the nodal projection procedure through Element_Formulation.7 0. and it will compute an average nodal value from these nodal values for the node. 4•121). The results of nodal heat flux are shown in Figure 4•37 The projected nodal heat flux values are obviously less accurate than the temperature solutions shown in Figure 4•37a.

4•123 reduces to the continuity equation ∂u ∂v ----. 4•125 of condition of irrotationality.= 0 .+ ----. 4•127 Substituting Eq. and curl u ≡ ∇×u = 0 . This is the condition that ψ to be a potential function in calculus. Similarly starting from Eq.– ----. Along a stream line the volume flux across it is zero by definition. 4•125. defined as dψ = u dy . 4•127 back to Eq. gives the volume flux across the path. ∂x ∂y Eq.= 0 ∂y ∂x Eq. The conditions of incompressible (a solenoidal field) and irrotational (a toroial.free field) give div u ≡ ∇•u = 0. 4•124. as 368 Workbook of Applications in VectorSpace C++ Library . According to Stokes’s theorem circulation along any closed loop is zero.+ -------. Eq.v dx where ψ is a scalar function. and v = – -----∂y ∂x Eq. 4•128 We identify that ∇2ψ = 0 is the Laplace equation. Integration of Eq. 4•127 into the condition of irrotationality. 4•124 gives the identity of cross derivatives of ψ to be equal. That is along a streamline ψ is constant. Eq. inviscid fluid which gives the potential flow. Eq. as shown in Figure 4•38a. it follows that u dy .2 Potential Flow Basic Physics and Element Formulation We consider incompressible. 4•126 Eq. Substituting Eq. gives ∂2 u ∂ 2 v div ( grad ψ ) ≡ ∇•( ∇ψ ) ≡ ∇2ψ = -------.v dx is an total derivative. 4•126 along an arbitrary path. and ∂ψ ∂ψ u = -----. the scalar function ψ is known as the stream function. 4•125 From the continuity equation Eq. 4•124 and an equation with zero vorticity component perpendicular to the x-y plane ∂u ∂v ----.Chapter 4 Finite Element Method Primer 4.3. Therefore. 4•123 respectively. In 2-D. curl v = 0 at all point of the fluid..= 0 ∂x 2 ∂y 2 Eq.

e. we can define a potential function φ. 4•125 of condition of irrotationality. we have two different integration paths.. 4•124. ∫ u • dx + ∫ u • dx C1 C2 = 0. i. Therefore. 4•130 Therefore. along any two points form a closed circle. and v = – ----∂x ∂y Eq. or ∫ u • dx C1 = – ∫ u • dx C2 Eq. i.. we have another Laplace equation that – ∇2φ = 0 Eq. Therefore. If the integration path coincides with the streamline. 4•132 Again. 4•133 Furthermore. 4•131 φ is known as the velocity potential. or ∇φ ( x ) = – u ( x ) Eq. 4•132 into the continuity equation of Eq. (b) The circulation of a loop is zero for irrotational flow. a potential function φ can be defined which only depends on position. any two paths of integration give the same result. dφ ( x ) = – u • dx. the integration depends only on end-points. and the components of velocity as ∂φ ∂φ u = – ----. from Eq. 4•132. Substituting Eq. we have Workbook of Applications in VectorSpace C++ Library 369 . 4•129 From Figure 4•38b.e. we have the cross derivatives of φ which is identical to assert the exact differential nature of φ. C1 and C2.Two Dimensional Problems y volume flux across path = u dy .v dx.. C2 dx v B ∫ °u • dx C = 0 Eq.v dx C1 u dy A x (a) (b) Figure 4•38 (a) The volume flux across an arbitrary integration path is equal to u dy . substituting Eq. 4•127 and Eq. the volume flux across the integration path should become zero by definition. 4•132 back to Eq.

360-365 in Reddy.-----. and v = – -----∂x ∂y At the right-boundary ΓAE (Figure 4•39b). An example of finite element problem1 (a confined flow around a cylinder is shown in Figure 4•39) in both stream function—ψ formulation and velocity potential—φ formulation are solved using VectorSpace C++ Library and “fe. Inc.N. i < nen. the “contours” of φ and ψ are orthogonal to each others. 370 Workbook of Applications in VectorSpace C++ Library .+ ----.ψ0 = U0 y.0.. 4•127. ∂ψ ∂ψ u = -----.lib” in the followings. “An introduction to the finite element method”. and – ----. 4•128 with the above boundary conditions. J. since ∂φ ∂ψ ∂φ ∂ψ .= -----. the_element_nodal_value).∇φ • ∇ψ = ----. C0 projected_nodal_velocity = SUBVECTOR("int. but the vertical velocity v = 0. C0&". 4•134 This relation ensures that the gradients of stream function and velocity potential are orthogonal to each other. for(int i = 0. v = −∂ψ/∂x = 0. the_element_nodal_value &= C0(nen*velocity_no.. Eq. qp).. Quadrature". y = 2. H0 Velocity = INTEGRABLE_VECTOR("int. 4•136 simplified to ψ(y) = U0 y.. we can integrate ψ as ψ(y) . i++) { 1.. velocity_no. Stream Function—ψ Formulation Recall Eq. Eq. Velocity = 0. 4•135 The gradients are the normals to the equipotential lines of φ and the streamlines of ψ. velocity_no. New York.= 0 ∂x ∂x ∂y ∂y Eq. McGraw-Hill.-----. Notice that the corner E is shared by the boundaries ΓAE and ΓED. Therefore. 1 2 3 4 5 6 7 if(Matrix_Representation::Assembly_Switch == Matrix_Representation::NODAL_FLUX) { int velocity_no = 2. we have ψ(2) = 2U0. Therefore. The Program Listing 4•14 implements the Eq. is unknown.= – -----∂x ∂y ∂x ∂y Eq.e. The only difference to the 2-D heat conduction problem is the post-processing of the derivative information. At the right-boundary ΓDC the horizontal velocity. since u = ∂ψ/d∂. p.. u.Chapter 4 Finite Element Method Primer ∂ψ ∂φ ∂φ ∂ψ – ----. i. On the top-boundary ΓED. The streamline at boundary ΓBC follows from the boundary ΓAB which has ψ =ψ0 (= 0). (double*)0). along the left-boundary ΓAE.. 2nd ed. 4•136 At the bottom-boundary ΓAB we choose the arbitrary reference value of ψ0 = 0.

16 } 17 } else stiff &= (Nx * (~Nx)) | dv. Figure 4•39(a) A confined flow around a circular cylinder.. and (c) velocity potential B.C. 10 } 11 for(int i = 0. Only the upper left quadrant is model due to symmetries of geometry.Two Dimensional Problems 8 Velocity[0] += Nx[i][1]*(ul[i]+gl[i]). 4•127. 15 projected_nodal_velocity(i) = ( ((H0)N[i])*Velocity | dv ) / lumped_mass. (c) velocity potential B. k++) 14 lumped_mass += (((H0)N[i])*((H0)N[k])) | dv. i < nen. k < nen. the velocity is interpolated at the element formulation level as U0 4 8 (a) ψ = y U0 E ψ = 2U0 D ∂ψ/∂x = 0 C A ψ=0 ψ=0 B -∂φ/∂x = U0 A ∂φ/∂y = 0 E ∂φ/∂y = 0 D φ=0 C ∂φ/∂n = 0 B (b) stream function B. boundary conditions. i++) { 12 C0 lumped_mass(0. 9 Velocity[1] += .C.C. (b) stream function B. and PDE. From Eq. Workbook of Applications in VectorSpace C++ Library 371 . 13 for(int k = 0.Nx[i][0]*(ul[i]+gl[i]).C.0).

N[0] = (1-Zai)*(1-Eta)/4. Zai &= Z[0]. for(int i = 52. c = cos(PI/4.u_h() = u. 8). N[1] = (1+Zai)*(1-Eta)/4.0). 5. c1 = cos(PI/8.Chapter 4 Finite Element Method Primer #include "include\fe.Nx[i][0]*(ul[i]+gl[i]).total_node_no(). {3.rhs())) / ((C0)(mr.0}. 8.0-c.0.assembly(FALSE).0-c2.0. i < nen. for(int i = 0. – --------.0. i++) { Velocity[0] += Nx[i][1]*(ul[i]+gl[i]).0*PI/8.0-c.0}. i <= 64.0).0}. gh_on_Gamma_h gh(ndf.0). Omega_h& omega_h) { __initialization(df. coord0[0].0}. qp). cout << "nodal velocity:" << endl. 372 Workbook of Applications in VectorSpace C++ Library . the_element_nodal_value). double coord0[4][2] = {{0. Velocity[1] += . k < nen. 5. (double*)0. Element_Type_Register element_type_register_instance. N[2] = (1+Zai)*(1+Eta)/4. 5.h" EP::element_pattern EP::ep = EP::QUADRILATERALS_4_NODES. C0&". int control_node_flag[5] = {TRUE.ψ a ∂y ∂x h ˆ u e = ( M L ) –1 ∫ ( Nu e ) dΩ Ω T ke = ∫ ( ∇N ⊗ ∇N )det  ------ dξ  ∂ξ Ωe ∂x assembly and matrix solver update free and fixed dof post-processing for nodal velocity Listing 4•14 Stream function formulation potential flow problem(project “fe. 5. 2. 8). U_h uh(ndf. for(int k = 0.141592653509. 2. 8. 8. control_node_flag.u_h() = gd. 0.0-c1. {3. {1. Matrix_Representation mr(gd). i++) { C0 lumped_mass(0. C0 u = ((C0)(mr.C. Global_Discretization& gd) { return new Irrotational_Flow_Q4(en. 8.0}. static Irrotational_Flow_Q4 flowq4_instance(element_type_register_instance). coord2[0]. i < uh.0. {4. Omega_h oh. coord1[5][2] = {{3. Matrix_Representation::Assembly_Switch = Matrix_Representation::NODAL_FLUX.0*U0. c2 = cos(3.gd). coord1[0].gh_on_gamma_h(). Velocity = 0. oh). const double U0 = 1. uh). Global_Discretization& gd) : Element_Formulation(en. J dv(d(X).0.inverse().lhs())). s2}}. gd) { Quadrature qp(2. 0. {3. } gh_on_Gamma_h::gh_on_Gamma_h(int df. {0. } } else stiff &= (Nx * (~Nx)) | dv. the_element_nodal_value &= C0(nen*velocity_no. s}. v_no. 5. control_node_flag.0}. block(this. 0. qp). H1 X = N*xl. for(int i = 0. H0 Velocity = INTEGRABLE_VECTOR("int.0}. int.0). N[3] = (1-Zai)*(1+Eta)/4.0. i++) the_gh_array[node_order(i)](0) = gh_on_Gamma_h::Dirichlet. 5.( 1 + ξ a ξ ) ( 1 + η a η ) 4 ∂N a ∂N a h ˆ ˆ u e = --------.det()).ψ a. s}. oh). for(int i = 0. 8. qp). return 0. Global_Discretization&). η ) = -. i++) { the_gh_array[node_order(i*13)](0) = gh_on_Gamma_h::Dirichlet. gd. coord2[5][2] = {{4. {4. TRUE. 4. s1 = sin(PI/8. const double h_y = 0.0. v_no.ide”. Zai. 2. TRUE. {4. 2.0. 2. int main() { int ndf = 1.5. i++) { the_gh_array[node_order(i)](0) = gh_on_Gamma_h::Dirichlet. 4).0. i <= 4. C0 projected_nodal_velocity = SUBVECTOR("int. i < nen.node_no() << "| " << (mr. 4. N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int. }.0. Eta &= Z[1]. Element_Formulation* Irrotational_Flow_Q4::make(int en. } Irrotational_Flow_Q4::Irrotational_Flow_Q4(int en.0. s = sin(PI/4. gh. TRUE. 2.u_h(). } for(int i = 1. gd. s1}}. } } class Irrotational_Flow_Q4 : public Element_Formulation { public: Irrotational_Flow_Q4(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation *make(int. block(this. 0. Irrotational_Flow_Q4(int.node_array()[i]. } define nodes and elements define B.assembly(). 1. the_gh_array[node_order(i*13)][0] = (((double)i)*h_y)*U0. control_node_flag. {4. mr. mr. s2 = sin(3.0). Eta. omega_h). 2. Global_Discretization&). TRUE}. Omega_h::Omega_h() { const double PI = 3. 4. H0 Nx = d(N) * d(X).0}}. Quadrature".global_nodal_value()[i]) << "}" << endl. } Element_Formulation* Element_Formulation::type_list = 0.0).0}. cout << gd. H1 Z(2. define element formulation 1 N a ( ξ. projected_nodal_velocity(i) = ( ((H0)N[i])*Velocity | dv ) / lumped_mass. {4. 0.0. Quadrature".0*PI/8. 8). i <= 12.0). block(this. 5. } for(int i = 0. k++) lumped_mass += (((H0)N[i])*((H0)N[k])) | dv. 4. if(Matrix_Representation::Assembly_Switch == Matrix_Representation::NODAL_FLUX) { int v_no = 2. Global_Discretization gd(oh.0}. (double*)0). the_gh_array[node_order(i)][0] = 2. project “potential_flow” with macro definition “__TEST_STREAM_FUNCTION” set). i++) cout << "{ " << oh. {1. 5.

The results of this computation with element discretization.U0.0 0. Inspecting Figure 4•40 and Figure 4•41. and nodal velocity vectors shown as arrows.5 0.25 intervals). On the cylinder surface ΓBC.5 1. and v = – ----∂x ∂y Eq.75 1.. ∂φ/∂n = 0. 4•138 where ML is the lumped mass matrix. – --------.0 Figure 4•40 Finite element discretization (open circles are nodes). 4•132 ∂φ ∂φ u = – ----.25 ψ= 1. 4•139 At the left-boundary ΓAE of Figure 4•39c. Recall Eq. Velocity Potential—φ Formulation The velocity potential formulation has the boundary conditions shown in Figure 4•39(c). The code is implemented in the same project file without the macro definition “__TEST_STREAM_FUNCTION” set at compile time.ψ a.0 1. we have ∂φ/∂x = . This is consistent with the orthogonality condition proved Workbook of Applications in VectorSpace C++ Library 373 . At the left-boundary ΓCD a reference value of φ is set to zero.25 0.ψ a ∂y ∂x T Eq. and nodal velocity vectors are shown in Figure 4•41. The results of this computation with element discretization. and nodal velocity vectors are shown in Figure 4•40. 4•137 The least squares nodal projection can be calculated accordingly as h ˆ u e = ( M L ) –1 ∫ ( Nu e ) dΩ Ω Eq. streamlines (ψ = 0-2. 2. velocity equipotential lines. streamlines. we see that the contours lines of the stream function ψ and velocity potential φ is orthogonal to each others at every point.∂φ/∂x. At the top and bottom-boundaries ΓAB and ΓED we have ∂φ/∂y = 0. from u = .75 0.0 at 0.Two Dimensional Problems ∂N a ∂Na h ˆ ˆ u e = --------. where n is its outward normal.

0 1.0 4.Chapter 4 Finite Element Method Primer φ= 5. this is a popular method to generate a finite element mesh automatically.0 at 0.L.5 2.0 1.0 1. Paris.0 3.0 0.5 1.5 4.5 intervals).0 0. P. Masson. “Automatic mesh generation: application to finite element methods”.5 4.0 2. and nodal velocity vectors shown as arrows.1 1.0 5.5 3.. in Eq.0 3. 4•135.5 2. France. 374 Workbook of Applications in VectorSpace C++ Library . Actually. 1991.0 2.99-106 in George.5 3.5 0. John Wiley & Sons.5 0.5 Figure 4•41 Finite element discretization (open circles are nodes). velocity equi-potential lines (φ = 0-5. The contours of stream function ψ and velocity potential φ make a smoothed mesh. p.0 4.

Two Dimensional Problems
4.3.3 Plane Elasticity
We introduce three commonly used formulations for plane elasticity (1) the coordinate-free tensorial formulation, (2) the indicial notation formulation, and (3) the B-matrix (strain-displacement Matrix) formulation. We begin from Cauchy’s equation of equilibrium which is the continuum version of Newton’s second law of motion. In static state, the summation of surface force (= divergence of the stress tensor; i.e., div σ ≡ ∇•σ ) and external force (f) equals zero. We expressed this balance of forces in both the coordinate free tensorial notation and the indicial notation as
div

σ+f

= 0, or σ ij, j + f i = 0

Eq. 4•140

This is subject to displacement boundary conditions and traction boundary conditions
u = g on Γ g, and t =

σ•n

= ti = σ ij n j = h on Γ h

Eq. 4•141

where t is the traction and n is the outward unit surface normal. The weighted-residual statement of the Eq. 4•140 is

∫ w ( div σ + f )

= 0, or

∫ wi ( σij, j + fi )

= 0

Eq. 4•142

Integration by parts and then applying the divergence theorem of Gauss, we have
– ∫ ( grad w ) :

σ dΩ + ∫ w ( σ • n )dΓ + ∫ wfdΩ = 0 , or
Γ Ω

– ∫ w i, j σ ij dΩ + ∫ w i σ ij n j dΓ + ∫ w i f i dΩ = 0
Ω Γ Ω

Eq. 4•143

where the gradient operator, “grad”, and its relation to divergence operator, “div”, are
grad w = ∇w = ∇ ⊗ w = wi, j and div w = ∇ • w = tr ( grad w ) = w i, i ,

Eq. 4•144

respectively. The trace operator, “tr”, is the summation of all diagonal entries. The operator “:”, in Eq. 4•143, is the double contraction. Considering the variation of “w” is chosen to be homogeneous at Γg, the second term of the boundary integral, in Eq. 4•143, can be restricted to Γh as
– ∫ ( grad w ) : σ dΩ +

Γh

∫ whdΓh + ∫ wfdΩ = 0
Ω Ω

, or Eq. 4•145

– ∫ w i, j σ ij dΩ +

Γh

∫ wi hi dΓh + ∫ wi fi dΩ = 0

We first develop in tensorial notation for its clarity in physical meaning. The Cauchy stress tensor, 4•145 can be decomposed as

σ, in Eq.
Eq. 4•146
375

σ

= –p I+τ

Workbook of Applications in VectorSpace C++ Library

Chapter

4 Finite Element Method Primer

where p is the pressure, I is the unit tensor, and τ is the deviatoric stress tensor. For isotropic material, the constitutive equations are
p = – λ div u , and

τ

= 2µ def u

Eq. 4•147

where λ and µ are the Lamé constants. µ is often denoted as G for the shear modulus. The operator def u is defined as the symmetric part of grad u; i.e.,
1 def u ≡ ∇ s u ≡ -- ( grad u + ( grad u ) T ) ≡ ε 2

Eq. 4•148

where the superscript “s” denotes the symmetrical part of grad ( ≡ ∇ ), and ε is the (infiniteismal) strain tensor, and the skew-symmetric part of grad u is defined as
1 rot u ≡ -- ( grad u – ( grad u ) T ) 2

Eq. 4•149

def u and rot u are orthogonal to each other. From Eq. 4•148 and Eq. 4•149, we have the additative decomposition of grad u as
grad u = def u + rot u

Eq. 4•150

Recall the first term in Eq. 4•145, and substituting the constitutive equations Eq. 4•146 and Eq. 4•147

∫ ( grad

w ) : σ dΩ =

∫ ( grad

w ) : ( λ I div u + 2µ def u )dΩ

Eq. 4•151

Note that, grad w : I = tr(grad w) = div w Eq. 4•152

The last identity is from the second part of the Eq. 4•144. With the Eq. 4•150 and the orthogonal relation of def u and rot u, we can verify that grad w : (2µ def u) = (def u + rot u) : (2µ def u) = 2 µ (def u : def u) where the double contraction of def u can be written as def u : def u = tr((def u)Tdef u) With Eq. 4•152 and Eq. 4•153, the Eq. 4•151 becomes Eq. 4•154 Eq. 4•153

∫ ( grad

w) :

σ dΩ

=

∫ [ λ ( div

w • div u ) + 2µ ( def w :( def u )]dΩ

Eq. 4•155

With the element shape function defined, e.g., as Eq. 4•104, the element stiffness matrix is

376

Workbook of Applications in VectorSpace C++ Library

Two Dimensional Problems
I. Coordinate-Free Tensorial Forumlation:
k e=
a a ( φe , b φe )

=

∫ [ λ ( div

N a • div N b ) + 2µ ( def N a : def N b )]dΩ

Eq. 4•156

where indices {a, b} in superscripts and subscripts are the element node numbers. In the indicial notation, we have the infinitesimal strain tensor εij(u) = def u = u(i,j) (with the parenthesis in the subscript denotes the symmetric part), and the generalized Hooke’s law as σij = cijkl εkl cijkl is the elastic coefficients. For isotropic material, it is well-known that cijkl = λ δij δkl +µ (δik δjl+ δil δjk) where δij is the Kronecker delta (δij = 1 if i = j, otherwise δij =0). The equivalence of Eq. 4•155 is Eq. 4•158 Eq. 4•157

∫ wi, j σij dΩ

=

∫ w( i, j )

c ijkl u ( k, l ) dΩ =

∫ wi, j

c ijkl u k, l dΩ

Eq. 4•159

The last identity is due to the minor symmetry of cijkl. The element stiffness matrix for the indicial notation formulation is
pq iajb = k e = ke

∫ Na, k

[ λ ( δ ik δ jl ) + µ ( δ ij δ kl + δ il δ kj ) ] N b, l dΩ

Eq. 4•160

II. Indicial Notation Forumlation:
  iajb = λ N ke ∫ a, i Nb, j dΩ + µ  δij ∫ Na, k Nb, k dΩ + ∫ Na, j Nb, i dΩ  Ω  Ω Ω

Eq. 4•161

where the indices {i, j} are the degree of freedom numbers (0 ≤ i, j < ndf, where ndf is the “number degree of freedoms” which equals to the nsd the “number of spatial dimension” in the present case; i.e., 0 ≤ k < nsd), and the indices {a, b} are element node numbers (0 ≤ a, b < nen, where nen is the “element node number”). The relation of indices {p, q} and {i, a, j, b} are defined as p = ndf (a-1) + i, and q = ndf (b-1)+j Eq. 4•163 In the engineering convention, the strain tensor, ε, and stress tensor, σ, are flatten out as vectors (e.g., in 2-D)
∂ ----- 0 ∂x ∂ u , and = = 0 ----∂y v ∂ ∂ ∂u ∂v ----- ----- ----- + ----∂y ∂x ∂y ∂x ∂u ----∂x ∂v ----∂y

ε

εx = εy γ xy

σ

σx = σy τ xy

Eq. 4•164

Workbook of Applications in VectorSpace C++ Library

377

according to 2λµ λ = --------------λ + 2µ Eq.... 4•172 III. (ndf .1) h ˆ ai u e ( ξ. the Young’s modulus. η ) ≡ N a ( ξ. and B = B 0 B 1 B2 … B n – 1 ∂y ∂N a ∂N a --------. 4•168 rewritten Eq. D can be defined by replacing λ by λ. 4•105 for a = 0. and Poisson’s ratio. 4•171 The element stiffness matrix of the B-matrix (strain-displacement matrix) formulation is ke = Ω ∫ ε ( δu ) T σ ( u )dΩ = Ω ∫ ε ( δu )T D ε ( u )dΩ T = e iT ∫ Ba D B b dΩe j Ω Eq. . They can be related as νE E λ = ------------------------------------... 1.1). η )u e e i.--------∂y ∂x Eq. and µ = ------------------( 1 + ν ) ( 1 – 2ν ) 2(1 + ν) Eq.. . We can write h ˆ ai ε( ue ) = Ba u e ei Eq. B-matrix Formulation: pq iajb = ke = ke Ω ∫ ε ( δu )T D ε ( u )dΩ Eq. ν.Chapter 4 Finite Element Method Primer σ=Dε The constitutive equation is Eq. 4•166 in plane stress case.. ( no sum on i ) Eq. 4•173 378 Workbook of Applications in VectorSpace C++ Library . 4•165 In plane strain case. 4•169 where ei is the Euclidean basis vector. (nen . 4•170 ∂N a --------∂x Ba = 0 0 ∂N a --------. we can show that the fourth-order tensor D becomes a matrix as λ + 2µ λ 0 λ λ + 2µ 0 0 0 µ D = Eq. are often given instead of the Lamé constants. 4•167 In engineering applications. and i = 0.. E.

the B-matrix formulation is the carnonical formula provided. and “10”.10 = -50 fy. because the element boundary is linear.9 = 0. and “10”. 4•175 With the given parameters. Therefore.lib”. τy = 150 psi 2 in.4 = 0 Figure 4•42 Discretization of eight 4-nodes quadrilateral elements or two 9-nodes Lagrangian elements for a cantilever beam.9 = 0. p. 3•1 of Chapter 3 on page 166).5.9 = 0 ux. and -75 psi to nodes “0”.0 = -50 0 11 4 0 6 1 11 6 1 0 5 1 7 2 12 7 2 12 6 2 8 3 13 8 1 3 13 7 3 9 4 14 9 4 14 ux. Similarly. 1/3} to compute the three nodes on the boundary. q} and {i. Inc. 10 fy. and for the element “4”.4 = 0 ux. To element “0”.N. ν = 0. McGraw-Hill. “5”. 2nd ed. J. 4•173.14 = 0 ux. -200.. and “5”. and -50 psi on nodes “0”. “ An introduction to the finite element method”. “5”. respectively.10 = -75 fy.. the weighting for the end-points of a line segment is {0. 10 in. this value is “-0. we also add -75 psi to nodes “5”. In the trapezoidal rule (Eq.1 + ------------------3EI L2 Eq.5 = -200 5 fy. the relation of indices {p. for a 9-nodes Lagrangian element. respectively. 0.14 = 0 ux. 4•163. This yields -50. 4/3. New York. we use Simpson’s rule with weightings of {1/3.25 subject to a uniformly distributed shear stress τ = 150 psi at the end (see Figure 4•42). and “10”. 1. -150. Consider an example of a cantilever beam1 with Young’s modulus E = 30 × x106 psi. The analytical solution on the tip deflection is 3(1 + ν) PL 3 v = – --------. For boundaries of a 4-node quadrilateral element. Workbook of Applications in VectorSpace C++ Library 379 . we discuss the implementation of the three formulations in reverse order. Adding the nodal loading on the two element together.5}. b} are defined in Eq. the boundary is quadratic.9 = 0 ux. this yields nodal load specification of -75. a. In most finite element text.Two Dimensional Problems In Eq. j.0 = -75 0 10 fy.5 = -150 5 fy. and uy.51875”. we use trapezoidal rule to compute the nodal load. we add -75 psi to nodes “0”. The shear stress at the end is τy = -150 psi. and uy. 1993. 473 in Reddy. We proceed to implement this problem in C++ with the aid of VectorSpace C++ Library and “fe.

x B-matrix lay-out aliase submatrices Figure 4•43 Construction of B-matrix using one-by-one concatenation operation.0)) & (C0(0. H0&".x N3.x 0 0 N3. Eta.y N3.y N2. 1x2 // dim B = {3x8}. int. 380 Workbook of Applications in VectorSpace C++ Library . The “Nx” is then partitioned into submatrix “w_x”. wy. stiff &= ((~B) * (D * B)) | dv. {a_*v_. nsd. // {8x3}*{3x3}*{3x8}={8x8} } Line 17 is the computation of the derivatives of the shape function “Nx” (see Figure 4•43).y spatial dim. (double*)0. 4). 3.y N1. qp).x N3. ElasticQ4::ElasticQ4(int en. 2/*nsd*/. 4•173. H1 X = N*xl.inverse(). qp).Chapter 4 Finite Element Method Primer Implementations for B-Matrix Formulation: The Program Listing 4•15 implements Eq. wx.x N2. H0 w_x = INTEGRABLE_SUBMATRIX("int.x N2. J dv(d(X).y N2.0. C0 D = MATRIX("int. N0.y wy &= wx &= w_x[0][0] w_x[0][1] N0. int. wx &= w_x[0][0]. Quadrature".y B &= (~wx || C0(0. Eta &= Z[1]. N0.x N0. The Element_Formulation of “ElasticQ4” is 1 2 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 static const double a_ = E_ / (1-pow(v_. a_*(1-v_)/2. 3. where dim wx[i] = {1x4} B &= (~wx || C0(0. 0.0} }. N[2] = (1+Zai)*(1+Eta)/4.x N1.y N2.y N0. const double*". {0. B.2)). 0.x 0 0 N0. Zai.0)) & (C0(0.0 }. a_*v_.y N1. N[3] = (1-Zai)*(1+Eta)/4. N[1] = (1+Zai)*(1-Eta)/4. The regular increment submatrices wx &= w_x[0][0] and wy &= w_x[0][1] are w_x node number N0.x N3. 4/*nen*/.y N1.y N2. Zai &= Z[0]. wy &= w_x[0][1].y N1.0) || ~wy ) & (~wy || ~wx ). Global_Discretization& gd) : Element_Formulation(en.x 0 0 N1. // Natrual Coordinates N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int. H0 Nx = d(N) * d(X). N[0] = (1-Zai)*(1-Eta)/4. Nx).x N2. int.0 }. // plane stress D matrix static const double Dv[3][3] = {{a_.x N1. // 2-dimension.0) || ~wy ) & (~wy || ~wx ).0. 2x2 integration points H1 Z(2.det()). a_.x 0 N3. Dv[0]). gd) { Quadrature qp(2.x N1.y N3.x N0.y 0 N3. 1. // aliase submatrices. 0.y N2.

Two Dimensional Problems
#include "include\fe.h" static const double L_ = 10.0; static const double c_ = 1.0; static const double h_e_ = L_/2.0; static const double E_ = 30.0e6; static const double v_ = 0.25; static const double lambda_ = v_*E_/((1+v_)*(1-2*v_)); static const double mu_ = E_/(2*(1+v_)); static const double lambda_bar = 2*lambda_*mu_/(lambda_+2*mu_); EP::element_pattern EP::ep = EP::QUADRILATERALS_4_NODES; Omega_h::Omega_h() { double x[4][2] = {{0.0, 0.0}, {10.0, 0.0}, {10.0, 2.0}, {0.0, 2.0}}; int flag[4] = {1, 1, 1, 1}; block(this, 3, 5, 4, flag, x[0]); } gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) { __initialization(df, omega_h); the_gh_array[node_order(4)](0) = the_gh_array[node_order(9)](0) = the_gh_array[node_order(9)](0)=the_gh_array[node_order(14)](0)=gh_on_Gamma_h::Dirichlet; the_gh_array[node_order(0)](1) = the_gh_array[node_order(5)](1) = the_gh_array[node_order(10)](1) = gh_on_Gamma_h::Neumann; the_gh_array[node_order(0)][1] = the_gh_array[node_order(10)][1] = -75.0; the_gh_array[node_order(5)][1] = -150.0; } class ElasticQ4 : public Element_Formulation { public: ElasticQ4(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation *make(int, Global_Discretization&); ElasticQ4(int, Global_Discretization&); }; Element_Formulation* ElasticQ4::make(int en, Global_Discretization& gd) { return new ElasticQ4(en,gd); } static const double a_ = E_ / (1-pow(v_,2)); static const double Dv[3][3] = {{a_, a_*v_, 0.0}, {a_*v_, a_, 0.0 }, {0.0, 0.0, a_*(1-v_)/2.0} }; C0 D = MATRIX("int, int, const double*", 3, 3, Dv[0]); ElasticQ4::ElasticQ4(int en, Global_Discretization& gd) : Element_Formulation(en, gd) { Quadrature qp(2, 4); H1 Z(2, (double*)0, qp), Zai, Eta, N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 4, 2, qp); Zai &= Z[0]; Eta &= Z[1]; N[0] = (1-Zai)*(1-Eta)/4; N[1] = (1+Zai)*(1-Eta)/4; N[2] = (1+Zai)*(1+Eta)/4; N[3] = (1-Zai)*(1+Eta)/4; H1 X = N*xl; H0 w_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, nsd, Nx), wx, wy, B; wx &= w_x[0][0]; wy &= w_x[0][1]; B &= (~wx || C0(0.0)) & (C0(0.0) || ~wy ) & (~wy || ~wx ); stiff &= ((~B) * (D * B)) | dv; } Element_Formulation* Element_Formulation::type_list = 0; Element_Type_Register element_type_register_instance; static ElasticQ4 elasticq4_instance(element_type_register_instance); int main() { int ndf = 2; Omega_h oh; gh_on_Gamma_h gh(ndf, oh); U_h uh(ndf, oh); Global_Discretization gd(oh, gh, uh); Matrix_Representation mr(gd); mr.assembly(); C0 u = ((C0)(mr.rhs())) / ((C0)(mr.lhs())); gd.u_h() = u; gd.u_h() = gd.gh_on_gamma_h(); cout << gd.u_h(); return 0; }

Young’s modulus and Poisson ratio plane stress λ modification

generate nodes and elements B.C. u4 = u9 = v9 = u14 = 0 τy0 = τy10 = -75, τy5 = -150

1 N a ( ξ, η ) = -- ( 1 + ξ a ξ ) ( 1 + η a η ) 4 ∂N ∂x –1 ∇N =  ------   -----   ∂ξ   ∂ξ ∂Na --------∂x Ba = 0

0

∂N a --------∂y ∂Na ∂N a --------- --------∂y ∂x

T k e = e iT ∫ B a D Bb dΩe j Ω

Listing 4•15 Plane elastiticity (project workspace file “fe.dsw”, project “2d_beam” with Macro definition “__TEST_B_MATRIX_CONCATENATE_EXPRESSION_SUBMATRIX” set at compile time). Workbook of Applications in VectorSpace C++ Library
381

Chapter

4 Finite Element Method Primer

also shown. The B-matrix, according to Eq. 4•171, is defined with one-by-one column-wise concatenation operation “H0::operator || (const H0&)”. When the argument of the concatenation operation is of type C0, it will be promote to H0 type object before concatenation occurred. Line 25 is the element stiffness matrix definition of the Eq. 4•173. A complete parallel algorithm, without the use of the one-by-one concatenation operation, results in C++ statements closer to linear algebraic expression with basis 1 2 3 4 5 6 7 8 9 10 H0 w_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, nsd, Nx), wx, wy; wx &= w_x[0][0]; wy &= w_x[0][1]; H0 zero = ~wx; zero = 0.0; C0 e3(3), e(ndf), E(nen), U = (e3%e)*(~E); H0 B =+((~wx) * U[0][0] + zero * U[0][1] + zero * U[1][0] + (~wy) * U[1][1] + (~wy) * U[2][0] + (~wx) * U[2][1]) ; stiff &= ((~B) * (D * B)) | dv;

Line 4 takes the size and type of the transpose of “wx”, then re-assigns its values to zero. Line 7 uses unary positive operator “+” to convert a Integrable_Nominal_Submatrix (of object type H0) into a plain Integrable_Matrix (also of object type H0). We note that the expression “U[2][1]” can be written as “(e3[2] % e[1]) * (~E)” without having to define the additional symbol “U = (e3%e)*(~E)”. One needs to set both macro definitions of “__TEST_B_MATRIX_CONCATENATE_EXPRESSION_SUBMATRIX” and “__TEST_BASIS” for this implementation at compile time The semantics in the construction of B-matrix in the above is a bottom-up process. We first define the components of the B-matrix than built the B-matrix with these pre-constructed components. The semantics of the program code can be constructed in a reversed order; i.e., top-down process. We may want to construct the Bmatrix first, giving its size and initialized with default values (“0.0”). Then, we can assign each components of the B-matrix with its intended values. 1 2 3 4 5 6 7 8 9 H0 w_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, nsd, Nx), B = INTEGRABLE_MATRIX("int, int, Quadrature", 3, nsd*nen, qp), epsilon = INTEGRABLE_SUBMATRIX("int, int, H0&", 3, nsd, B), wx, wy; // aliases of w_x components wx &= w_x[0][0]; wy &= w_x[0][1]; epsilon[0][0] = ~wx; epsilon[0][1] = 0.0; // εx = ∂u/∂x epsilon[1][0] = 0.0; epsilon[1][1] = ~wy; // εy = ∂v/∂y epsilon[2][0] = ~wy; epsilon[2][1] = ~wx; // γxy = ∂u/∂y + ∂v/∂x stiff &= ((~B) * (D * B)) | dv;

The B-matrix is constructed first, then, its components {εx, εy, γxy}T are assign according to the definition in the first part of Eq. 4•164 and Eq. 4•170, where the strain “epsilon” is a submatrix referring to “B” matrix. For this implementation the same project “2d_beam” in project workspace file “fe.dsw” can be used with only the macro definition “__TEST_B_MATRIX_CONCATENATE_EXPRESSION_TOP_DOWN” set.
382

Workbook of Applications in VectorSpace C++ Library

Two Dimensional Problems
The above two methods of programming depend heavily on the submatrix facility in the VectorSpace C++ Library. This dependency can be removed, if we flatten the submatrix into plain matrix with concatenation operations as (by setting only macro definition “__TEST_B_MATRIX_CONCATENATE_EXPRESSION” in the same project) 1 2 3 4 5 6 H0 Nx0, Nx1, Nx2, Nx3; Nx0 &= Nx[0]; Nx1 &= Nx[1]; H0 B = (Nx0[0] | C0(0.0) | Nx1[0] (C0(0.0) | Nx0[1] | C0(0.0) (Nx0[1] | Nx0[0] | Nx1[1] stiff &= ((~B) * (D * B)) | dv; // aliases Nx3 &= Nx[3]; | C0(0.0) | Nx3[0] | C0(0.0) ) & | Nx2[1] | C0(0.0) | Nx3[1] ) & | Nx2[0] | Nx3[1] | Nx3[0] );

Nx2 &= Nx[2]; | C0(0.0) | Nx2[0] | Nx1[1] | C0(0.0) | Nx1[0] | Nx2[1]

We see that this implementation takes direct image of the right-hand-side block in the Figure 4•43. In the above code, no submatrix facility is used only the concatenate operator “|” is used to built the B-matrix from ground-up. Comparing the bottom-up with the top-down algorithms, the only difference is the semantics. In the last algorithm, we have flatten out the submatrix into simple matrix. In doing so, we can avoid using the requirement of submatrix features supported by the VectorSpace C++ Library. We may want to optimize the rapid-proto-typing code by eliminating the features supported in VectorSpace C++ Library step-by-step, such that the overhead caused by the use of VectorSpace C++ Library can be alleviated. A even more Fortran-like equivalent implementation is as the followings1 (set the macro definition to nothing) 1 2 3 4 5 6 7 8 9 9 10 11 12 13 14 15 16 17 18 H0 k(8, 8, (double*)0, qp), DB(3, nsd, (double*)0, qp), B1, B2; for(int b = 0; b < nen; b++) { B1 &= Nx[b][0]; B2 &= Nx[b][1]; DB[0][0] = Dv[0][0]*B1; DB[0][1] = Dv[0][1]*B2; DB[1][0] = Dv[0][1]*B1; DB[1][1] = Dv[1][1]*B2; DB[2][0] = Dv[2][2]*B2; DB[2][1] = Dv[2][2]*B1; for(int a = 0; a <= b; a++) { B1 &= Nx[a][0]; B2 &= Nx[a][1]; k[2*a ][2*b ] = B1*DB[0][0] + B2*DB[2][0]; k[2*a ][2*b+1] = B1*DB[0][1] + B2*DB[2][1]; k[2*a+1 ][2*b ] = B2*DB[1][0] + B1*DB[2][0]; k[2*a+1 ][2*b+1] = B2*DB[1][1] + B1*DB[2][1]; } } for(int b = 0; b < nen; b++) for(int a = b+1; a < nen; a++) { k[2*a ][2*b ] = k[2*b ][2*a ]; k[2*a ][2*b+1 ] = k[2*b+1 ][2*a ];

// D*B takes care of zeros

// BT* DB takes care of zeros

// determined by minor symmetry

1. p. 153 in Thomas J.R. Hughes, 1987, “ The finite element method: Linear and dynamic finite element analysis.”, PrenticeHall, Englewood Cliffs, New Jersey.

Workbook of Applications in VectorSpace C++ Library

383

The last step is to have an numerical integration at the most outer loop where we evaluate all values at Gaussian quadrature points and multiply these values with their corresponding weights. k dΩ + ∫ Na. 21 } 22 stiff &= k | dv. i ) ) µ ∂N a ∂N b ∂N a ∂N b  --------. j dΩ + µ  δij ∫ Na.---------  ∂y ∂x  ∂N a ∂N b ∂N a ∂N b  --------.---------  ∂y ∂x  = ∂N a ∂Nb ∂N a ∂N b  --------.---------  --------.---------  ∂x ∂x  ∂N a ∂N b  --------.--------- +  --------. Certainly the optimized low-level code is much longer. The lower triangular part matrix is then determined by symmetry with keab = (keba)T (lines 15-21). By this way. i dΩ  Ω  Ω Ω The integrand of the nodal submatrices kab (ndf × ndf submatrices) has the first term(the volumetric part) as ∂N a ∂N b ∂N a ∂N b  --------. 4•176 and Eq.Chapter 4 Finite Element Method Primer 19 k[2*a+1 ][2*b ] = k[2*b ][2*a+1 ]. 20 k[2*a+1 ][2*b+1 ] = k[2*b+1 ][2*a+1 ]. this last version can be easily optimized even more aggressively in plain C language without using the VectorSpace C++ Library at all. In lines 2-14.---------  ∂x ∂x   ∂y ∂y  ∂N a ∂Nb  --------.---------  ∂x ∂x   ∂y ∂x  N b. 4•161   iajb = λ N ke ∫ a. k Nb. i Nb. 4•176 Note that λ may replace λ for the plane stress case in Eq. j ) = λ ∂N a ∂N b ∂N a ∂N b  --------.--------- +  --------. 4•161 is its deviatoric part µ ( δ ij ( Na.---------  ∂x ∂y   ∂y ∂y  λ ( N a. at compile time. j N b. and harder to maintain for programmers. The rest of the integrands of Eq. we may avoid using the submatrix facility in VectorSpace C++ Library entirely. Only the “nodal submatrices”—keab in the diagonal and upper triangular matrix of ke is computed. j Nb. 4•167. less readable. k ) + ( Na. i Eq.---------  --------.---------  ∂x ∂y  0 ∂N a ∂N b ∂N a ∂N b  --------. 4•177 are implemented as (by setting. the macro definition of “__TEST_INDICIAL_NOTATION_FORMULATION” ) 384 Workbook of Applications in VectorSpace C++ Library . provision is taken to eliminate the multiplication with “0” components in BTDB. Nonetheless.---------  ∂x ∂y   ∂y ∂y  = µ Eq. k N b.---------  ∂x ∂x   ∂y ∂y  0 ∂N a ∂N b ∂N a ∂N b 2  --------.---------  --------. Implementations for Indicial Notation Formulation: Recall Eq.--------- + 2  --------. We recognize that this is the idiom of using the low-level language expression with indices in accessing the submatrices of the matrix ke as “k[ndf a + i][ndf b + j]”.---------  ∂x ∂x   ∂y ∂y  +µ ∂N a ∂Nb  --------.--------- +  --------.---------  ∂x ∂x   ∂y ∂y  ∂N a ∂Nb  --------. 4•177 Eq.

The same implementation with one-by-one concatenation operations “||” and “&&” will be ) && 1 C0 stiff_dev = mu_ *( ( ((2*Wx*~Wx+Wy*~Wy) || (Wy*~Wx) 2 ((Wx*~Wy) || (2*Wy*~Wy+Wx*~Wx)) 3 ) | dv). 1. qp). stiff &= stiff_vol + stiff_dev. nsd.--------- +  --------. vol_sub[1][0] = wy*~wx. Line 4-8 implements the integrand of the volumetric element stiffness by Eq. dev_sub[0][1] = wy*~wx. int. C0 stiff_dev = mu_* ( // ∂N a ∂N b ∂N a ∂N b ∂Na ∂N b  --------. E(nen).--------- +( (2*wx*~wx+wy*~wy)*((e[0]%e[0])*(E%E))+// 2  --------. C0 stiff_dev = mu_ * (dev | dv). 4 C0 stiff_vol = lambda_bar *( ( ((Wx*~Wx) || (Wx*~Wy)) && 5 ((Wy*~Wx) || (Wy*~Wy)) 6 ) | dv). dev_sub[1][1] = 2*wy*~wy+wx*~wx. wx. qp). H0 w_x = INTEGRABLE_SUBMATRIX("int. dev_sub[1][0] = wx*~wy. nx). H0&". nsd. 4•176 and line 11-18 implements the integrand of the deviatoric element stiffness by Eq. U = (e%e)*(E%E). 7 stiff &= stiff_vol + stiff_dev.--------- + 2  --------. dev). int. wx &= w_x[0][0]. nsd*nen.--------- --------. H0&". H0&". Quadrature". Quadrature".---------  ∂x ∂x   ∂y ∂x  ( // λ ∂N a ∂N b +( wx*~wx*U[0][0]+wx*~wy*U[0][1]+ //  ∂N a ∂N b  --------.---------  ∂x ∂y   ∂x ∂x   ∂y ∂y  (wx*~wx+2*wy*~wy)*((e[1]%e[1])*(E%E))// ) | dv). Note that the unary positive operator in front of both line 6 and line 13 are conversion operation to convert an Integrable_Nominal_Submatrix (of object type H0) into an Integrable_Matrix (of type H0).--------wy*~wx*U[1][0]+wy*~wy*U[1][1] ) //  ∂x ∂y   ∂y ∂y  | d_v). nsd.---------  --------. nsd. vol). C0 stiff_vol = lambda_bar * (vol | dv).Two Dimensional Problems 1 2 3 4 5 6 7 8 11 12 13 14 15 16 17 18 19 C0 e(ndf). nsd*nen. H0 dev = INTEGRABLE_MATRIX("int. nsd*nen. stiff &= stiff_vol + stiff_dev. dev_sub = INTEGRABLE_SUBMATRIX("int. // ∂N a ∂N b ∂N a ∂N b C0 stiff_vol = lambda_bar* //  --------. vol_sub[1][1] = wy*~wy. Workbook of Applications in VectorSpace C++ Library 385 . int.---------  ∂x ∂x   ∂y ∂y   ∂y ∂x  (wy*~wx) *((e[0]%e[1])*(E%E))+ // µ ∂Na ∂N b ∂N a ∂N b ∂N a ∂N b (wx*~wy) *((e[1]%e[0])*(E%E))+ //  --------. int. int. nsd*nen. vol_sub[0][1] = wx*~wy. wy. dev_sub[0][0] = 2*wx*~wx+wy*~wy. vol_sub[0][0] = wx*~wx. 4•177. An Integrable_Submatrix version of this implementation will be 1 2 3 4 5 6 7 8 9 10 11 H0 vol = INTEGRABLE_MATRIX("int. vol_sub = INTEGRABLE_SUBMATRIX("int. nsd. wy &= w_x[0][1].---------  --------.

// Ω K[j+a*ndf][i+b*ndf] = c3*Kji + c2*Kij.Chapter 4 Finite Element Method Primer The flatten-out coding using concatenation operations “|” and “&” is not recommended. stiff &= K. // λ replaces λ for plane stress H0 k(nen*ndf. j < i. New Jersey. j < ndf. 1987. a <= b. An aggressively optimized counterpart using less VectorSpace C++ library features is shown in the followings1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 double c1 = lambda_bar + mu_.--------- + µ  --------. 386 Workbook of Applications in VectorSpace C++ Library . i++) // for(int j = 0.---------  --------. j < ndf.--------- + µ  --------.0. j++) // k[i][j] = k[j][i]. b < nen. i++) { if(i == j) // diagonal components of nodal submatrices—keiaib K[i+a*ndf][i+b*ndf] = c1*K[i+a*ndf][i+b*ndf]+ c2*temp. i < nen*ndf. //  ∂x ∂y   ∂y ∂y  for(int i = 1. 155 in Thomas J. a++) //  --------. i < nen*ndf.---------  dΩ   ∂x ∂y   ∂y ∂x   K[i+a*ndf][j+b*ndf] = c3*Kij + c2*Kji. qp). 1. a <= b. j++) // diagonal nodal submatrices only ∂Na ∂N b ∂N a ∂Nb for(int a = 0. b++) //only the upper triangular of nodal submatrices—keab for(int a = 0.---------  --------. Englewood Cliffs. // rices—keiaja(those in upper triangular of ke. i++) // get lower triangular part of ke by symmetry for(int j = 0.--------- k[i+a*ndf][j+b*ndf] = Nx[a][i]*Nx[b][j]. PrenticeHall. p. k++) temp += K[k+a*ndf][k+b*ndf]. “ The finite element method: Linear and dynamic finite element analysis. // of off-diagonal nodal submatrices—keiajb (a ≠ b) ∂N a ∂Nb ∂N a ∂N b Kji = K[j+a*ndf][i+b*ndf]. i<j) else { // off-diagonals components double Kij = K[i+a*ndf][j+b*ndf]. Hughes.R. b < nen. for(int k = 0. c2 = mu_. k < ndf.”. b++) // upper triangular nodal submatrices + for(int j = 0. // get lower triangualr part by symmetry C0 K = k | dv. a++) { C0 temp = 0. i++) //  ∂x ∂x   ∂y ∂x  dΩ if( (a != b) || (a == b && i <= j) ) // ke(temp) = ∫ ∂N ∂N ∂N a ∂Nb a b Ω  --------. j++) // K[i][j] = K[j][i].--------- for(int i = 0. since the 8 ×8 stiffness matrix is just too much for either write it all out. c3 = lambda_bar.// ∂N a ∂N b ∂N a ∂Nb } // ∫  λ  --------. j++) for(int i = 0. nen*ndf.---------  dΩ   ∂y ∂x   ∂x ∂y   Ω } } for(int i = 1. i <= j. j < i. or be read easily. else if(a == b) // off-diagonal components of diagonal nodal submatK[i+a*ndf][j+a*ndf] *= c1. i < ndf. (double*)0. // ke for(int b = 0. for(int j = 0. // ∫  λ  --------. for(int b = 0.

--------- + µ  --------. to reduce the number of calculation. 4•180 where the null symbol “ ∅ ” denotes the corresponding components in the matrix are not calculated. we have Na.y = Na.---------  ∂x ∂x   ∂y ∂x  dΩ ∂N a ∂N b ∂N a ∂N b  --------. the symmetry consideration is taken. and only the components of the diagonal nodal submatrix and upper-triangular nodal submatrices belonging to the upper triangular part of ke are calculated.--------- +  --------.--------- + µ  --------. 4•182 are not calculated.---------  --------. Lines 13-34 will have no integrable objects involved.y Na.---------  ∂x ∂x   ∂y ∂y  ∅ ∅ ∂N a ∂N b ∂N a ∂N b ( λ + 2µ )  --------.---------  ∂x ∂y   ∂y ∂y  keab (temporary) = Ω ∫ Eq. Lines 22-24. line 17 calculates the following quantity and store in the variable “temp” Ω ∫ ∂Na ∂N b ∂Na ∂N b  --------. 4•181 is reduced to ∂N a ∂Na ( λ + µ ) ∫ ∅ --------. 4•181 Special care is taken in lines 22-23.--------- + µ  --------.--------- dΩ  ∂x ∂x   ∂y ∂y  Eq. ke is overwritten by the rest of the codes. when the nodal submatrices are diagonal nodal submatrices. and 25-30 get the off-diagonal components of nodal submatrices ∅ ∂N a ∂N b ∂N a ∂Nb λ  --------.---------  --------. because these compoWorkbook of Applications in VectorSpace C++ Library 387 . Notice that components in lower-left corner of Eq. 4•179 Lines 20-21 gets diagonal components of the nodal submatrices according to ∂N a ∂N b ∂N a ∂N b ( λ + 2µ )  --------. That is the off-diagonal components in the diagonals nodal submatrices in Eq.--------- + µ  --------. Firstly. Data of the derivatives of shape function are stored in matrix ke temporarily as ∂N a ∂N b ∂N a ∂N b  --------.x.---------  ∂y ∂x   ∂x ∂y  ∂Na ∂N b ∂N a ∂N b λ  --------.--------. 4•178 Then. In the case the node number index is “a”.Two Dimensional Problems All integration operations are done with between lines 3-12. 4•182 For these diagonal nodal submatrices the off-diagonal components calculation is therefore further simplified to lines 22-23.---------  ∂y ∂y   ∂x ∂x  dΩ Ω ∫ Eq. In both of these two parts.dΩ ∂x ∂y Ω ∅ ∅ Eq.x Na.---------  ∂x ∂y   ∂y ∂x  ∅ Ω ∫ dΩ Eq.

VectorSpace C++ Library together with object-oriented features in C++ language may serve as the rapid proto-typing tools. b} are the element node numbers. Implementation for Coordinate-Free Tensorial Formulation: Now we turn away from the goal of optimization for efficiency completely to the goal of obtaining a most physically and mathematically comprehensive implementation. 4•183 The inner product gives a scalar. not intended for code reuse.e. of size (ndf × nen) × (ndf × nen) = 8 × 8. Vh(Ωe). v3}T. Vh(Ωe). ke . the off-diagonal components in off-diagonal nodal submatrices. The implementation for the coordinate free tensorial formulation will be based on Eq. and they can be obtained by symmetry as in lines 32-34. where V ≡ { v ∈ H 1 } 388 Workbook of Applications in VectorSpace C++ Library .g. 4•181. Lots of programming merits are all compromised in the name of efficiency. neither does it has the knowledge of “div”. We also observed that the differential operators “div”. u3. i. in VectorSpace C++ Library is the type H1 which is an integrable type differentiable up to the first order. it is most likely to have a formula available that is derived from physical principles such as the development of elasticity in the beginning of this section.. v2. 4•155 for V ≡ { v ∈ H 1 }. The finite element formula may not be available. The variable vector u has the size of (ndf × nen) = 2 × 4 =8. However. the numerical results of the high-level prototype code can be used to debug the optimized code which is often quite un-readable and error-prone. we have the inner product defined by a symmetrical bilinear form a ( v. First we recall Eq. If it turns out further optimization is necessary for either saving computation time or memory space. all need to be defined. u1. and superscripts and subscripts {a. 4•156 which is k e = a ( N a. v0. we identify that the finite element space— Vh(Ωe) has its inner product operation producing an element stiffness matrix. For research scientists and engineers. “def” or “:” operators. H1 is certainly not a finite element space. The element variables. v1.Chapter 4 Finite Element Method Primer nents belong to the lower triangular part of ke. N b ) = Ω ∫ [ λ ( div N a • div Nb ) + 2µ ( def N a : def Nb )]dΩ Eq. class “H1_h” in ad hoc manner for the finite element space—Vh(Ωe) as 1 2 class H0_h. in 2-D elasticity for bilinear 4-nodes element. the code is quite abstruse without study its comments and explanations carefully. class H1_h { // forward declaration // finite element space—Vh(Ωe).. are arranged in the order of u = {u0. they all lie in the upper triangular part of ke. u2. A high-level code can be quickly implemented with VectorSpace C++ Library because it provides capability of making computer code very close to its mathematical counterparts. The inner product of objects defined by H1 will not generate a (ndf × nen) × (ndf × nen) element stiffness matrix. and the double contraction “:” on the finite element space. “def”. However. The closest thing to the finite element space. e. v ) = Ω ∫ [ λdiv v • div v + 2µ ( def v ): def v)]dΩ Eq. Therefore. Lines 2429 take care of the rest by Eq. This implementation is probably the most efficient of all. We may implement a customized. 4•184 where N a ∈ V h .

// double contraction “:” }. grad. } The differential operators “div” and “def” are applied to the finite element space—Vh(Ωe) which can be implemented as an abstract data type “H1_h”. H1&). } H0_h grad_t(H1_h& n) { return n. These two objects have been defined earlier in the element formulation. H0_h grad_(). } H0_h grad(H1_h& n) { return n. We can view class “H0_h” as an extension of class H0 to define the double contraction operation “:”. we design to have H1_h used in the element formulation as close to the mathematical expression as possible. and the second argument is the physical coordinates— “X”. The derivatives of the shape function can be computed from these two objects as H0 Nx = d(N) * d(X). x = X. } H0_h def(H1_h& n) { return n. }. Moreover. H0_h div(H1_h& n) { return n. def public: H0_h(const H0& a) : H0(a) {} H0 operator ^(const H0_h&). // stiff &= K_vol + K_dev.grad_().div_(). class H0_h inherits all the public interfaces and implementations of class H0. H0_h grad_t_(). H1& X) { n = N.inverse(). // K_dev = (2*mu_) * ( (def(N_) ^ def(N_)) | dv). } H1_h::H1_h(H1& N. C0 K_vol = lambda_bar*(((~div(N_))*div(N_)) | dv). 4•144 Workbook of Applications in VectorSpace C++ Library 389 . H0_h def_(). The double contraction operator is defined as a public member binary operator “H0_h::operator ^ (const H0_h&)” (line 14). In the terminology of object-oriented analysis. H0_h “IS-A” H0 type. such that. Ω ∫ 2µ ( def Ω ∫ λ ( div N a • div N b )dΩ N a : def Nb )dΩ which is almost an exact translation of high-flown mathematical expression of Eq. 4•184.grad_t_(). The return values of these differential operators are of yet another abstract data type “H0_h”. public: H1_h(H1&. Lines 16-20 are auxiliary free functions defined to provide better expressiveness. class H0_h : public H0 { // return type for the differential operators div. x. we may write in element formulation as simple as 1 2 3 4 H1_h N_(N. Now we get to the definition of the divergence operator “div” according to Eq. The constructor of class H1_h take two arguments of type H1. We emphasize that with the public derived relationship. X).Two Dimensional Problems 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 H1 n. The “IS-A” relationship between H0_h and H0 is manifested by the definition of class H0_h as publicly derived from class H0 (line 11).def_(). The first argument is the shape functions—“N”. H0_h div_().

The gradient operator “grad” is defined (also in Eq.d(). 4•186 of size 1 × 8. wy. This special ordering makes the gradient tensor in Eq. 2. 4•185 Or in the form of the nodal subvector (row-wise) for the finite element space—Vh(Ωe) as ∂N a ∂N a --------. H0 wx = (+w_x[0][0]). H0&". 4•188 Eq. 4•186 return ~(+ret_val). H0 w_x = INTEGRABLE_SUBMATRIX("int. “v” in row-wise order to be compatible with the order of the variable vector in element formulation. 4•144) ∂u ----∂x = ∂u ----∂y ∂v ----∂x ∂v ----∂y grad u = ∇ ⊗ u = u i. 1. will return an element stiffness matrix object (an Integrable_Matrix of type H0) of size 8 × 8. Nx).inverse().i = ----. // Eq. should return a 2 × 8 Integrable_Matrix. 2. not with respect to node number. for “grad” operator on Vh. Eq. 4•187 Notice that we arrange “u”. int. wx. 4•187 as the transpose of the ordinary mathematical definition on grad u. 4). C0 u = BASIS("int".Chapter 4 Finite Element Method Primer ∂u ∂v div u = ui. H0 w_x = INTEGRABLE_SUBMATRIX("int. 4•188.--------∂x ∂y Eq.+ ----∂x ∂y Eq. E = BASIS("int". and it is implemented as 1 2 3 H0_h H1_h::grad_() { H0 Nx = n.d() * x. H0 ret_val = wx(0)*(u[0]*E) + wy(0)*(u[1]*E). Nx).inverse().d() * x. 390 Workbook of Applications in VectorSpace C++ Library . 4•186 can be implemented as 1 2 3 4 5 6 7 8 H0_h H1_h::div_() { H0 Nx = n. wy = (+w_x[0][1]). 2). The nodal submatrices of the return value of “grad” operator are ∂N a --------∂x ∂N a --------∂y ∂N a --------∂x ∂N a --------∂y Eq. Therefore. H0&". j Eq. the inner product. } This divergence operation will return an Integrable_Matrix of size 1 × 8. “ div • div ”. int.d(). 1.

Two Dimensional Problems 4 5 6 7 8 9 10 11 12 13 } wx &= ~(+w_x[0][0]). 2). 1). 3 H0 w_x = INTEGRABLE_SUBMATRIX("int. H0&". 13 } The operator “def”. 12 return ret_val. // Eq. 4•190 With both “grad” and “gradT” already defined. 4). wy. 2. 1. The operator gradT has its nodal submatrices ∂N a --------∂x ∂N a --------∂x ∂N a --------∂y ∂N a --------∂y Eq. “def” can be implemented simply as Workbook of Applications in VectorSpace C++ Library 391 . e = BASIS("int". C0 eu = BASIS("int". 9 a = (e%eu)*(E1%E2). 2). wy &= ~(+w_x[0][1]).d() * x. E2 = BASIS("int". E1 = BASIS("int". int. is defined according to Eq.inverse(). 4•148 1 def u ≡ -. Nx).( grad u + ( grad u ) T ) 2 Eq. 5 C0 eu = BASIS("int". wy &= ~(+w_x[0][1]). 7 E1 = BASIS("int". of type H0_h which is derive from Integrable_Matrix of type H0. 4•189 11 wx*a[1][1] + wy*a[1][3]. 6 e = BASIS("int". 8 E2 = BASIS("int". return ret_val. 1). 10 H0 ret_val = wx*a[0][0] + wy*a[0][2] + //Eq. 4•189 which is implemented as 1 H0_h H1_h::grad_t_() { 2 H0 Nx = n. 4). which can not be obtained by the transpose of the resulting matrix of “grad”.d(). H0 ret_val = wx*a[0][0] + wx*a[0][3] + wy*a[1][0] + wy*a[1][3]. for the finite element space—Vh(Ωe). Both differential operators “grad” and “gradT” have return value. 4•188 The operator “gradT” is defined independently from “grad” for the finite element space—Vh(Ωe). This is because that the transpose operation on grad is with respect to its spatial derivatives only not with respect to element node number index—a. 4). with the size of 2 × 8. 4). wx. 4 wx &= ~(+w_x[0][0]). a = (e%eu)*(E1%E2).

Chapter
1 2 3 4

4 Finite Element Method Primer
// Eq. 4•190

H0_h H1_h::def_() { H0 ret_val = (+(grad_t(*this) + grad(*this))/2); return ret_val; }

The differential operator def also return a 2 × 8 H0_h type object. The double contraction is defined in Eq. 4•154 def u : def u = tr((def u)Tdef u) Eq. 4•191

The implementation of the binary operator “^” as double contraction operator is completely ad hoc. Under the discretion of the programmer, it has assumed that the two operands of the binary operator are the return values of the def operator. The return value has the size of 8 × 8. This is evident from the left-hand-side of Eq. 4•191. 1 H0 H0_h::operator^(const H0_h& a) { 2 H0 ret_val(8, 8, (double*)0, a.quadrature_point()); 3 H0 ret_sub = INTEGRABLE_SUBMATRIX("int, int, H0&", 2, 2, ret_val); 4 H0 def_w = INTEGRABLE_SUBMATRIX("int, int, H0&", 2, 4, a); 5 for(int a = 0; a < 4; a++) 6 for(int b = 0; b < 4; b++) { 7 H0 def_wa = +def_w(0,a), def_wb = +def_w(0,b); 8 H0 def_def = (~def_wa)*def_wb; // (def u)Ta (def u)b 9 H0 dds = INTEGRABLE_SUBMATRIX("int, int, H0&", 2, 2, def_def); 10 ret_sub(a,b) = +(dds(0,0)+dds(1,1)); // trace of “(def u)Ta (def u)b” 11 } 12 return ret_val; 13 } Line 3 is the nodal submatrices that we calculated according to Eq. 4•191, and upon which we loop over all nodes. This implementation can be activated by setting, at compile time, the macro definition “__TEST_COORDINATE_FREE_TENSORIAL_FORMULATION” for the same project “2d_beam” in project workspace file “fe.dsw”. The extension of H1 class in VectorSpace C++ Library to finite element space—Vh(Ωe) as H1_h class in the above is an example of the so-call programming by specification in the object-oriented method.

392

Workbook of Applications in VectorSpace C++ Library

Two Dimensional Problems
Post-Processing—Nodal Reactions
The reaction on each node can be computed after the displacement is known, according to “Kijuj”. The actual computation is done at the constructor of class “ElasticQ4”, and is invoked in the main() program as the followings. 1 2 3 4 5 6 7 8 9 10 11 12 13 ElasticQ4::ElasticQ4(int en, Global_Discretization& gd) : Element_Formulation(en, gd) { ... if(Matrix_Representation::Assembly_Switch == Matrix_Representation::REACTION) { stiff &= K | dv; the_element_nodal_value &= stiff * (ul+gl); } else stiff &=K | dv; } int main() { ... Matrix_Representation::Assembly_Switch = Matrix_Representation::REACTION; mr.assembly(FALSE); cout << "Reaction:" << endl << (mr.global_nodal_value()) << endl; }

The class “Matrix_Representation” has the member function “assembly()” which maps “the_element_nodal_value” to the “mr.global_nodal_value()” used in the “main()” function. The reaction is not computed in the present example of project “beam_2d”. The next project “patch_test”, in the next section, will compute this quantity.

Post-Processing—Stresses on Gauss Points
After the displacement solution is obtained, stresses can be computed from stress-strain relation, e.g., in Bmatrix form of Eq. 4•173, the stress is
h σe

ˆa = D Bu e

Eq. 4•192

ˆa After the nodal displacements, ue , are obtained, we can loop over each element to calculate the stresses on each Gaussian integration point as,

1 HeatQ4::HeatQ4(int en, Global_Discretization& gd) : Element_Formulation(en, gd) { 2 ... 3 if(Matrix_Representation::Assembly_Switch == Matrix_Representation::STRESS) { 4 H0 Sigma = INTEGRABLE_VECTOR("int, Quadrature", 3, qp); 5 Sigma = 0.0; 6 for(int i = 0; i < nen; i++) { 7 B1 &= Nx[i][0]; B2 &= Nx[i][1]; 8 DB[0][0] = Dv[0][0]*B1; DB[0][1] = Dv[0][1]*B2; 9 DB[1][0] = Dv[0][1]*B1; DB[1][1] = Dv[1][1]*B2; 10 DB[2][0] = Dv[2][2]*B2; DB[2][1] = Dv[2][2]*B1;

Workbook of Applications in VectorSpace C++ Library

393

Chapter

4 Finite Element Method Primer

h 11 Sigma += DB(0)*(ul[i*ndf]+gl[i*ndf]) + DB(1)*(ul[i*ndf+1]+gl[i*ndf+1]); // σ e = D Bu e ˆa 12 } 13 int nqp = qp.no_of_quadrature_point(); 14 for(int i = 0; i < nqp; i++) { 15 cout << setw(9) << en 16 << setw(14) << ((H0)X[0]).quadrature_point_value(i) 18 << setw(14) << ((H0)X[1]).quadrature_point_value(i) 19 << setw(14) << (Sigma[0].quadrature_point_value(i)) 20 << setw(14) << (Sigma[1].quadrature_point_value(i)) 21 << setw(14) << (Sigma[2].quadrature_point_value(i)) << endl; 22 } 23 } else stiff &= ... 24 } 25 int main() { 26 ... 27 Matrix_Representation::Assembly_Switch = Matrix_Representation::STRESS; cout << << "gauss point stresses: " << endl; 28 29 cout.setf(ios::left,ios::adjustfield); 30 cout << setw(9) << " elem #, " << setw(14) << "x-coor.," << setw(14) << "y-coor.," 31 << setw(14) << "sigma-11," << setw(14) << "sigma-22," << setw(14) << "sigma-12" << endl; 32 mr.assembly(FALSE); 33 }

Post-Processing—Stress Nodal Projection Method

ˆ ˆa Stress projection for nodal stress, σ e , is similar to the heat flux projection on node qe , the element stresses are interpolated from the nodal stresses as
a h ˆa σe ≡ Na ( ξ, η ) σe

Eq. 4•193

The weighted-residual statement with Galerkin weighting that w = Na

∫ Na ( σ e – σeh ) dΩ
h

= 0

Eq. 4•194

Substituting Eq. 4•192 and Eq. 4•117 into Eq. 4•118, we have
 ˆb  ∫ N a N b dΩ σ e = Ω 
a

ˆ ∫ ( Na ( D Buea ) ) dΩ

Eq. 4•195

ˆ The nodal stresses σ e can be solved for from Eq. 4•195. Following the same procedure for the heat flux projection on node, in the previous section, Eq. 4•195 can be approximated similarly for the stress nodal projection by implementing the following codes.

394

Workbook of Applications in VectorSpace C++ Library

Workbook of Applications in VectorSpace C++ Library 395 . The flag “Matrix_Representation::Assembly_Switch” is now set to “Matrix_Representation::STRAIN” and “Matrix_Representation::NODAL_STRAIN” for Gauss point stresses and nodal stresses.assembly(FALSE).node_array()[i]. } int main() { . DB[2][1] = Dv[2][2]*B1. nodal stresses and nodal strains of the 4-node quadrilateral element are shown in Figure 4•44. i++) { int node_no = oh. } The computation of strains on Gaussian integration points and nodes is similar to the computation of stresses. " << (mr. cout << "{ " << node_no << "| " << (mr. projected_nodal_stress(i) = ( ((H0)N[i])*Sigma | dv ) / lumped_mass. for(int i = 0.total_node_no().node_no().. stress_no. Quadrature". DB[2][0] = Dv[2][2]*B2. i < nen. The results of relative magnitudes of displacements. mr. i++) { C0 lumped_mass = ((H0)N[i]) | dv. the_element_nodal_value). } . cout << "nodal stresses: " << endl. } for(int i = 0. Sigma = 0. i < oh. Global_Discretization& gd) : Element_Formulation(en. (double*)0).0. for(int i = 0.global_nodal_value()[i][1]) << ". DB[0][0] = Dv[0][0]*B1. respectively. i < nen. We introduce the notorious pathology of the finite element method by demonstrating (1) shear locking and (2) dilatational locking for the bilinear four-node element in plane elasticity. C0 projected_nodal_stress = SUBVECTOR("int. DB[1][1] = Dv[1][1]*B2. DB[0][1] = Dv[0][1]*B2. if(Matrix_Representation::Assembly_Switch == Matrix_Representation::NODAL_STRESS) { int stress_no = (ndf+1)*ndf/2. we have strains computed according to ε e = Bu e . B2 &= Nx[i][1]. } } else stiff &= K | dv. 4•192 for stresses. Sigma += DB(0)*(ul[i*ndf]+gl[i*ndf]) + DB(1)*(ul[i*ndf+1]+gl[i*ndf+1]). Matrix_Representation::Assembly_Switch = Matrix_Representation::NODAL_STRESS.global_nodal_value()[i][0]) << ". H0 Sigma = INTEGRABLE_VECTOR("int.. In h ˆa place of Eq. " << (mr. gd) { . DB[1][0] = Dv[0][1]*B1... C0&". qp). 3.Two Dimensional Problems 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 ElasticQ4::ElasticQ4(int en. i++) { B1 &= Nx[i][0]..global_nodal_value()[i][2]) << "}" << endl.. the_element_nodal_value &= C0(nen*stress_no.

dashed line for tension). 396 Workbook of Applications in VectorSpace C++ Library . nodal stresses (crossed-hairs. solid line for compression. The magnitudes of these three quantities have all been re-scaled. and nodal strain (ellipsoidals) of the beam bending problem.Chapter 4 Finite Element Method Primer Figure 4•44Displacement (arrows).

y 2 2 u = Eq. 4•196. x2 and y2. 1989. 4•196 We considered a special case of a rectangle (Eq. McGraw-Hill book company. and R. Inc. η ) ≡ N a ( ξ. in the solution of vertical displacement “v” will not be captured by the element. “The finite element method: basic formulation and linear problems”. x. in plane stress.and y. for simplicity. Workbook of Applications in VectorSpace C++ Library 397 . vol. ξ. we have 1 1 –1 = -4 –1 1 1 1 –1 –1 1 1 1 1 1 –1 1 –1 h u e ( ξ. is1 xy u = 1 2 υ 2 v – -. under applied bending moment as shown in Figure 4•45. ˆa η )u e = P ( ξ. u = xy. 1994. ξη}. Therefore. v = const. 4•196 can be expressed in its generic form as “ Na = PC-1 ”. 1.axes. v]T for the bending problem. Since referential coordinates ξ. R.( 1 + ξ a ξ ) ( 1 + η a η ) 4 Eq. Taylor. Marcel Dekker. 4•198 η ξ 1 Λ (a) key-stoning. in-plane bending (b) (c) Figure 4•45 Rectangular element shear locking analysis. since the basis “xy” is included.Two Dimensional Problems Shear Locking of Bilinear 4-Node Element The bilinear 4-node element has shape functions as 1 N a ( ξ. For the bilinear four-node element the shape functions Eq.218 in MacNeal. 116 in Zienkiewicz. 2. will be represented correctly by the bilinear four-node element. p. y. p. the finite element space is spanned by the bases of P = {1. O.x – -. UK. 4•197 This analytical solution is shown in Figure 4•45b with υ = 0 for simplicity...H.axes of the rectangle is assumed to coincide with the physical coordinates x. from Eq. u = xy. The solution to the displacement field u = [u. η. The horizontal displacement component. xy}. η ) = -.2 Therefore. 4•45a). The quadratic terms. ˆa η )C – 1 u e . These quadratic forms of solution will be “substituting” or “aliasing” to the linear combination of bases in P.L. 1. New York.and η. the finite element space is also spanned by {1. where C–1 Eq. “Finite elements: their design and performance”.C.

Chapter 4 Finite Element Method Primer Let’s exam the “aliasing” of a quadratic solution u = ξ2 into a bilinear four-node element.= cons tan t 2 2 Eq. 4•197. and we have the corresponding strains as 398 Workbook of Applications in VectorSpace C++ Library . when a boundary value problem corresponding to a higher-order solution is imposed. 4•199 and.– -. The analytical strain. By symmetry of the element we can also obtain the alias of η 2 ⇒ 1 . 4•200 That is we have the alias of ξ 2 ⇒ 1 . 4•197. 1 1 Eq. considering the aspect ratio “Λ” in the transformation of natural to physical coordinates in a rectangular element. The vertical displacement solution in the bending problem in Eq. 4•202 where u and v are solutions in Eq. The bilinear 4-node element under the same bending condition responds with the solution in Eq. 4•201. 4•197 will then be aliased. The corresponding h ˆa nodal values u e and discretized variable u e are 2 ξ0 ˆa ue = ( ξa ) 2 = 2 ξ1 2 ξ2 2 ξ3 1 1 = .   1 = 1 ξ η ξη  -4   1 –1 –1 1 1 1 –1 –1 1 1 1 1 1 –1 1 –1       1 1 = 1 1 1 h ue = –1 ˆ a PC u e Eq.u = xy. and v = – ----. into Λ2 ν . corresponding to the bending problem is ∂u ----∂x ∂v ----∂y ∂v ∂u ----. That is the lower-order element. 4•201 With vertical displacement “v” as constant through out the element domain. the deformation becomes a “keystoning” or “x-hourglass” mode (see Figure 4•45c. such as the bilinear 4-node element. where the constant “v” is set to zero for comparing to the original configuration). exhibits locking phenomenon. derived from Eq.+ ----∂x ∂y εx εy γxy = y = – νy 0 Eq.

Eq. η axes are disWorkbook of Applications in VectorSpace C++ Library 399 .x).x) and εy(=v. 4•202 and Eq. That is the two hourglass modes become eigenvectors for the stiffness matrix that is evaluated at the center of the element. spurious modes (x-hourglass and y-hourglass) will arise. 0.Two Dimensional Problems εx εy γ xy y = 0 x Eq. 4•206 The components in Eq.---------  ∂y ∂x  ∂N a ∂N b  --------.---------  ∂y ∂x   ∂y ∂y  λ ( N a.---------  --------. 4•204 and the first term in Eq. 4•201. 4•205 only involve the direct strains εx(=u. The cross-hairs which parallel to the ξ. i ) ) ∂N a ∂N b 2  --------. 4•205 Notice that the positions in the stiffness matrix corresponding to variables u and v and their variations u’ and v’ as ( u’u ) ( u’v ) ( v’u ) ( v’v ) Eq.) In retrospect. With Poisson’s ratio in the range of ν = [0. A more satisfactory treatment is to add back both x2 and y2 to the set of shape functions which is the subject of “non-conforming element” in page 502 of Chapter 5. That is one Gauss point integration of in-plane shear strain at the center of the element. 4•203 Comparing Eq. j N b.---------  --------. 4•176 and Eq. had we apply 1-point integration to all terms.---------  ∂x ∂x   ∂x ∂y  N b.y+v.---------  ∂y ∂y  = µ +µ Eq. 4•177 are re-written as ∂N a ∂Nb ∂N a ∂N b  --------. both εy and γxy are in error.---------  ∂x ∂x  0 0 ∂N a ∂N b 2  --------. 4•203. This is evident from Figure 4•45c. 4•204 and µ ( δ ij ( N a. and 2 × 2 integration for the remaining direct strain components εx and εy. A partial solution to this locking problem is to evaluate γxy at ξ = 0.y). The components in the second term of Eq. 4•205 involve the in-plane shear strain γxy(=u.5]. and η = 0. These terms are evaluated with 2 × 2 points Gauss integration (the full-integration).---------  ∂y ∂y  ∂N a ∂Nb  --------.---------  ∂x ∂x  ∂N a ∂Nb  --------. The source of error is the interpolating failure of the bilinear four node element which leads to the aliasing of x2 and y2 terms in Eq. and these are to be evaluated at the center of the element where ξ = 0. k Nb. i Eq. This term is applied with 1-point Gauss integration (the reduced integration.---------  ∂x ∂y  ∂N a ∂N b  --------. γxy will be more serious than εy. η = 0. η = 0) in the followings. k ) + ( N a. 4•197 into constants in Eq. We introduce the treatment by selective reduced integration on inplane shear strain γxy(at ξ = 0. j ) = λ ∂N a ∂Nb ∂N a ∂N b  --------.

. y) is unlikely to be infinitesimal as can be approximated in Eq. the mapping from the reference element (in ξ. the selective reduced integration for curing the in-plane shear locking has a side effect.o. for an arbitrary element shape. “Finite elements: their design and performance”.e. i. discussed in the above.Chapter 4 Finite Element Method Primer torted at 2 × 2 Gauss integration points. The bilinear four-node element has 4(nen) × 2(ndf) = 8 d. A first-order approximation can be proposed to correct the frame dependent problem for the shear term. The origin is at the centroid of the element (computed as the intersections of two opposing mid-side line segments). There is no practical invariance formulation that can remove the shape sensitivity if the trapezoid component for a particular element shape is strong. Therefore the orientation of an element does matter. which often implemented with sparse matrix technique. 400 Workbook of Applications in VectorSpace C++ Library .e.2 In a finite element program. An alternative view is reveal by the rank of the element stiffness matrix. 1 × 3 = 3. “ The finite element method: linear static and dynamic finite element analysis”... The x’ and y’ axes are to make angles with ξ and η axes in natural coordinates such that ∂ξ ∂η ----. i. 1 The idea is the shear term presented in Eq. Englewood Cliffs. for example. The invariance formulation. 4•207 This approximation is possible to make the shear term nearly invariant if we deal only with element shapes that are very close to a square element. At the limit of infinitesimal coordinate transformation. We can symmetrize the two off-diagonal terms by choosing a local preferred coordinate system x’ as shown in Figure 4•46. 4•205 is not symmetrical. This is true only if the element stiffness matrix is fully integrated. Sudden change of the node-ordering can therefore inadversarily change the value of the global stiffness matrix dramatically. Unfortunately. For an arbitrary element shape.dx ∂y ∂x Eq. If the three rigid body modes have been properly constrained for the problem. can be activated by setting macro definition “__TEST_HUGHES” at compile time.. The selective reduced integration on the offending in-plane shear term is implemented in Program Listing 4•16 (project: “invariance_formulation” in project workspace file “fe. see p. the element is invariant with respect to rotation since a complete order of polynomial has been used. Therefore. 4•205 that involves in-plane shear strain γxy . 1994.241-248 in MacNeal. we expect spatial isotropy. When the selective reduced integration is applied to the second term in Eq. New Jersey. Prentice-Hall. we are left with 8-3 = 5 d. Therefore.dy = -----. 4•207 is to assume the “spin” at the centroid vanishes. R. the spatial isotropy will be lost.e. while it is totally undisturbed at the center of the hourglass deformation mode. That is the hourglass modes give zero energy if 1-point Gauss integration is used. the 2 × 2 integration on the terms involving the direct strains εx and εy provides a finite stiffness for the xhourglass and y-hourglass modes to prevent them from becoming spurious. Inc. we can decomposed the shape distortion into eigenvectors as rectangular. However.o.J. 261-262 from Hughes.f. the so-called completeness requirement. i.R. Marcel Dekker. which corresponding to the x-hourglass and y-hourglass modes. in the selective reduced integration. For an isoparametric element such as the bilinear 4-node element. the rank deficiency for the 1-point integration element stiffness matrix is 5-3 = 2.dsw”). T. A practical fixed to remedy the frame dependent in1.H. 4•207. 1987. parallelogram.. and trapezoid shapes (see Figure 4•48b).f. in order to minimize the bandwidth of the global stiffness matrix. New York. η) to physical element (in x. 2. see project in p. Eq.. Inc. The rank of the stiffness matrix is provided by number of integration points (1) times the number of stress-strain relations (3). which is adopted in the “co-rotational” formulation in finite element method.. the node-ordering can be changed.

0*e_.add(node).add(elem). int ena[4]. Global_Discretization&). 0. v[0] = 2. 2. 4. ena[2] = 12. 2. ena). elem = new Omega_eh(5. Omega_h::Omega_h() { Node *node.add(elem).0*h_e_-e_. 0. 0. omega_eh_array().0*h_e_. ena[3] = 11. Elastic_Invariant_Formulation_Q4(int. static const double lambda_bar = 2*lambda_*mu_/(lambda_+2*mu_). v[0] = 3. omega_eh_array().0.0*h_e_. node = new Node(11. u4 = u9 = v9 = u14 = 0 τy0 = τy10 = -75. ena[3] = 7. Global_Discretization&).0*h_e_. ena[0] = 6. 2. Omega_h& omega_h) { __initialization(df. ena[2] = 13.add(elem). node_array().Two Dimensional Problems #include "include\fe. 2. node = new Node(14. ena[0] = 5. v). double v[2]. v). 4. static const double lambda_ = v_*E_/((1+v_)*(1-2*v_)). 0. v[0] = 1. v). v[0] = 4. 0. ena[3] = 5.25.0*h_e_+e_. 2. elem = new Omega_eh(7. node_array(). ena[2] = 7. 0. 0. static const double v_ = 0.add(elem).0.add(node). 2. 4.add(node). ena). v[0] = 2. node = new Node(5.0*c_. ena[3] = 10. node_array().add(elem). 2. 2. node_array().add(node). ena[1] = 2.0.0*mu_. v). 4. 0. v). ena[0] = 7.0.add(node). node_array(). if(i == 0 || i == (col_node_no-1)) the_gh_array[node_order(i*row_node_no)][1] = -75.add(node).0*e_. ena[1] = 9. static const double E_ = 30. v). ena[3] = 6.0.0*c_.add(node). i < col_node_no. node_array(). ena[1] = 1. omega_h). v[0] = h_e_+e_. omega_eh_array(). node_array().0*h_e_. 2. v[0] = 3. node = new Node(9. node_array(). elem = new Omega_eh(3. ena[2] = 8.add(elem). omega_eh_array(). 0. omega_eh_array(). ena[3] = 8.0/3. v[0] = 2. 0.add(node). node = new Node(6.0. v[0] = 3. node = new Node(4. node = new Node(0. ena[1] = 4. ena[0] = 8. node_array(). ena[0] = 1. node = new Node(1. for(int i = 0. v). 0. i++) { the_gh_array[node_order(i*row_node_no)](1) = gh_on_Gamma_h::Neumann. 0. static const double mu_ = E_/(2*(1+v_)). Young’s modulus and Poisson ratio plane stress λ modification define nodes define elements B. ena[2] = 11. v). 4. node_array(). 2.add(node). else the_gh_array[node_order(i*row_node_no)][1] = -150.C. node = new Node(8. v[0] = 4. ena). 2.0e6. ena). static const double e_ = 0. the_gh_array[node_order(4)](0) = the_gh_array[node_order(14)](0) = the_gh_array[node_order(4)](1) = the_gh_array[node_order(9)](1) = gh_on_Gamma_h::Dirichlet. ena[2] = 6. ena[3] = 12. elem = new Omega_eh(4. 2. node_array(). ena[1] = 3. node = new Node(13. ena[2] = 14. 4. 2. node_array(). static const double K_ = lambda_bar+2. node = new Node(3. static const double h_e_ = L_/4.add(elem). v). 2. node = new Node(2. ena). elem = new Omega_eh(1. ena[0] = 2. ena[0] = 3.0. ena). 0. 0.add(node). v[0] = 4. node_array(). ena). node = new Node(10. } gh_on_Gamma_h::gh_on_Gamma_h(int df. elem = new Omega_eh(6. omega_eh_array(). node = new Node(7. v[1] = 2.h" static const double L_ = 10.0*h_e_. 4. elem = new Omega_eh(2. node = new Node(12. ena[1] = 7. v[0] = 0. ena[1] = 6.add(elem). v). v[1] = 1. v). omega_eh_array(). v). τy5 = -150 Workbook of Applications in VectorSpace C++ Library 401 . omega_eh_array(). v[0] = 0. v). ena). 4. }.add(node). node_array(). node_array().0*h_e_+2.0*h_e_. col_node_no = 3. ena[2] = 9.add(node).add(node).0. 2. 0. ena[3] = 13. v).0*h_e_-2. v[0] = h_e_-e_. int row_node_no = 5. v[1] = 0.0. static const double c_ = 1. v). v[0] = 0. ena[0] = 0. ena[1] = 8.add(node).0. elem = new Omega_eh(0.add(node). Omega_eh *elem. } } class Elastic_Invariant_Formulation_Q4 : public Element_Formulation { public: Elastic_Invariant_Formulation_Q4(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation *make(int. 0.

E = BASIS("int". int. H0 Nx = d(N) * d(X).---------  ∂x ∂x  ∂N a ∂Nb  --------.---------  ∂x ∂y  ∂N a ∂N b  --------.inverse(). wy &= w_x[0][1]. uh).0. Zai &= Z[0]. wy. Nx). } Elastic_Invariant_Formulation_Q4::Elastic_Invariant_Formulation_Q4( int en. H0 W_x = INTEGRABLE_SUBMATRIX("int. C0 stiff_vol = ( lambda_bar*( +((Wx*~Wx)*U[0][0]+(Wx*~Wy)*U[0][1]+ (Wy*~Wx)*U[1][0]+(Wy*~Wy)*U[1][1] ) ) ) | dV. Element_Type_Register element_type_register_instance. Eta &= Z[1]. H1 x1 = N*xl. wy1. Quadrature". nsd. int. ndf). nsd. Omega_h oh.) µ ∂N a ∂N b 2  --------. H0&". cout << gd. } 2 × 2 integration volumetric terms λ ∂N a ∂Nb  --------. n[2] = (1. Eta. int. eta. wx1.0. Global_Discretization& gd) : Element_Formulation(en.gd).0-zai)*(1. zai &= z[0]. Quadrature qp1(2. u = e*E. J dv1(d(x1). nx).---------  ∂y ∂y  2 × 2 integration (deviatoric stiffness which only involve direct strains εx & εy. 4. N[2] = (1+Zai)*(1+Eta)/4. nen).) 402 Workbook of Applications in VectorSpace C++ Library . C0 stiff_dev = stiff_dev_shear + stiff_dev_direct_strain. Zai. Wy &= W_x[0][1]. 1.lhs())). H0 nx = d(n) * d(x). notice that if the coordinates has been rotated the local preferred coordinates is the same as the pure shear term in the above not the volumetric term. Wy. (double*)0. 4. H0 w_x1 = INTEGRABLE_SUBMATRIX("int.det()).det()). oh). n[3] = (1. H1 X = N*xl.inverse(). eta &= z[1]. N[0] = (1-Zai)*(1-Eta)/4. qp1). wx1 &= w_x1[0][0].0. H1 x = n*xl. gh_on_Gamma_h gh(ndf. wx &= w_x[0][0]. Wx &= W_x[0][0].rhs())) / ((C0)(mr. n[1] = (1.0. C0 u = ((C0)(mr. 1).dsw”.---------  ∂y ∂y  Listing 4•16 Seletive reduce integration on the offending shear term (project workspace file “fe.inverse(). 4).det()). gd.---------  ∂y ∂x  ∂N a ∂N b  --------.---------  ∂x ∂x  0 0 ∂N a ∂N b 2  --------. zai. qp).0-eta)/4.Chapter 4 Finite Element Method Primer Element_Formulation* Elastic_Invariant_Formulation_Q4::make(int en.u_h() << endl. Wx. N[1] = (1+Zai)*(1-Eta)/4. 2.0*mu_)* (+( (wx1*~wx1)*U[0][0]+(wy1*~wy1)*U[1][1] ) )| dv1. static Elastic_Invariant_Formulation_Q4 elastic_invariant_formulation_q4_instance(element_type_register_instance).0+zai)*(1.0+eta)/4. n[0] = (1. H0&". J dV(d(X). 1.u_h() = gd. int. int. 2. gd. gd) { Quadrature qp(2.0-eta)/4. H0 w_x = INTEGRABLE_SUBMATRIX("int. Global_Discretization& gd) { return new Elastic_Invariant_Formulation_Q4(en. H0&". wy1 &= w_x1[0][1]. H1 z(2. stiff &= stiff_vol + stiff_dev. 1. C0 e = BASIS("int".0-zai)*(1.u_h() = u. qp). wx. oh).---------  ∂x ∂x  ∂N a ∂N b  --------. N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int.gh_on_gamma_h(). Global_Discretization gd(oh.0+zai)*(1. C0 stiff_dev_direct_strain = (2. H0 nx1 = d(n) * d(x1). mr.---------  ∂y ∂y  1 point integration(deviatoric stiffness which only involve shear strain γxy) µ ∂N a ∂N b  --------. int main() { int ndf = 2. n = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int. U_h uh(ndf. H1 Z(2. Quadrature". C0 stiff_dev_shear = mu_* (+( (wy*~wy)*U[0][0] +(wy*~wx)*U[0][1]+ (wx*~wy)*U[1][0] +(wx*~wx)*U[1][1] ) )| dv. gh. N[3] = (1-Zai)*(1+Eta)/4. return 0. nsd. } Element_Formulation* Element_Formulation::type_list = 0. U = (e%e)*(E%E).---------  ∂x ∂y  ∂N a ∂N b  --------. J dv(d(x). Matrix_Representation mr(gd). (double*)0. nx1).0+eta)/4.---------  ∂y ∂x  ∂N a ∂N b  --------. project “invariance_formulation”. qp1).assembly().

x x’ ξ θ1 = ξ. 1994. The origin of the local coordinate system is chosen as center at the intersection of the two diagonals of the quadrilateral. After the stiffness is computed at the element level. 1.Two Dimensional Problems y’ y θ2 x θ2 θ1 θ1 x’ Figure 4•47 MacNeal’s local preferred coordinate system for selective reduced integration on shear term. or simply θ1 = θ2 . This implementation can be activated by setting macro definition “__TEST_MACNEAL”. Note that for simplicity we do not implements the part of algorithm that choose the longest edge. θ1 ||dy|| = θ2 ||dx||.y y x Figure 4•46 Hughes’s local preferred coordinate system for the invariance formulation of the shear term under selective reduced integration. which is the bisector of the diagonals.H. p. if ||dy|| ~ ||dx|| which is consistent with the infinitesimal mapping assumption. under a preferred local coordinate system. the longest edge of the elements to begin element node numbering. for example. plane shear (after reduced integration) is to implement an algorithm to select. R. 1 Then transform the global coordinate system. We only implemented the more mathematical part of the algorithm that demonstrates how to translate to the center of the intersection of the two diagonals and then rotate to the local coordinate x’-axis. for computing the stiffness matrix. Workbook of Applications in VectorSpace C++ Library 403 .292 in MacNeal. same as the above. it is transformed back to the global coordinate system then assembled to the global stiffness matrix. The xaxis is chosen to be the bisector of the diagonal angle as shown in Figure 4•47.. η y’ θ2 = η.

Full Integration -0. 0.00518750 TABLE 4•2. (b) 5 eigenvectors for an arbitary shape distortion (x-.translation and rotation are not included). -0.125 (a) 0. The invariance formulation are performed on a distorted meshes as shown in Figure 4•48. Tip-deflections for selective reduced integration to prevent shear locking and choices of local preferred coordinate system for invariance of the formulation.tapering & y-tapering trapezoids Figure 4•48 (a) Distorted element mesh for testing invariance formulation in the selective reduced integration for shear terms.0061448 Hughes’ local coord.00535423 MacNeal’s local coord.0625 1 δ δ x-stretching & y-stretching rectangulars parallelogram (b) x.Chapter 4 Finite Element Method Primer The solutions of the selective reduced integration with invariance formulation is listed in TABLE 4•2. y.00565686 Analytical -0. -0.00311871 Selective Reduced -0.0625 Λ 0. 404 Workbook of Applications in VectorSpace C++ Library .

N[2] -= N[8]/4.pow(2))*(1+Eta)-N[8])/2. 10 N[6] = ((1-Zai. H1 Z(2. 3 × 3 integration points. For using this example. // add four edge nodes 9 N[4] = ((1-Zai.pow(2))*(1-Eta)-N[8])/2. N[1] -= (N[4]+N[5])/2.pow(2))*(1-Zai)-N[8])/2.00503098 -0. Zai &= Z[0]. // initial four corner nodes 5 N[0] = (1-Zai)*(1-Eta)/4. When define element in the constructor of the discretized domain “Omega_h” this is the number to be referred to the “ElasticQ9” element. The shape function is implemented based on a 4-to-9 nodes algorithm (see page 190 in Chapter 3) 1 2 3 4 Quadrature qp(2. 9. // add ceter node 7 N[8] = (1-Zai.We observe that the shear locking problem in bilinear fournode element is easily removed by using higher-order interpolation functions. The element is registered with element type number “1” in project “2d_beam”. Element Type ElasticQ9 Analytical Tip Deflection -0. N[1] = (1+Zai)*(1-Eta)/4. 6 N[2] = (1+Zai)*(1+Eta)/4. // Natrual Coordinates N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int. N[1] -= N[8]/4. we set macro definition to “__LAGRANGIAN_9_NODES”. 9). N[5] = ((1-Eta.pow(2))*(1-Eta. // modification to four corner nodes due to the presence of the center node 8 N[0] -= N[8]/4. N[3] = (1-Zai)*(1+Eta)/4. Eta &= Z[1]. 2. qp). Eta. N[7] = ((1-Eta. Quadrature".00518750 TABLE 4•3. 12 N[2] -= (N[5]+N[6])/2.Two Dimensional Problems Quadratic Element: The Lagrangian 9-Node Element The Lagrangian 9-node element is implemented as class “ElasticQ9” derived from class Element_Formulation. The results of tip deflection of the problem in this section are listed in TABLE 4•3. N[3] -= N[8]/4.pow(2))*(1+Zai)-N[8])/2.pow(2)). Workbook of Applications in VectorSpace C++ Library 405 . Tip deflection of Lagrangian 9-node element comparing to the analytical solution of Eq. Zai. N[3] -= (N[6]+N[7])/2. 4•175. (double*)0. int. // 2-dimension. // modification to four corner nodes due to the presence of the four edge nodes 11 N[0] -= (N[4]+N[7])/2. qp).

5 . In this section. we introduce the popular engineering approach. 4•210) remains finite.Λ 2 – ------------------2(1 – ν) 2 Eq.5) or nearly incompressibility ( ν → 0. and ε v → 0 (Eq. the selective reduced integration for dilatational locking. which has been shown to be both very simple and very successful. p.5).H.. 4•210). R.x 2 – ------------------. New York. 4•213 1. A more systematic study is the main subject of Chapter 5 on the “Mixed and hybrid finite element methods”. 4•211 Notice that even when ν → 0.y 2 2 2(1 – ν ) Eq. We will show examples that standard element formulation. 4•209 The volumetric strain is 1 – 2ν Ey ε v = ε x + εy =  -------------. while the pressure “p” (Eq. and v = – -. Let’s first resume the analysis for bending problem in the bilinear 4-node element in plane strain. 1994 “Finite elements: their design and performance”. the aliasing of solution in Eq.5 will have its solution “locked” severely. 4•208 leads to ν 1 u = xy. with ν → 0.Chapter 4 Finite Element Method Primer Dilatation Locking of Nearly Incompressible Elasticity in Plane Strain Considerable attention has been paid to the condition of incompressibility (with Poisson ratio ν = 0.y (1 – ν) 0 Eq. we have K → ∞ (Eq.5 in elasticity the condition is equivalent to imposing a kinematic constraint that the material is incompressible. 4•211). 406 Workbook of Applications in VectorSpace C++ Library . 4•212 The corresponding strains manifested in the bilinear 4-node element are εx εy γxy y = 0 x Eq. and p = Kεv = ------------------ 1–ν 3(1 – ν ) Eq. For ν = 0. in plain strain case. Marcel Dekker.. y. For a 4-node rectangular element.216-217 in MacNeal. The analytical solution is1 1 ν u = xy. 4•208 The corresponding analytical strains are εx εy γ xy y ν = – ---------------. Inc. and v = – -. Poisson’s ratio ν are related as E K = ---------------------3 ( 1 – 2ν ) Eq. 4•210 where the bulk modulus K and Young’s modulus E.

“The finite element method: basic formulation and linear problems”. The mean stress or pressure is 1 p ≡ -. 4•214 A volumetric-deviatoric split1 is applied to the stiffness of Eq. vol. γxy]T.Two Dimensional Problems Now the volumetric strain εv = y. Then. A quick fix to solve this “dilatation locking” problem is that we can divide the stiffness matrix into volumetric and deviatoric part. 4•216 K is the bulk modulus of the material. which is a finite value. 4•217 The deviatoric stress σd is (in vector form σ = [σx.5. 4•213. Workbook of Applications in VectorSpace C++ Library 407 . εy. UK. that is pq iajb = ke = ke Ω ∫ ε ( δu ) T Dε ( u )dΩ T = e iT ∫ B a D B b dΩe j Ω Eq. This is the dilatation locking at the incompressible limit of ν → 0. With this selective reduced integration scheme. both εy and γxy are in error. 4•210. 4•215 In vector form of plane elasticity. 1. Therefore. It is not immediately clear that how we can perform selective reduced integration on the B-matrix formulation.L. the volumetric part is applied the reduced integration. p. 0]T and ε = [εx. which adds back x2 and y2 to the set of the interpolation functions. the non-conforming element (page 502 in Chapter 5). When ν → 0. London. 4•214 into the volumetric part and deviatoric part. τxy]T) σd where 2 = µ D 0 ε d = µ  D 0 – -. McGraw-Hill.m ⊗ m ε   3 Eq.5 . will have the capability to remedy both the shear locking and dilation locking problems for the bilinear four-node element. 4•218 2 00 D0 = 0 2 0 0 01 Eq. 1989.334-352 in Zienkiewicz. We define the devioatric strain εd as v ε d ≡ ε – ---------3 mε m⊗m =  I – ---------------...( σ x + σ y + σ z ) = Kε v = K m • ε 3 Eq. Comparing Eq. 1. 4th ed. O. 4•209 and Eq. the condition of constant volume constraint can be relaxed. m = [1. ε  3  Eq. The error is caused by the interpolating failure of the bilinear four-node element in representing x2 and y2. K → ∞ and p → ∞ from Eq. σy. Taylor.C. Define the volumetric strain εv as ε v = ε x + εy = m • ε Eq. and R. The situation is exactly the same as in the shear locking problem. 4•219 1.

0}.det()). 0. Quadrature". 4. Workbook of Applications in VectorSpace C++ Library 408 . 3. Eta &= Z[1]. // D 0 = 0 2 0 {0. 2. n = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int. 00 1 {0. Quadrature". int. n[0] = (1-zai)*(1-eta)/4. Zai. the selective reduced integration can be applied to these two separate terms accordingly.0. 1). n[3] = (1-zai)*(1+eta)/4. qp). int. 2.m ⊗ m B b dΩ e j   3 Ω Eq. 4•221 as 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Quadrature qp(2. qp1). 20 0 double d_0[3][3] = { {2. 4•214) becomes pq iajb = k e = ke Ω ∫ ε ( δu ) T σ ( u )dΩ = Ω ∫ ε ( w )T [ σd ( u ) + mp ( u ) ]dΩ Eq. 0. zai. n[2] = (1+zai)*(1+eta)/4. Eta. (double*)0.det()).0}}.0. d_0[0]). 4•216 and Eq. J d_v(d(x). 3. H1 x = n*xl.inverse(). Zai &= Z[0]. eta.Chapter 4 Finite Element Method Primer From Eq.0. N[2] = (1+Zai)*(1+Eta)/4. qp). H0 nx = d(n) * d(x). 4. The following codes implemented Eq. N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int. 2.inverse(). int. n[1] = (1+zai)*(1-eta)/4. qp1). H1 X = N*xl. // 1-point reduced integration H1 z(2.0}. // Physical Coordinates H0 Nx = d(N) * d(X). const double*".0. N[3] = (1-Zai)*(1+Eta)/4. 1.0. 4•218 the volumetric-deviatoric split version of the B-matrix formulation (Eq. eta &= z[1]. zai &= z[0]. 4). // 2 × 2 points standard integration H1 Z(2. 0. 4•221 Therefore. N[0] = (1-Zai)*(1-Eta)/4. N[1] = (1+Zai)*(1-Eta)/4. Quadrature qp1(2.0.m ⊗ m B b dΩ + ∫ B a K ( m ⊗ m )B b dΩ e j   3 Ω  Ω We may define the volumetric stiffness and deviatoric stiffness separately as T k vol = e iT ∫ B a K ( m ⊗ m )B b dΩe j Ω 2 T k dev = e iT ∫ B a µ  D 0 – -. 4•220   2 T T = e iT  ∫ B a µ  D 0 – -. 0. J dv(d(X). (double*)0. C0 D_0 = MATRIX("int.

and “__TEST_SELECTIVE_REDUCED_INTEGRATION” defined at compile time. The deviatoric stiffness is implemented in line 33.“__NEARLY_INCOMPRESSIBLE”. 0]T H0 W_x = INTEGRABLE_SUBMATRIX("int. 2 T C0 stiff_dev = ((~B) * (mu_*(D_0-2.0) || ~wy ) & (~wy || ~wx ). wx &= w_x[0][0]. the bulk modulus1 is 2 K = λ + -.00305825”. The result of tip deflection with standard integration scheme is “-0. b &= (~wx || C0(0...0)) & (C0(0. N. 1. Workbook of Applications in VectorSpace C++ Library 409 . const double*".   iajb = λ N ke ∫ a. 1965. “ Foundations of solid mechanics”. 4•156. // kdev = e iT ∫ Ba µ  D 0 – -. int.Two Dimensional Problems 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 double m_0[3] = {1. For the coordinate-free tensorial formulation of Eq. nsd. 1. This computation can be done with macros “__TEST_PLAIN_STRAIN”.Y.J.“__TEST_B_MATRIX_VOLUMETRIC_D EVIATORIC_SPLIT”. 1.000149628” (i. 4•223 We notice that in Eq. b.0) || ~Wy ) & (~Wy || ~Wx ). wy &= w_x[0][1]. B.129-130 in Fung.e. see p. k dΩ + ∫ Na. With the selective reduced integration on the volumetric term.µ 3 Eq.. // kvol = e iT ∫ B a K ( m ⊗ m )Bb dΩe j stiff &= stiff_dev + stiff_vol. H0&". T C0 stiff_vol = ((~b) * ((K_*(m%m)) * b)) | d_v. i dΩ  Ω  Ω Ω Eq.0. Nx).0}.0*(m%m)) * B)) | dv. Wx &= W_x[0][0]. the tip-deflection is “-0. 4•224 1. m_0). C0 m = VECTOR("int. a b k e= a ( φ e . j dΩ + µ  δij ∫ Na. 4•161. Englewood Cliffs. 1. Prentice-Hall.0)) & (C0(0. and lines 11-20 define 1-point integration. H0&".25 in TABLE 4•2. Wy. sever locking compared to tip deflection of ElasticQ4 element with ν = 0. nx). // m = [1. i Nb. 0. wy. Wx. Ω Lines 1-10 define 2 × 2 points integration. Inc. C. j Nb. B &= (~Wx || C0(0. 4•221.). 3. k Nb.0/3. and the volumetric stiffness in line 40.0. Ω wx. under B-matrix formulation.m ⊗ m B b dΩ e j   3 H0 w_x = INTEGRABLE_SUBMATRIX("int. nsd. 4•222 and the indicial notation formulation of Eq. φe ) = Ω ∫ [ λ ( div N a • div N b ) + 2µ ( def N a ): def N b )]dΩ Eq. Wy &= W_x[0][1]. int.

in project “2d_beam”. We have K ≈ λ . “__NEARLY_INCOMPRES SIBLE”. the tip-deflection is “-0. 410 Workbook of Applications in VectorSpace C++ Library . involve µ and λ. 4•221. The two first terms of Eq. and with the reduced integration scheme.000149995”. “__TEST_COORDINATE_FREE_TENSORIAL_FORMULATION” and “__TEST_INDICIAL_NOTATION_FORMULATION”. 4•223 are approximately equivalent to the kvol in Eq. We can simply choose these two terms for reduced integration and the implementation is straight forward.5 ). and“__TEST_SELECTIVE_REDUCED_INTEGRATION” together with corresponding macro definitions for the above two formulations. 4•222 and Eq. With standard integration scheme. give the same results. respectively. λ >> µ.Chapter 4 Finite Element Method Primer At the nearly incompressible limit ( ν → 0. The implementation can be activated. These two alternative formulations.00311641”. by setting the macro definitions “__TEST_PLAIN_STRAIN”. the tip-deflection is “-0.

4•225 We observe that the imposing displacement field for the patch test is therefore linear. Zienkiewicz. a well-thought-out plan for debugging and testing is of primary importance for hands-on finite element practitioners. O.00024 -0.3. J.H.L.L. or. R.0006 v 0.0000 0.. 1986. and σy = τxy = 0. This gives a simple exact solution the nodal displacements. and A. for more availability. Nodal displacement. and displacement (u. Chan. and R.0028 0.0008 0.00096 rx 2 -3 -2 3 0 0 0 0 ry 0 0 0 0 0 0 0 0 TABLE 4•4.C. pp.002x νσ x νσ x σ y εy = – -------.0040 0..lib requires user’s participation in programming to complete the application programs. Taylor.0000 0. Taylor. an abbreviated representation as Chapter 11 in Zienkiewicz. 22. The strain-stress relation for the plane stress assumption gives solutions of constant strain. vol. London.+ ----. In this case. “The finite element method: basic formulation and linear problems”. Workbook of Applications in VectorSpace C++ Library 411 . As a framework based library.Two Dimensional Problems 4. nodal stresses and nodal reactions of the element patch.002 E E E ⇒ u = 0. and nodal reactions shown in TABLE 4•4. 39-62. McGraw-Hill.0006 y E E E 2 ( 1 + ν )τ xy γ xy = ---------------------------. A simple constant stress (strain) solution over entire problem domain is assumed. not a caned-program.3. Therefore. the only non-zero stress is a constant stress in x-direction σx = 2.0000 0. This section gives many examples of how to develop proper test suites for finite element method. These test suites are based on a well known test plans1.0000 -0.. 1. and Poisson’s ratio ν = 0.00036 -0. “The patch test--a condition for assessing f..00120 -0. 1989. σx 2 2 2 2 2 2 2 2 σy 0 0 0 0 0 0 0 0 τxy 0 0 0 0 0 0 0 0 Node # 0 1 2 3 4 5 6 7 u 0. International Journal of Numerical Methods in Engineering.4 Patch Tests—Finite Element Test Suites for Software Quality Assurance (SQA) Finite element is such a complicated method that the software quality assurance (SQA) can be quite a challenging task.0030 0. O.00180 -0.C. convergence”.C.= ----. Simo.= 0 E Eq.C. UK. nodal stresses. Patch Tests—Consistency and Stability Consider an element patch shows in Figure 4•49 in plane stress with material properties of Young’s modulus E = 1x103. fe.= – 0.m. v) as σ x νσ y σx εx = ----.0006 ⇒ v = – 0.– -------.= 0.0040 0.e.00120 -0.= – -------.

The uniform reduced integration (1-point Gauss integration) can be performed on this program by setting macro definition “__TEST_UNIFORM_REDUCED_INTEGRATION” at compile time. as listed in TABLE 4•4.4. 7).6) 1 (2.002x.5. We first specify all nodes with the linear displacement calculated from u = 0.o. the “reaction” calculated according to “-Kijuj” should be identically zero. The Test B can be 412 Workbook of Applications in VectorSpace C++ Library . Then. 4•226 where Kij is the global stiffness matrix and fi is the global nodal force vector. if the governing partial differential equation is to be satisfied. will lose accuracy significantly and may give out erroneous solution. 5.s fx =3 Figure 4•49 Patch of elements for consistency and stablility test. 2. is specified for the internal nodes (# 4. and v = -0. A problematic stiffness matrix. vj]T is the solution vector. 2) 3 (0.3 σx = 2. and x = [x.Chapter 4 Finite Element Method Primer Consistency 2 (2. 3) (Test A) (Test B) E = 1x103.f. The Program Listing 4•17 implements the test suite for the Test A described in the above. This is the “Test A” in Figure 4•49.o. Since no loading. fi in Eq.lib”. y]T is the nodal coordinates. σy = τxy =0 (0. 0) 4 6 Stability (Test C) fx =2 (1. the unknown uj on internal nodes (# 4. the “Test B” checks the accuracy of the stiffness matrix maintained in the process of matrix solution step.3. 6.4) 0 (0. 7) can be calculated according to uj = (Kij)-1fi Eq.0006y fixed d. we specified only nodes on the boundaries.0) 5 (1.002x free d. 1. where uj = [uj. ν = 0. 4•226. up to machine accuracy. Consistency Requirement demands the governing partial differential equation to be satisfied exactly. 6. 0. The matrix solver is a fixture in “fe.f.0006y. The matrix form of the weak statement derived from the governing partial differential equation is Kijuj = fi Eq. In the “Test B” in Figure 4•49. Assuming the matrix solver chosen is appropriate to solve the problem at hand. The standard (full-) integration (2 × 2) for Test A is the default setting of this program. Both the standard integration and uniform reduced integration produce the exact reaction. The Test A is useful in checking the correctness of program statements in implementing the stiffness matrix. 4•227 This step requires the matrix solver to “invert” the stiffness matrix Kij.6) 7 (0.4. a second step for checking the consistency requirement. 5. or improper matrix solver. 0) u = 0.0.s v = -0.

b++) { B1 &= Nx[b][0]. ena). Eta &= Z[1]. v[1] = 0.add(node). v). node = new Node(2. } static const double a_ = E_ / (1-pow(v_. Omega_eh* elem. elem = new Omega_eh(3. Eta. b++) for(int a = b+1. static const double lambda_bar = 2*lambda_*mu_/(lambda_+2*mu_). Node* node. const double*". node = new Node(6. node = new Node(3.0}. C0 D = MATRIX("int. 0. ElasticQ4::ElasticQ4(int en.004. 2. b < nen. Global_Discretization&). DB[0][1] = Dv[0][1]*B2. 2. 0. 2. ena[0] = 0. a_*(1-v_)/2. the_gh_array[node_order(6)][1] = -0. a++) { K[2*a ][2*b] = K[2*b ][2*a ]. node = new Node(5. Zai &= Z[0]. J dv(d(X). the_omega_eh_array.add(elem).004. 4.h" static const double E_ = 1.0. v[1] = 0. v[1] = 0. Omega_h::Omega_h() { double v[2]. K[2*a ][2*b] = B1*DB[0][0] + B2*DB[2][0].00036. v). } gh_on_Gamma_h::gh_on_Gamma_h(int df. H1 Z(2. 0. i < 8. ena[2] = 7. ena[0] = 7. N[1] = (1+Zai)*(1-Eta)/4. node = new Node(1. the_omega_eh_array.0008. v). v[0] = 0. DB[2][1] = Dv[2][2]*B1. 2. ena).0. qp). DB[2][0] = Dv[2][2]*B2.add(node).4. elem = new Omega_eh(1. v[0] = 0. ena[3] = 3. K[2*a+1][2*b] = K[2*b ][2*a+1].0} }. ena[1] = 4. 2.0018. a <= b.add(elem). Global_Discretization& gd) { return new ElasticQ4(en. qp). 0.add(node).det()). the_omega_eh_array. 3. the_gh_array[node_order(7)][1] = -0. v[0] = 1. i++) for(int j = 0.003. N[0] = (1-Zai)*(1-Eta)/4. omega_h). elem = new Omega_eh(0. 0.00096. ena[3] = 6.add(elem). 0. Quadrature". ena[3] = 7. node = new Node(4. 2.gd). v[0] = 2.4. v[0] = 1. ena[0] = 0. (double*)0. DB[1][1] = Dv[1][1]*B2. v). v[1] = 0. j++) the_gh_array[node_order(i)](j) = gh_on_Gamma_h::Dirichlet.0.add(node).0. the_gh_array[node_order(7)][0] = 0.2)). int. N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int. ena). for(int b = 0. } define nodes define elements define boundary conditions define element “ElasticQ4” Workbook of Applications in VectorSpace C++ Library 413 . ena[0] = 5. the_omega_eh_array. static const double Dv[3][3] = { {a_. the_node_array. j < 2. 3. the_gh_array[node_order(5)][1] = -0. v). K[2*a ][2*b+1] = K[2*b+1][2*a ]. 0. ena[1] = 1. K[2*a ][2*b+1] = B1*DB[0][1] + B2*DB[2][1]. the_node_array. Dv[0]). 4. the_gh_array[node_order(3)][1] = -0. the_gh_array[node_order(1)][0] = 0. v[1] = 2. N[2] = (1+Zai)*(1+Eta)/4. K[2*a+1][2*b] = B2*DB[1][0] + B1*DB[2][0]. the_gh_array[node_order(2)][0] = 0. ena[3] = 4. } class ElasticQ4 : public Element_Formulation { public: ElasticQ4(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation *make(int. ena[3] = 3.0}. v[1] = 3. the_node_array. ena[2] = 5. ena[1] = 5. ena). the_node_array. } } for(int b = 0. {0. ena). for(int a = 0. v). the_omega_eh_array.0. 0. H1 X = N*xl.0006. int.0. elem = new Omega_eh(4. }. ElasticQ4(int. v[0] = 0. int ena[4]. the_gh_array[node_order(4)][0] = 0. ena[1] = 6. a_.0. {a_*v_. gd) { Quadrature qp(2.5. elem = new Omega_eh(2. v). the_node_array.0. 0. a_*v_. DB[1][0] = Dv[0][1]*B1. 2. ena[2] = 6. the_gh_array[node_order(5)][0] = 0. node = new Node(7. b < nen.6.0. 0. 0. 4.add(node). node = new Node(0. 4. 0. B2 &= Nx[b][1].add(elem).add(node).add(node). static const double mu_=E_/(2*(1+v_)). for(int i = 0. the_node_array.add(elem).00024. v[0] = 0.add(node). v[0] = 2. Global_Discretization& gd) : Element_Formulation(en. the_gh_array[node_order(6)][0] = 0.0012.0. N[3] = (1-Zai)*(1+Eta)/4. the_node_array. 4.0e3. 4. K[2*a+1][2*b+1] = K[2*b+1][2*a+1]. Omega_h& omega_h) { __initialization(df. v[1] = 2. ena[2] = 2. DB[0][0] = Dv[0][0]*B1. ena[2] = 2. v).3. ena[1] = 1. 2. static const double lambda_=v_*E_/((1+v_)*(1-2*v_)). Zai. ena[0] = 4.0.0028. a++) { B1 &= Nx[a][0]. B2 &= Nx[a][1]. the_gh_array[node_order(2)][1] = -0. the_gh_array[node_order(4)][1] = -0. v[1] = 1.Two Dimensional Problems #include "include\fe.0012. Element_Formulation* ElasticQ4::make(int en.4. a < nen. Global_Discretization&).6. the_node_array. K[2*a+1][2*b+1] = B2*DB[1][1] + B1*DB[2][1]. static const double v_ = 0. 0.3. 2. 4).

oh). DB[0][1] = Dv[0][1]*B2. DB[1][0] = Dv[0][1]*B1. } for(int i = 0. projected_nodal_stress(i) = ( ((H0)N[i])*Sigma | dv ) / lumped_mass. DB[0][1] = Dv[0][1]*B2. } else if(Matrix_Representation::Assembly_Switch == Matrix_Representation::STRESS) { H0 Sigma = INTEGRABLE_VECTOR("int. for(int i = 0. DB[1][1] = Dv[1][1]*B2. i++) { B1 &= Nx[i][0].quadrature_point_value(i)) << setw(14) << (Sigma[1]. i < nen. } compute nodal stresses projection declare global discretization and matrix representation compute reaction compute stresses on Gauss points compute nodal stresses projection Listing 4•17 Patch test A(project workspace file “fe. mr. 3. Quadrature". (double*)0). Omega_h oh. } } else if (Matrix_Representation::Assembly_Switch == Matrix_Representation::NODAL_STRESS) { int stress_no = (ndf+1)*ndf/2.assembly(FALSE).quadrature_point_value(i) << setw(14) << ((H0)X[1]). DB[1][0] = Dv[0][1]*B1. stress_no. DB[2][0] = Dv[2][2]*B2. 414 Workbook of Applications in VectorSpace C++ Library .no_of_quadrature_point().0. DB[2][0] = Dv[2][2]*B2. for(int i = 0. i++) { cout << setw(9) << en << setw(14) << ((H0)X[0]). C0&". C0 projected_nodal_stress = SUBVECTOR("int. Matrix_Representation::Assembly_Switch = Matrix_Representation::STRESS. the_element_nodal_value). DB[2][1] = Dv[2][2]*B1.. i++) { B1 &= Nx[i][0]. Quadrature". U_h hh(ndf.quadrature_point_value(i)) << endl. Global_Discretization gd(oh. B2 &= Nx[i][1].Chapter 4 Finite Element Method Primer Post-processing compute reaction compute stresses on Gauss integration points if(Matrix_Representation::Assembly_Switch == Matrix_Representation::REACTION) { stiff &= K | dv. H0 Sigma = INTEGRABLE_VECTOR("int. cout << setw(9) << " elem #. cout << "gauss point stresses: " << endl. Sigma = 0.assembly(FALSE). mr.0.quadrature_point_value(i) << setw(14) << (Sigma[0]. Matrix_Representation::Assembly_Switch = Matrix_Representation::REACTION. gh. Matrix_Representation mr(gd). cout. DB[1][1] = Dv[1][1]*B2. Global_Discretization hd(oh. DB[2][1] = Dv[2][2]*B1.assembly(FALSE).. } int nqp = qp." << setw(14) << "y-coor. 3. Matrix_Representation::Assembly_Switch = Matrix_Representation::NODAL_STRESS. qp)." << setw(14) << "sigma-22. DB[0][0] = Dv[0][0]*B1.global_nodal_value()) << endl. the_element_nodal_value &= C0(nen*stress_no. mr. the_element_nodal_value &= stiff * (ul+gl)." << setw(14) << "sigma-12" << endl. return 0. oh). i < nqp. Element_Type_Register element_type_register_instance.setf(ios::left. qp). static ElasticQ4 elasticq4_instance(element_type_register_instance). int main() { int ndf = 2. i < nen. i++) { C0lumped_mass = ((H0)N[i]) | dv. B2 &= Nx[i][1]. cout << "reaction:" << endl << (mr. Sigma = 0.quadrature_point_value(i)) << setw(14) << (Sigma[2]. for(int i = 0. Sigma += DB(0)*(ul[i*ndf]+gl[i*ndf]) + DB(1)*(ul[i*ndf+1]+gl[i*ndf+1]).ios::adjustfield). } } else stiff &= K | dv. hh). cout << "nodal stresses: " << endl << (mr. DB[0][0] = Dv[0][0]*B1. U_h uh(ndf. gh." << setw(14) << "sigma-11. } Element_Formulation* Element_Formulation::type_list = 0. Sigma += DB(0)*(ul[i*ndf]+gl[i*ndf]) + DB(1)*(ul[i*ndf+1]+gl[i*ndf+1]).dsw”. oh). gh_on_Gamma_h gh(ndf. i < nen.global_nodal_value()) << endl. uh). " << setw(14) << "x-coor. project “patch_test” with Macro definition “__PATCH_TEST_A” set at compile time).

which is also the same as the reactions on these two nodes computed from the reaction of Test A. x-translation. and node #2 is given loading of fx = 2. If this does occur the eigenvectors.0006y. node #1 is given loading of fx = 3. i. y-translation. associated with the signular values of the uniform reduced (1 × 1) integration stiffness matrix. Workbook of Applications in VectorSpace C++ Library 415 . will pollute the solution and render the solution useless.e. Again. which is chosen to prohibit three modes of rigid body motions.hourglass modes). namely. and v = -0. node # 0 is fixed on both directions and node # 3 is fixed on x-direction but allowed to be moved on y-direction. Stability Requirement examines if the zero-energy modes (eigenvalues) of the stiffness matrix Kij can be excited from loading fi on the boundaries. In the “Test C” in Figure 4•49.002x. The displacement solutions from Test C are. In this case. Note that fixing these three degree of freedoms to zero is still consistent with the assumed solution of u = 0. with singular value decomposition. both the standard integration and uniform reduced integration produce the exact internal nodal displacements as listed in TABLE 4•4. (b) solution with uniform reduced (1 × 1) integration..Two Dimensional Problems activated by setting macro definition “__PATCH_TEST_B” in the same project. (c) solution with uniform reduced integration using pseudo (Moore-Penrose) inverse for matrix solver. checked against the assumed solutions. (d) and (e) are compared to two zero-energy hourglass modes of a square bilinear element (the eigenvectors designated as the xhourglass and y. This suppresses three degree of freedoms. (b) 1x1 solution (c) Pseudo-inverse 1x1 solution (a) 2x2 solution (d) y-hourglass mode (for a square) (e) x-hourglass mode (for a square) Figure 4•50 Deformation of the element patch magnifies 50 times in (a) solution with standard 2 × 2 integration points. then. with arbitrary magnitudes but no energy contribution. and infinitesimal rotation.

Chapter 4 Finite Element Method Primer With the same project “patch_test” in project workspace file “fe.. The effect of this generalized inverse is to filter out two eigen-modes corresponding to the two singular values which are very close to zero... the exact solution in TABLE 4•4.R. the most expensive one among the matrix solver provided in Chapter 1.242 in Hughes. With singular value decomposition the matrix can be solved with the so-called Moore-Penrose (pseudo-) inverse as in page 40 of Chapter 1. is reproduced. For standard integration (2 × 2). These two eigen-modes are plotted and compared to two spurious hourglass modes of a square bilinear element (some use the “key-stoning mode” for bilinear element and reserve the term “hourglass mode” for quadratic element)1. see p. T. Note that we compare the outlines of these two eigen-modes to the two spurious modes of a square bi-linear element.e.dsw”. Under the uniform reduced integration. However. 1987. the rank of the stiffness matrix has rank deficiency of 2 (the full rank = 13 under 2 × 2 integration. i. “The finite element method: linear static and dynamic finite element analysis”. If we set macro definition “__TEST_UNIFORM_REDUCED_INTEGRATION” (1-point Gaussian integration) at compile time. we emphasize that the singular value decomposition is used as an analytical tool to analyze the rank-deficient nature of the problem. The internal nodes of the current element patch can be considered as to add to higher order variations on top of the two spurious modes. 1. and the rank = 11 under 1-point integration). J. The solution magnified by 50 times is shown in Figure 4•50a. We can then set macro defintion “__TEST_SINGULAR_VALUE_DECOMPOSITION” to analyze the problem. Englewood Cliffs. the Test C is activated by setting the macro definition “__PATCH_TEST_C”. Prentice-Hall Inc. New Jersey. The computation cost of the singular value decomposition is very expensive compared to that of the Cholesky decomposition for a symmetrical matrix. 416 Workbook of Applications in VectorSpace C++ Library . the solution rendered is useless as shown in Figure 4•50 (b).

S. and J. 2. 39-62. and z. London. vol. Goodier. and w = 0 The strain vector is defined as2 ∂w -----∂z ∂u ----∂r = u -r ∂u ∂w ----.H. an arbitrary mesh size should produce the exact solution. Zienkiewicz. The weak patch test. convergence”. Consider an axisymmetrical problem as shown in Figure 4•51. where the B-matrix for the axisymmetrical case becomes 1. International Journal of Numerical Methods in Engineering.e. Workbook of Applications in VectorSpace C++ Library 417 . w are the displacement along r-.C. Simo. We assume solution as u = 2r.L.P. pp. “ Theory of elasticity”. 1986..1 For an axisymmetrical problem with coordiz θ h 3 0 r=1 Figure 4•51An axisymmetricl problem for weak patch test. and the displacement along θ-direction is assumed zero.N. θ.m.directions.. nate system denoted as r. z. 4•228 εz ε = εr εθ γ rz Eq. U..Two Dimensional Problems Weak Patch Test for an Axisymmetrical Problem The patch test require only when the mesh size “h” approach zero. respectively. Inc. with h εe ˆa = B a u e .C. Taylor.. McGraw-Hill. for problem written in other than the Cartesian coordinates. 1970. J. 22.K. Chan. 4•229 Therefore. and u.C. and A. O.+ -----∂z ∂r h 4 1 5 2 h r Eq. Therefore. revert the criterion to pass the patch test as a series of solutions converging to the exact solution when the mesh size approaches zero. the approximated solution should converge to the exact solution. Chapter 12 in Timoshenko. For problem in the Cartesian coordinates the “coefficients” of the governing partial differential equation are constants. “The patch test--a condition for assessing f. R.

and ue = e ˆa Na ve ----. For the selective reduced integration. This gives εr= εθ = σr = σθ = 2 Workbook of Applications in VectorSpace C++ Library Eq. 0]T.--------∂z ∂r Eq.1 1–ν 0 0 0 0 Eq. 4•233 with two simple modifications for axisymmetrical consideration that m = [1. for simplicity. 4•234 For the current problem.0 r ∂N a ∂N a --------.Chapter 4 Finite Element Method Primer 0 ∂N a --------∂z ∂N a --------.0 ˆ ua ∂r ˆa Ba = . the material constants are given as E = 1. 4•235 418 . and υ = 0. 4•221 is still valid T k vol = e iT ∫ Ba K ( m ⊗ m )B b dΩe j Ω 2 T k dev = e iT ∫ B a µ  D 0 – -.m ⊗ m B b dΩ e j   3 Ω Eq. 1. 1.----------1–ν 1–ν ν 1 ----------1–ν ν ----------. 4•230 For isotropic material case the D matrix becomes 1 ν ----------E(1 – ν) 1–ν D = ------------------------------------( 1 + ν ) ( 1 – 2ν ) ν ----------1–ν 0 ν ν ----------. 4•231 0 1 – 2ν ------------------2( 1 – ν ) In the integration of axisymmetrical problem the stiffness matrix is Ke = ∫ B T D BdΩ Eq. 4•232 The infinitesimal volume is taken over the whole ring of material as dV = 2πr dr dz. the volumetric and deviatoric split of the stiffness matrix as in Eq. and 2 D0 = 0 0 0 0 2 0 0 0 0 2 0 0 0 0 1 Eq.

The Program Listing 4•18 implements the axisymmetrical patch test (Eq.8. 0. B.1 0.0 ∂r Ba = Na ----. The results of nodal radial displacement (u) at node number 1 and 4 are all exact under the 2 × 2 integration scheme. and 0. It shows that when the element size “h” goes down. Wz.4. Wz &= W_x[0][1].0 2.00049 2.0) )& (~Wz || ~Wr ). 4•230.01114 TABLE 4•5. We notice that the implementation of the B-matrix for the axisymmetrical problem is implemented according to Eq. The macro definition “__TEST_SELECTIVE_REDUCED_INTEGRATION” can be set at compiled time for the selective reduced integration.05.2.1. R &= (H0)X[0]. and Eq.0 2. The z-displacement of node number 1 is fixed to zero to prevent the ztranslation of the rigid body motion. 0. 1. 0.05 0.--------∂z ∂r Lines 5-8 use matrix concatenation operation to capture the semantics of B-matrix directly. and 4 2. Workbook of Applications in VectorSpace C++ Library 419 . 4•233 with definition of D0 in Eq. nsd. int. Wr. The radial displacement for axisymmetrical problem.0 r ∂N a ∂N a --------. For the patch test we take the element size “h” as 0. Nx). B &= (C0(0. H0&".8 Node # 1. the radial displacement solution converges quickly to the assumed solution Element size “h” 0. 4•234).2 0. 0 ∂N a --------∂z ∂N a --------. The radial displacement on nodes 1 and 4 under the selective reduced integration are shown in TABLE 4•5.00003 2. R. Wr &= W_x[0][0].4 0.0) )& (~((H0)N)/R || C0(0.Two Dimensional Problems and all other stresses and strains as zero.0) || ~Wz )& // (~Wr || C0(0. 4•230 as 1 2 3 4 5 6 7 8 H0 W_x = INTEGRABLE_SUBMATRIX("int.

static const double lambda_=v_*E_/((1+v_)*(1-2*v_)). v[0] = r_.0. omega_h). }. f_r = 2. 2. ena[1] = 1. f_r = -2.0*PI_*r*h_*sigma_r. static const double r_=1. elem = new Omega_eh(1. 4. sigma_r = 2. the_gh_array[node_order(1)](1) = gh_on_Gamma_h::Dirichlet. v[1] = 0. elem = new Omega_eh(0. f_r. } 420 Workbook of Applications in VectorSpace C++ Library . the_omega_eh_array. the_node_array. static const double v_ = 0. Omega_h& omega_h) { __initialization(df.0. the_node_array.0. ena[3] = 3. static const double K_ = lambda_ + 2. node = new Node(4.0. Omega_h::Omega_h() { double v[2]. 0. Node* node. static const double PI_=3.add(node).0+h_. 2. double sigma_r.add(node). Global_Discretization& gd) { return new ElasticAxisymmetricQ4(en. the_node_array.add(node).add(node). 4.add(node). v[1] = h_. node = new Node(5. } gh_on_Gamma_h::gh_on_Gamma_h(int df. Omega_eh* elem. ena[0] = 1. r = 1. ena). ena[0] = 0. the_gh_array[node_order(5)][0] = f_r / 2. the_node_array.0*PI_*r*h_*sigma_r.0.14159265359. r = 1. 2. v). the_node_array. the_node_array. 0. v). Global_Discretization&).add(node).Chapter 4 Finite Element Method Primer #include "include\fe.0/3. v[0] = r_+h_.0. node = new Node(2.add(elem). ena[2] = 4. node = new Node(3. } class ElasticAxisymmetricQ4 : public Element_Formulation { public: ElasticAxisymmetricQ4(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation *make(int. 2. node = new Node(1. 0. v). node = new Node(0. r. ena[1] = 2. v). 2.gd). int ena[4]. the_gh_array[node_order(3)][0] = f_r / 2. v[0] = r_+h_.8. v).0. v[0] = r_.h" static const double E_ = 1. the_gh_array[node_order(0)][0] = f_r / 2. ena[2] = 5.0. ena).0-h_. static const double mu_ = E_/(2*(1+v_)). ElasticAxisymmetricQ4(int. the_omega_eh_array. static const double h_=0. Global_Discretization&). v[0] = r_-h_.0. 2. v[0] = r_-h_. the_gh_array[node_order(2)][0] = f_r / 2.0 * mu_. v). ena[3] = 4. 0. Element_Formulation* ElasticAxisymmetricQ4::make(int en.add(elem).

0/3.0.0) || ~Wz ) & (~Wr || C0(0. b. 1. C0 m = VECTOR("int.0.0.det()).gh_on_gamma_h(). gh_on_Gamma_h gh(ndf. static ElasticAxisymmetricQ4 elasticaxisymmetricq4_instance(element_type_register_instance). {0. wr.0*PI_*(((~b) * ((K_*(m%m)) * b) * r) | dv). int.lhs())). (double*)0. J dv(d(x). 1. Element_Type_Register element_type_register_instance. wz &= w_x[0][1]. double d_0[4][4]={ {2.0. stiff &= stiff_vol + stiff_dev.u_h() = gd. Quadrature qp1(2.1. qp). 1. J dV(d(X). {0. Wz &= W_x[0][1]. Global_Discretization gd(oh. Wz. } Element_Formulation* Element_Formulation::type_list = 0. const double*". gh. Zai &= Z[0]. Matrix_Representation mr(gd). 0. mr.0}. int. N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int. B. 0. 2. gd.0)) & (~((H0)N)/R || C0(0. N[3] = (1-Zai)*(1+Eta)/4. eta &= z[1]. zai &= z[0]. Quadrature". eta. C0 D_0 = MATRIX("int.0.2. int. H1 x = n*xl. Omega_h oh. 4. double m_0[4] = {1.0.0.0. Wr.0. n[0] = (1-zai)*(1-eta)/4. n[1] = (1+zai)*(1-eta)/4. H1 X = N*xl. uh).u_h() = u. R. qp1).0} }. int. H0 nx = d(n) * d(x).0)) & (~Wz || ~Wr ). nx).0. Eta. R &= (H0)X[0].0. n[3] = (1-zai)*(1+eta)/4.0 r ∂N a ∂N a --------.0)) & (~((H0)n)/r || C0(0.inverse(). } 0 ∂N a --------∂z ∂N a --------. qp1). H0 W_x = INTEGRABLE_SUBMATRIX("int. b &= (C0(0. qp). 1. N[1] = (1+Zai)*(1-Eta)/4. nsd. m_0).--------∂z ∂r 2 D0 = 0 0 0 0 2 0 0 0 0 2 0 0 0 0 1 m = [1. Wr &= W_x[0][0]. wz. int main() { int ndf = 2. U_h uh(ndf. 2.rhs())) / ((C0)(mr.0 ∂r Ba = Na ----. 4. Nx). 0. 1.0}. 4. H0 w_x= INTEGRABLE_SUBMATRIX("int.0.inverse().0.assembly(). B &= (C0(0. r. N[0] = (1-Zai)*(1-Eta)/4. n[2] = (1+zai)*(1+eta)/4.0.0.m ⊗ m B b dΩ e j   3 Ω 1-point integration on T k vol = e iT ∫ Ba K ( m ⊗ m )B b dΩe j Ω Listing 4•18 Axisymmetrical patch test (project workspace file “fe. 1. nsd. 0. H1 z(2. 4. N[2] = (1+Zai)*(1+Eta)/4. project “axisymmetrical_patch_test” with Macro definition Workbook of Applications in VectorSpace C++ Library 421 . H0&". C0 stiff_dev = 2.0. H0 Nx = d(N) * d(X). Eta &= Z[1].0. C0 u = ((C0)(mr. 0]T 2 × 2 integration on 2 T k dev= e iT ∫ B a µ  D0 – -. 1).0*(m%m))) * B) * R) | dV). (double*)0. const double*". d_0[0]). H0&". gd) { Quadrature qp(2.0) || ~wz ) & (~wr || C0(0.dsw”. 4. 2. Global_Discretization& gd) : Element_Formulation(en. int.0}.0*PI_*(((~B) * ((mu_*(D_0-2.0}.0.0.u_h() << endl. Zai. gd. return 0.det()). n = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int.0)) & (~wz|| ~wr ). zai. {0. oh).0. 4). Quadrature". cout << gd. r &= (H0)x[0]. H1 Z(2.0.Two Dimensional Problems ElasticAxisymmetricQ4::ElasticAxisymmetricQ4(int en. wr &= w_x[0][0]. oh). C0 stiff_vol = 2.

the assumed solution is linear.750000 0.750000 0. E = 103. The successfulness of the selective reduced integration will be evident. The distortion factor is a static constant “d_” in the very beginning of the program. The common edge of the two elements is slanted with the distortion. respectively. The project “higher_order_patch_test” implemented program for the present test.666839 0. The eight-nodes and nine-nodes elements are activated by setting macro definition “__TEST_Q8” and “__TEST_Q9”. ν = 0.750000 9-nodes Quadrilateral 0. d=1 d d=0 Distortion d=0 d=0 d=1 d=1 d=2 d=2 Integration Points 3×3 2×2 3×3 2×2 3×3 2×2 8-nodes Quadrilateral 0.750000 0.744849 0. Three amount of element distortion away from rectangular shape (d = 0). Tip deflection of 8-nodes and 9-nodes quadrilaterals. which may reveal additional problems. Either eight-nodes or nine-nodes elements as shown in Figure 4•52. away from axes of Cartesian coordinates. and shown in Figure 4•52.750000 0. The tip deflection on the middle point of the left edge is listed in TABLE 4•6.676616 TABLE 4•6.788531 0. 422 Workbook of Applications in VectorSpace C++ Library . B.750000 0. the element shape sensitivity effect is considered for eight-nodes serendipity element and nine-nodes Lagrangian element.3 15 2 -15 10 d=2 d Figure 4•52 Beam subject to bending moement on the left. There is no new program implementation needed for the higher-order patch test.Chapter 4 Finite Element Method Primer Higher-Order Patch Test In the patch Test A. This is followed by robustness of an element formulation when the material becomes incompressible. The uniform reduced integration can be achieved by setting all qaudrature point to 2 × 2 in the program. and C.750000 0. We now study when the assumed solution is quadratic. Shape Sensibility: Consider two quadratic elements. In the first set of problem. denoted as “d”.750000 0.

. which both converge to value of ~0.75”. and W. and “__TEST_Q4_512”.56 comparing to “0. The results with various combinations of the options are shown in TABLE 4•7. For ν = 0. but also less stiff compared to 8nodes element with standard integration scheme. the solution shows significant locking without signs of convergence. For the present case 2 × 2 uniform reduced integration gives 3 × 8 = 24 independent relations. 164-165 in Bathe. The test suite is implemented in project “higher_order_q4” in project workspace file “fe.1 With uniform reduced integration (2 × 2). 167-169 in Zienkiewicz.75. when applied with the full integration.4999 in plane strain (with the same boundary value problem in Figure 4•52). Englewood Cliffs. and is shown in Figure 4•53.3 in plane stress and (2) ν = 0.L. it produces better result. The 9-nodes element has “total degree of freedom” = 15(nodes) × 2(dofs)4(constraints) = 26 > 24. 1976. can be set at compile time.Two Dimensional Problems The exact solution is “0. This is because the 9-nodes element is capable of reproducing arbitrary quadratic displacement of straight-edged quadrilateral while the 8-nodes element is not. Therefore. The accuracy of the solution collapses fast with the increasing amount of the distortion.4999. the accuracy of the 8-nodes element deteriorates fast when 9-nodes element is still capable of producing the exact solution.3. p. For total element number greater than 8. Inc. with the last numbers indicate the total element number. The lowest order of numerical integration required for convergence relaxes the stiffness and produces improved results. “The finite element method: basic formulation and linear problems”. p. K. the macro definitions “__TEST_Q4_32”. the reverse is true. under this integration scheme the 9-nodes element is rank deficient. O. The solution and its convergence are obtainable with the selective reduced integration schemes as shown in the last two columns. UK.dsw”. since it pass the consistency and stability parts of the patch test. Taylor. the nearly incompressible condition. in plane strain case. Workbook of Applications in VectorSpace C++ Library 423 . The options of (a) the selective reduce integration on the shear term of the deviatoric stiffness and (b) the volumetric stiffness are also tested. Prentice-Hall. Both the full integration and selective reduced integration on the offending shear treatment converge to exact solution of 0. and R. The displacement formulation usually leads to over-estimated stiffness. When the distortion occurs.. “ Numerical method in finite element analysis”. the corresponding macro definitions are “__SHEAR_SELECTIVE_REDUCED_INTEGRATION” and “__INCOMPRESSIBLE_ SELECTIVE_REDUCED_INTEGRATION”. McGraw-Hill.C..-J.5625” in mixed u-p formulation (ν = 0. For Poisson ratio ν = 0. 2. 1989. For the selective reduced integration on the offending shear terms and dilatational term in incompressible materials. New Jersey. This convergence is guaranteed by the patch test for the 4nodes bi-linear element. London. which is not only rank sufficient. “__TEST_Q4_128”. Therefore. in plane stress. Each integration point contribute to 3 independent relations from the definitions of 3 strain equations. the convergence is clear with increasing number of element used in the computation.2 Convergence of bilinear 4-node element: We show the convergence of bilinear 4-node element at (1) Poisson ratio ν = 0. the total degree of freedoms is 13 × 2-4 = 22 < 24. For the 8-nodes element. Wilson.L. The same problem is divided with successively finer meshes. The standard integration scheme (3 × 3) for 8-nodes and 9-nodes elements with no distortion both match the exact solution. 1.5 in Chapter 5). The successive results agree on more digits after the decimal points.

655087 0. _ ν = 0.564732 ν = 0.4999 (standard) 0.3 (standard) 0.00799228 0.4999 (selective reduced on dilatation) 0.097860 0.7443220 0.4999 in plane strain case to prevent dilatational locking.02823080 0.3 (selective reduced on shear) 1.09621180 ν = 0.Chapter 4 Finite Element Method Primer 8 elements 32 elements 128 elements 512 elements Figure 4•53 Mesh refinement of 4-nodes quadrilateral element.666701 0.964387 0.570704 0.772274 0.584802 0.lements 8 32 128 512 TABLE 4•7. 424 Workbook of Applications in VectorSpace C++ Library .7295910 0. Convergence of four node bi-linear element with selective reduced integration on offending shear terms to prevent shear-locking ν = 0.595018 0.00271635 0.4999 (selective reduced on shear & dilatation) 0.6920159 0.568095 Number of E.3 in plane stress case.755571 ν = 0.7485400 ν = 0. and selective reduced integration on volumetric terms when the Possion ratios ν = 0.839392 0.

u ≡ ∂x/∂t. Taylor. For monatomic gas λ' = -2µ/3. λ' div u .520 in Zienkiewicz and R.= ----. A popular treatment for the incompressible condition is to use penalty method where the pressure variable is eliminated by taking p = – λ div u Eq.-------------------.5 Stokes Flow For a fluid particle with density ρ and velocity u relative to an inertial frame of reference. In most applications. Inc. grad u ≡ ∂u/∂x. λ.+ ----. Du ρ ------. “p”. As discussed earlier (see page 409).Two Dimensional Problems 4. 4•238 into Eq. 4•236 where divergence of interal stresses. and τ is the viscous stress. 1991.e. t ) .. 4th ed. p. in which u(x. 4•146 that σ = –p I+τ where p is the pressure.. 4•236. 4•239 Now λ and µ are equivalent to the Lamé constants in elasticity. 4•237 and viscous stress of Eq. 4•236 can be expressed as in Eq. 4•240 to approximate the nearly incompressible condition. t) can be differentiated with respect to time “t” (by first applying the Lebniz rule. The constitutive equations is τ = 2µ def u + λ' I div u Eq. and then the chain rule. the penalty parameter.+ u • grad u ∂t ∂t ∂x ∂t Dt Eq.L. and f is the body force. and λ' is the second viscosity (this term gives the deviatoric stress caused by the volumetirc deformation which is a process attributed to molecular relaxation). In the penalty method in the stokes problem. 4•237 where we have applied the definitions of the velocity. I is the unit tensor. is nearly completely negligible compared to the pressure. on the second term of the Lebniz rule) ∂u ∂u ∂u ∂x Du ( x. is usually taken as λ = (107~1010) µ Eq.1 Substituting Eq. Workbook of Applications in VectorSpace C++ Library 425 .= ----.= div Dt σ+f Eq. i. The Newton’s second law of motion requires the linear momentum of the fluid particle is equal to the forces applied to it. div σ. McGraw-Hill. “The finite element method”. equals the external surface force. and the velocity gradient. we have the Navier-Stokes equation 1.. 2. The Du/Dt in the left-hand-side is the fluid particle in Lagragian (material) description. UK. The stress in the first term of the right-hand-side of Eq. d(xy) = x dy + y dx. and it can be proved as the lower bound for λ' thermodynamically. near the incompressible condition K ≈ λ >> µ. 4•238 where µ is the fluid viscosity. d f(x) / dt = (df / dx) (dx / dt). vol.3.----.

and k vol ≅ e iT ∫ Ba D λ Bb dΩe j where D = λ λ 0 .R. in the computation.≡ Re div ( 2µ def u ) µ Eq. 97 in Tritton.≈ ---------. “ Physical fluid dynamics”. The Eq.Chapter 4 Finite Element Method Primer ∂u ρ ----. of Computational Physics. Journal. 4•244 Therefore. and A.. 4•147 for elasticity. µ now plays the role of fluid viscosity instead of the shear modulus G in elasticity. “Review of finite element analysis of incompressible viscous flows by the penalty function formulation”. The finite element formulation in the last section for plane elasticity can be applied to the stokes flow problem without modification. λ is now the penalty parameter we take λ = 108 µ. 2.. vol. 4•242 can be simplified to grad p = div ( 2µ def u ) + f Eq. For steady incompressible viscous fluid. 4•146 and Eq. D µ = 0 2µ 0 λ Ω Ω 0 0 µ 0 0 0 Eq. 426 Workbook of Applications in VectorSpace C++ Library . and λ = 108 µ for the penalty method. 4•242. 1-60. Brooks. Oxford University Press. 1979. p.J. it is the velocity in the stokes flow. and certainly with the selective reduced integration for the volumetric term. T. Eq. Oxford. the Reynolds number (denoted as Re) is the dynamic similarity of the inertia force “ ρu • grad u “ to the viscous force “div(2µ def u)” as1 ρUL ρu • grad u ----------------------------------------. UK.K. 4•243 At very low Reynolds number (Re << 1) the inertia force is negligible compared to the viscous force. 2nd ed. λ ≈ K . Liu. 4•246 1.J. 4•245 Since at the incompressible limit. Considering the B-matrix formulation for plane elasticity 2 T T k dev e iT ∫ B a µ  D 0 – -. The physical interpretation is different in that instead of regarding u as the displacement. 1988. the Navier-Stokes equation simplifies to ρu • grad u + grad p = div ( 2µ def u ) + f Eq. 1. and k vol = e iT ∫ B a K ( m ⊗ m )B b dΩe j =   3 Ω Ω Eq.. W. the resultant equation is completely identical to Eq. 4•241 We have dropped out the second viscosity λ' and use the identity that “div(p I) = grad p”. 4•140 with the constitutive equation of Eq. D.m ⊗ m B b dΩ e j . no. 30. p. see Hughes.+ ρu • grad u + grad p = div ( 2µ def u ) + f ∂t Eq. 4•242 From Eq. 4•245 becomes2 2µ 0 0 λλ0 T T k dev ≅ e iT ∫ B a D µ B b dΩ e j .

the second term corresponding to the Couette flow induced by the relative motion of the two bounding plates.dsw”.cpp” as a separate compilation unit. 4•244 from the superposition of two solutions of the viscous flow induced by the pressure gradient and by the bounding plates separately.cpp” is the implementation very close to of Lagrangian 9-node element for plane elasticity. is less accurate compared to the solution for the plane Couette flow. quadratic in nature. The exact solution are shown in solid curves. U=1 p = GL = 40. UK. The plane Couette flow can be activated by setting macro definition “__TEST_PLANE_COUETTE_FLOW” and the plane Poiseuille flow can be activated by setting macro definition “__TEST_PLANE_POISEUILLE_ FLOW”.K. p. That is the first term corresponding to the Poiseuille flow caused by the applied horizontal pressure gradient. To emphasize its relation to plane elasticity. 1967. as a dependent source file for this project. The distance between two rigid plates is “d” with the pressure gradient from the entrance of the flow to the exit as -∇p = G. In these test cases. and the Poiseuille flow provides an assumed higher-order (quadratic) solution. “An introduction to fluid dynamics”. The default is a combined flow with both pressure gradient applied on the entrance and relative motion of bounding plates. the Couette flow provides an assumed linear solution. Workbook of Applications in VectorSpace C++ Library 427 . which is linear. in the project “plane_couette_poiseuille_flow” in project workspace file “fe. 4•247 This solution can be derived from Eq. We notice that the solution for the plane Poiseuille flow.y ( d – y ) + -----2µ d Eq. The results of the computation are shown in Figure 4•55.. Program Listing 4•19. The viscosity of the fluid is µ. The “elasticq9. The velocity profile can be expressed as a function of y coordinate1 G Uy u ( y ) = ----.Two Dimensional Problems Plane Couette-Poiseuille Flow Consider a plane uni-directional flow (v = w = 0) drives by both pressure gradient (the Poiseuille flow) and relative motion (U) of two bounding plates (the Couette flow) as shown in Figure 4•54 . G. is implemented for these tests. 182 in Batchelor. 1. we use “elasticq9. The finite element solutions are shown in dashed curves with arrows to indicate the velocity profiles in the middle of the channel to avoid the entrance and exit effects. µ=1 p=0 d=1 L = 10 Figure 4•54 Plane Couette-Poiseuille flow problem. Cambridge University Press.

8 0.4 0.8 1 Plane Couette-Poiseuille Flow Figure 4•55 Plane Couette flow and plane Poiseuille flow.6 0.2 0.5 Plane Couette Flow + Plane Poiseuille Flow 1 0.4 0.6 0.2 0.Chapter 4 Finite Element Method Primer 1 0.2 0.2 0.2 0.4 0. The exact solutions are shown in solid curves the finie element solutions are shown in dashed curves with arrows.8 0.4 0.6 0.6 0. The finite element solutions are velocity profiles taken from the middle of the channel to avoid entrance and exit effect.6 0.4 0.4 0. 428 Workbook of Applications in VectorSpace C++ Library .3 0.1 0.8 1 0.8 0.2 1 0.

H0 W_x = INTEGRABLE_SUBMATRIX("int.pow(2)). 2*mu_. 1. Global_Discretization&). Zai &= Z[0]. 2.pow(2))*(1-Zai)-N[8])/2. B &= (~Wx|| C0(0.0. N[1] = (1+Zai)*(1-Eta)/4. C0 D_lambda = MATRIX("int.0. C0 stiff_dev = ((~B) * (D_mu * B)) | dV.0}.pow(2))*(1+eta)-n[8])/2. zai. n[6] = ((1-zai. wy. d_mu[0]). 3. C0 stiff_vol = lambda_* ( +( wx*~wx*U[0][0]+wx*~wy*U[0][1] +wy*~wx*U[1][0]+wy*~wy*U[1][1] ) | dv).0)) & (C0(0. 0. wx.0. nen).pow(2)). wx. class ElasticQ9 : public Element_Formulation { public: ElasticQ9(Element_Type_Register). ElasticQ9(int. nsd. N[0] -= (N[4]+N[7])/2.0e8*mu_. int. Wy. {0. H1 X = N*xl. Wy. qp1). n[4] = ((1-zai. H0 Nx = d(N) * d(X). n[5] = ((1-eta. Wy &=W_x[0][1]. Dµ = 0 0 0 Ω λλ0 2µ 0 0 0 2µ 0 0 0 µ T k dev ≅ e iT ∫ B a D µ B b dΩ e j . lambda_. 0. #else C0 e = BASIS("int". J dV(d(X). mu_} }. Wx. Nx). H0 w_x = INTEGRABLE_SUBMATRIX("int. N[5] = ((1-Eta. Quadrature". C0 D_mu = MATRIX("int. N[0] -= N[8]/4. n[2] -= (n[5]+n[6])/2. Eta. H1 x = n*xl. U = (e%e)*(E%E).0. 0. N[8] = (1-Zai. Quadrature qp1(2. Global_Discretization& gd) : Element_Formulation(en.0) || ~wy ) & (~wy || ~wx). C0 stiff_dev = mu_* ( +( ((2*Wx*~Wx)+(Wy*~Wy))*((e[0]%e[0])*(E%E))+(Wy*~Wx) *((e[0]%e[1])*(E%E)) +(Wx*~Wy) *((e[1]%e[0])*(E%E))+((2*Wy*~Wy)+(Wx*~Wx))*((e[1]%e[1])*(E%E)) ) | dV).gd). 4). int. Eta &= Z[1]. E = BASIS("int". wx &= w_x[0][0]. Nx). n[3] -= n[8]/4. 3. N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int.0}. 0. Zai. 3.pow(2))*(1+zai)-n[8])/2. nsd. eta &= z[1].pow(2))*(1-Eta. #endif stiff &= stiff_vol + stiff_dev. 3. N[0] = (1-Zai)*(1-Eta)/4.0. H1 Z(2. n[2] -= n[8]/4.pow(2))*(1+Zai)-N[8])/2. 0. n[1] -= n[8]/4. n[1] = (1+zai)*(1-eta)/4.0} }. int. qp1). } ElasticQ9::ElasticQ9(int en. int. b &= (~wx|| C0(0. Global_Discretization&). Quadrature".inverse(). n[0] -= (n[4]+n[7])/2. N[4] = ((1-Zai.0. N[2] -= N[8]/4. ndf). lambda_. } a separate source file “elasticq9. H0 nx = d(n) * d(x). C0 stiff_vol = ((~b) * (D_lambda * b)) | dv. n[3] -= (n[6]+n[7])/2. double d_mu[3][3] = { {2*mu_. H0&".h" static const double mu_ = 1. wx &= w_x[0][0].det()). (double*)0. N[6] = ((1-Zai. N[1] -= N[8]/4. N[3] -= N[8]/4.pow(2))*(1+Eta)-N[8])/2. nsd. 0.0}. int. H0 w_x = INTEGRABLE_SUBMATRIX("int. const double*". n[7] = ((1-eta. }. wy. 2. 1. 0. n[2] = (1+zai)*(1+eta)/4. Global_Discretization& gd) { return new ElasticQ9(en. {lambda_. wy &= w_x[0][1].det()). qp). n[1] -= (n[4]+n[5])/2.pow(2))*(1-Eta)-N[8])/2. {0. b. Element_Formulation *make(int.0. 9. N[2] = (1+Zai)*(1+Eta)/4.0)) & (C0(0. 9). 0. n = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int. nsd. N[7] = ((1-Eta. 9. const double*". Wx &=W_x[0][0]. d_lambda[0]). wy &= w_x[0][1]. H1 z(2. H0&".pow(2))*(1-zai)-n[8])/2. n[8] = (1-zai. n[3] = (1-zai)*(1+eta)/4.cpp” taken from plane elasticity problem penalty parameter is λ = 108 µ 3x3 integration 9-node Lagrangian shape functions 2x2 reduced integration 9-node Lagrangian shape functions B-matrix formulation for incompressiblility Dλ = λ λ 0 . H0 W_x = INTEGRABLE_SUBMATRIX("int. T k vol ≅ e iT ∫ B a D λ Bb dΩe j Ω standard λ−µ formulation Workbook of Applications in VectorSpace C++ Library 429 . gd) { Quadrature qp(2.pow(2))*(1-eta)-n[8])/2. int. n[0] = (1-zai)*(1-eta)/4. zai &= z[0]. H0&". 1. B. 1. nx). J dv(d(x). n[0] -= n[8]/4.inverse(). #if defined(__TEST_B_MATRIX_FORMULATION) double d_lambda[3][3] = { {lambda_. ElasticQ9::ElasticQ9(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation* ElasticQ9::make(int en. static const double lambda_ = 1. N[1] -= (N[4]+N[5])/2. Wx &= W_x[0][0]. Wy &= W_x[0][1]. qp). H0&". N[3] -= (N[6]+N[7])/2.Two Dimensional Problems #include "include\fe. eta.0}.pow(2))*(1-eta. nx). (double*)0. int. int.0) || ~Wy) & (~Wy|| ~Wx ). {0. N[3] = (1-Zai)*(1+Eta)/4. N[2] -= (N[5]+N[6])/2. Wx.

} define nodes define elements B.0. the_gh_array[node_order(i*row_node_no)][0] = p_*weight[i]*c_. omega_h). 1. static const int col_element_no = (col_node_no-1)/2. ena[6] = ena[3] + 1. int main() { int ndf = 2. i < col_element_no. 1. fnn = i * 2 * row_node_no + j * 2. } double weight[9] = { 0.add(elem). the_node_array.0.125. U_h uh(ndf. ena[0] = fnn. Element_Type_Register element_type_register_instance. } #elif defined(__TEST_PLANE_COUETTE_FLOW) for(int i = 0. v[0] = ((double)j)*h_e_.u_h() << endl. static const int col_node_no = 9. static ElasticQ9 stokesq9_instance(element_type_register_instance).0. ElasticQ9(int.0. }. 3. the_gh_array[node_order((col_node_no-1)*row_node_no+i)](1) = gh_on_Gamma_h::Dirichlet.0. j < row_node_no. i++) { the_gh_array[node_order(i*row_node_no)](0) = gh_on_Gamma_h::Neumann. Element_Formulation *make(int. } #else for(int i = 0.0.0. the_gh_array[node_order((col_node_no-1)*row_node_no+i)][0] = 1. static const double h_e_ = L_/((double)row_segment_no). 1. uh). i++) for(int j = 0.} double weight[9] = { 0. 0. i++) { the_gh_array[node_order(i)](0) = the_gh_array[node_order(i)](1) = the_gh_array[node_order((col_node_no-1)*row_node_no+i)](0) = the_gh_array[node_order((col_node_no-1)*row_node_no+i)](1) = gh_on_Gamma_h::Dirichlet.0 }. j++) { int nn = i*row_node_no+j. Omega_h& omega_h) { __initialization(df. Global_Discretization&). gh_on_Gamma_h gh(ndf.0. 0. i < col_node_no -1.Chapter 4 Finite Element Method Primer #include "include\fe. 1. Omega_h::Omega_h() { double v[2]. the_gh_array[node_order(i*row_node_no)][0] = p_*weight[i]*c_. Global_Discretization gd(oh. int ena[9]. #if defined(__TEST_PLANE_POISEUILLE_FLOW) for(int i = 0. v[1] = ((double)i)*c_. static const double mu_ = 1. static const int row_segment_no = row_node_no-1. 0. 1. i < row_node_no. i++) { the_gh_array[node_order(i*row_node_no)](0) = gh_on_Gamma_h::Neumann.0.u_h() = gd.0. the_gh_array[node_order((col_node_no-1)*row_node_no+i)][0] = 1.0.u_h() = u. 9. } for(int i = 0.0.assembly(). Node* node = new Node(nn. the_omega_eh_array. 2. Global_Discretization&). for(int i = 1. Element_Formulation* Element_Formulation::type_list = 0.0. } } gh_on_Gamma_h::gh_on_Gamma_h(int df. ena[1] = fnn + 2. i++) { the_gh_array[node_order(i)](0) = the_gh_array[node_order(i)](1) = the_gh_array[node_order((col_node_no-1)*row_node_no+i)](0) = gh_on_Gamma_h::Dirichlet. ena[7] = ena[0] + row_node_no. ena[5] = ena[1] + row_node_no. gd.C. 1. Omega_h oh.0.lhs())). 3.gh_on_gamma_h(). i < col_node_no. C0 u = ((C0)(mr.0/2. Poiseuille flow only open integration rule (see Numerical Reciepe) Couette flow only Poiseuille & Couette flow declare “ElasticQ9” class register “ElasticQ9” as element # 0 solution phase Listing 4•19 Plane Couette-Poiseuille flow in project “plane_couette_poiseuille_flow” 430 Workbook of Applications in VectorSpace C++ Library .0/2.rhs())) / ((C0)(mr. i++) { the_gh_array[node_order(i)](0) = the_gh_array[node_order(i)](1) = the_gh_array[node_order((col_node_no-1)*row_node_no+i)](0) = the_gh_array[node_order((col_node_no-1)*row_node_no+i)](1)=gh_on_Gamma_h::Dirichlet. i < row_node_no.h" static const int row_node_no = 7. j++) { int en = i * row_element_no + j.0. gd.0. cout << gd. gh. elem = new Omega_eh(en. i++) for(int j = 0. static const double p_ = 40. i < row_node_no. for(int i = 0. 1. for(int i = 1. 3. 1.0 }. static const double L_ = 10.0e8*mu_.0. oh).0/2. 3. mr. static const double lambda_ = 1. oh). 1. ena[3] = ena[0] + 2*row_node_no. static const double c_ = 0.0. Omega_eh *elem.0. 1. static const int row_element_no = (row_node_no-1)/2. } #endif } class ElasticQ9 : public Element_Formulation {public: ElasticQ9(Element_Type_Register). Matrix_Representation mr(gd). v).0/2. ena[2] = ena[1] + 2*row_node_no. i < col_node_no -1.0. ena).add(node).0. return 0. ena[8] = ena[4]+row_node_no. ena[4] = ena[0] + 1. j < row_element_no. 0.

is implemented for this computation. The bottom and the two sides are rigid-walls. New York. 462-465 in J. 1) u = 4(1-x)x (1. which is known as forced convection. Prentice-Hall. “The finite element method: linear static and dynamic finite element analysis”.Two Dimensional Problems Driven Cavity Flow1 The stokes flow in a square cavity is shown in Figure 4•56a. The top horizontal velocity boundary conditions vanish at the two top corners.J. Again. p. “Applied functional analysis and variational methods in engineering”. The penalty method (λ = 108 µ) is used with selective reduced integration and the 9-nodes Lagrangian quadrilateral element. such as corner node treatments described in p. (b) velocity vectors. McGraw-Hill.2 Program Listing 4•20. New Jersey. 1) x (1. the “elasticq9.dsw”.R.. Inc. Inc. which are to avoid the difficulty in defining boundary conditions at these two corner nodes. Englewood Cliffs. 1986.0) (b) (a) Figure 4•56(a) Flow in square cavity with sixteen 9-nodes Lagrangian elements. Workbook of Applications in VectorSpace C++ Library 431 .. (0.. T. The velocity vectors are shown in Figure 4•56b. 2. y (0. the project “square_cavity_flow” in project workspace file “fe. Reddy.231 in Hughes.N. The top is a boundary with velocity given as u(x) = 4(1-x)x. This velocity boundary condition causes a convecting current in the cavity.cpp” is a separate compilation unit for this project. 0) 1.

1.Chapter 4 Finite Element Method Primer #include "include\fe. static const int row_element_no = (row_node_no-1)/2.e8 * mu_. 1. Omega_h::Omega_h() { double x[4][2] = {{0.u_h() = gd. oh). int control_node_flag[4] = {1. u = v = 0 left. the_gh_array[node_order(nn)](0) = gh_on_Gamma_h::Dirichlet.gh_on_gamma_h(). double x = ((double)i)*h_e_. 4. row_node_no.u_h() << endl. Global_Discretization&). v = 0 declare “ElasticQ9” class register “ElasticQ9” as element # 0 solution phase Listing 4•20 Driven cavity flow (in project: “square_cavity_flow” in project workspace file “fe. i++) { int nn = (col_node_no-1)*row_node_no+i. static const int col_node_no = 9.h" static const int row_node_no = 9.0.0}}.0.dsw”. u = 4(1-x)x.u_h() = u. Matrix_Representation mr(gd). Omega_h& omega_h) { __initialization(df.lhs())). oh). i < col_node_no. } for(int i = 0. i < row_node_no-1.0 * (1. 1}. Global_Discretization&). 1. Omega_h oh. i < row_node_no-1. col_node_no. i < col_node_no. C0 u = ((C0)(mr. x[0]). for(int i = 0. cout << gd. mr. control_node_flag.). } gh_on_Gamma_h::gh_on_Gamma_h(int df.0}. 432 Workbook of Applications in VectorSpace C++ Library . } } class ElasticQ9 : public Element_Formulation { public: ElasticQ9(Element_Type_Register).0}. static const int col_element_no = (col_node_no-1)/2.C. } right. }. static const double h_e_ = 1. block(this. gh_on_Gamma_h gh(ndf. {0. EP::element_pattern EP::ep = EP::LAGRANGIAN_9_NODES. static const double v_e_ = 1. u = v = 0 bottom. i++) { the_gh_array[node_order((i+1)*row_node_no-1)](0) = the_gh_array[node_order((i+1)*row_node_no-1)](1) = gh_on_Gamma_h::Dirichlet. } for(int i = 1. 0. uh). gd.0/((double)col_element_no*2). int main() { int ndf = 2. static const double lambda_ = 1. ElasticQ9(int. {1. gh.0-x) * x. Element_Formulation *make(int. U_h uh(ndf. Element_Type_Register element_type_register_instance.0/((double)row_element_no*2). } for(int i = 1. {1. 1.assembly().0}.rhs())) / ((C0)(mr.. static const double mu_ = 1.0. the_gh_array[node_order(nn)][0] = u. gd. u = 4. forced B. i++) { the_gh_array[node_order(i*row_node_no)](0) = the_gh_array[node_order(i*row_node_no)](1) = gh_on_Gamma_h::Dirichlet. static ElasticQ9 stokesq9_instance(element_type_register_instance).0. 0. omega_h).0. Element_Formulation* Element_Formulation::type_list = 0. i++) { the_gh_array[node_order(i)](0) = the_gh_array[node_order(i)](1) = gh_on_Gamma_h::Dirichlet. u = v = 0 top. Global_Discretization gd(oh. the_gh_array[node_order(nn)](1) = gh_on_Gamma_h::Dirichlet. return 0.

Each lamina. it has often been argued that since the plate bending is the subject in the fourth-order differential equation that has been most extensively studied in finite element. The experiences gained in the plate finite element analysis may serve as an important example for solving other fourth-order partial differential equation.0 ∂x ∂ θ = – z 0 ----. that εz is almost negligible. uα = uα0-θαz. UK.. u = u0-θxz.x = – zL θ ∂y θ y ∂ ∂ ----. Sy). Workbook of Applications in VectorSpace C++ Library 433 .L. xβ) does not vary with thickness (z = [-t/2.g.----. 1991. However. w = w0. uβ = uβ0-θβz. 4•248 The membrane bending strains can be expressed as1 ∂ ----. (b) the shear forces (Sx.∂y ∂x ε εx = εy γ xy Eq. “fiber”. t/2]). McGraw-Hill.6 Plate Bending Problems The plate theory probably is only to interest the structural engineers. Inc. My). or w = w0 Eq. e. The displacements can be expressed as. which is parallel to the mid-surface.. 2. 4•249 (a) γα: shear strain (b) w = w0 fiber midsurface θα uα = uα0-θαz Sy My Myx Mxy Sx Mx Figure 4•57 (a) the displacements of plate under deformation.8 in Zienkiewicz and R. We also assumed. remain plane under deformation (see Figure 4•57a). the normal momenets (Mx. and the twisting moments (Mxy. vol. inconsistent to the plane stress assumption.3. such as the biharmonic equation of general physical interests. σz = 0. “The finite element method”. is assumed to be under plane stress. 1. so w(xα. v = v0-θyz.Two Dimensional Problems 4. Basic Plate Theory The basic assumption of the plate is that the plane sections. Myx) of a plate. 4th ed.. Taylor. p.

M x Sx 0 ∂x ∂y = . 4•26 and Eq. and ∇ T S + q = 0 Eq. is defined as 1 ν 0 Et 3 ν 1 0 D = ------------------------12 ( 1 – ν 2 ) 1–ν 0 0 ----------2 Eq. the normal moments (Mx. and the correction factor β = -. 4•252 The shear forces [Sx.0 ----.M xy ∂y ∂x ∂ ∂ Sx + qx = 0 ----. Eq.Chapter 4 Finite Element Method Primer and the transverse shear strains [γx. and My + Sy 0 ∂ ∂ . 5 Parallel to the equilibrium equations.----. assumed plane stress. we have in plate bending problem L T M + S = 0. 4•250 From the Figure 4•57b. 4•254 or we can express their components explicitly in matrix form as ∂ ∂ ----.is for rectangular homogeneous section with parabolic shear 6 stress distribution. γy] as γx γy θx θy ∂w -----∂x + = – θ + ∇w ∂w -----∂y γ = = – Eq.----. 4•253 where α = βGt. 4•27 for 1-D beam bending problem.0 ----. 4•255 434 Workbook of Applications in VectorSpace C++ Library . 4•251 where D. My) and the twisting moment (Mxy) are Mx M = My M xy t -2 = –∫ t – -2 σx σ y z dz = D L θ τ xy Eq. Sy] are S = Sx Sy = βGt ( – θ + ∇w ) ≡ α ( – θ + ∇w ) Eq.∂x ∂y S y qy 0 Eq.

n = 1.32... 4•257. use Eq.0 ∂x ∂ L∇ = 0 ----∂y ∂ ∂ ----. where m. 4•258 becomes the well-known classical biharmonic equation1 ∂4w ∂4w ∂4w 12 ( 1 – ν 2 ) --------. 4•256 to Eq.538 in Timoshenko. the transverse shear strains are all zero.∂y ∂x ∂2 -------∂x 2 ∂2 -------∂y 2 ∂2 2 -----------∂x∂y ∂ ----∂x = ∂ ----∂y Eq. 4•251 to substitute M in Eq. we get ( L∇ ) T D L∇w – q = 0 Eq. we get –∇ T L T M + q = 0 Eq.Two Dimensional Problems Kirchhoff (Thin-) Plate Theory and Finite Element Formulation—C1 Continuity Requirement In thin plate theory. with thin plate assumption. 4•260 The homogeneous solution for a simply supported rectangular plate with lengths of “a” and “b” has the simple form of nπy mπx w = cos  ---------. we assume that the fiber remains normal to the mid-surface during deformation. and from Eq. the Eq.N.----.g. The B-matrix is defined as B a = ( L∇ )Na Eq. Workbook of Applications in VectorSpace C++ Library 435 . 1970. 4•257 Then. 4•250. …  a   b  Eq. γ = 0 . and substitute θ. and J. we have the combined operator “L∇” as ∂ ----. S. 4•259.. we identify θ = ∇w Eq. 4•262 1. 3rd ed. Airy’s stress function satisfies the biharmonic equation as described in p. 4•261 The finite element formulation is obtained by substituting element shape function (Na) into Eq. “ Theory of elasticity”. 5. e. θ = ∇w in Eq. 4•256 Substituting first part of Eq.= 0 ∂x 2 ∂y 2 ∂y 4 Et 3 ∂x 4 Eq.P. 3. 4•258 From the definition of operators L and ∇. and p. That is.Therefore. 4•254 into the second part of it. 4•256. Goodier. cos  --------- . McGraw-Hill Book Company.– q ------------------------.+ --------. 4•259 For constant D.+ 2 ----------------.

For each of four nodes on the corner of the rectangle (a = 0. 4•269 436 Workbook of Applications in VectorSpace C++ Library . and θ ya =  -----. . 4•251 and Eq. we have the moment vector as ˆ Me = D Ba we a h a Eq. 4•263 ˆ where w e is the nodal deflection vector. i. and q = ndf (b-1)+j ke = ke i ∫ a b j Ω Eq. 1. 4•173. pq iajb = e T B T D B dΩe . The element stiffness matrix has no difference from Eq. with p = ndf (a-1) + i.. 4•264 Nonconforming Rectangular Plate Element (12 degrees of freedom) The nodal variables for this four-node rectangular element is wa ˆa ˆ u e ≡ θ xa ˆ θ ya Eq. 4•265 where ∂w ∂w ˆ ˆ θ xa =  – -----. 4•268 Notice that the polynomial is not complete up to the third-order. 2.Chapter 4 Finite Element Method Primer From Eq.  ∂y  a  ∂x  a Eq. we have twelve equations wa ˆ θ xa = ˆ θ ya ≡ Ca α 2 2 3 2 2 3 3 3 α 0 + α 1 x a + α 2 y a + α 3 x a + α 4 x a y a + α 5 y a + α 6 x a + α 7 x a y a + α 8 x a y a + α 9 y a + α 10 x a y a + α 11 x a y a 2 2 3 2 – α 2 – α 4 x a – 2α 5 y a – α 7 x a – 2α 8 x a y a – 3α 9 y a – α 10 x a – 3α 11 x a y a 2 2 2 3 α 1 + 2α 3 x a + α 4 y a + 3α 6 x a + 2α 7 x a y a + α 8 y a + 3α 10 x a y a + α 11 y a Eq.e. 4•256. 4•266 The nonconforming element defines a 12-terms polynomial for the deflection “w” as w = α0 + α1 x + α2 y + α3 x2 + α4 xy + α5 y2 + α6 x3 + α7 x2y + α8 xy2 + α9 y3 + α10 x3y + α11 xy3 ≡ Pα Eq. 4•267 where P = 1 x y x 2 xy y x 3 x 2 y xy 2 y 3 x 3 y xy 3 2 Eq. 3).

.1 The results are shown in TABLE 4•8. Inc. 4•273 The Program Listing 4•21 implements the generic procedure in the above to derive the nonconforming shape function (Eq. “The finite element method”. The plate is clamped at four sides and with uniform unit loading. 1991. 31 in Zienkiewicz. 4•273) for the thin-plate bending rectangular element. 1.C. The maximum deflection is at the center of the plate. The exact solution is computed from formula provided in p. The exact solution is 226800. 4 × 4 (= 16) elements are used in the computation. UK. Taylor. respectively (see Figure 4•58a). C is a 12 × 12 matrix. respectively. 3.. or at the lower-left corner of the finite element model. and ∂ w/ ∂ x =0. 4•264 are then taken to define the B-matrix and the stiffness matrix. Only a quarter (upper-right) of the plate is modeled due to the symmetry of the geometry and the boundary conditions.Two Dimensional Problems where Ca is defined as 2 2 1 xa ya xa x a ya ya 3 2 xa xa ya 2 x a ya 3 ya 3 xa y a 3 xa y a Ca ≡ 0 0 –1 0 – x a – 2y a 0 0 2 2 3 2 – x a – 2x a y a – 3y a – x a – 3x a y a 2 ya Eq. and reference therein. At the right and the top edges of the model the boundary conditions are w = ∂ w/ ∂ x = ∂ w/ ∂ y = 0 (clamped). 2. O.L. which shows the convergence toward the exact solution when the mesh size is refined. and R. 4•272 where the shape function is N = PC-1 Eq. 4•271 B = ( L∇ )PC – 1 Eq. 4•269 as α Therefore. Workbook of Applications in VectorSpace C++ Library 437 . Mesh 2×2 4×4 8×8 16 × 16 Exact Center Deflection 251691 234449 229464 228186 226800 TABLE 4•8. 4•262 and Eq. 4•270 0 1 0 2x a y a 2 3x a 2x a y a 0 2 3x a y a 3 ya for a = 0. Therefore. Eq. At the bottom and the left edges are taken as ∂ w/ ∂ y =0. vol. 4th ed. McGraw-Hill. 1. The vector α can be obtained by inverting Eq.. The solution of the vertical deflection is shown in Figure 4•58b. the B-matrix is defined as ˆa = C–1 ue Eq. 2. Center deflection.

{ H2 z(2.0. Quadrature qp(2. D_*v_.C. 1. gd) { int ndf = 3.0}. Omega_h::Omega_h() { double coord[4][2] = {{0. H0 dx_inv. eta. i < row_node_no-1.inverse(). TRUE. qp). PlateR4::PlateR4(int en. row_node_no. 4. C0 D = MATRIX("int. omega_h). i < row_node_no-1. n[2] = (1+zai)*(1+eta)/4. 3. J dv(d(X).0}}. Global_Discretization& gd) : Element_Formulation(en. i <= row_node_no.0. } gh_on_Gamma_h::gh_on_Gamma_h(int df.0. D_. 0. } } class PlateR4 : public Element_Formulation { public: PlateR4(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation *make(int. 0. zai. {0.C. }. Dv[0]).3) / (12. (double*)0. 0. 4/*nen*/. i++) { the_gh_array[node_order(row_node_no*(row_node_no-1)+i)](0) = gh_on_Gamma_h::Dirichlet. i++) the_gh_array[node_order(i*row_node_no)](2) = gh_on_Gamma_h::Dirichlet. for(int i = 1. 1. {1. 16). Global_Discretization&).C.C. for(int i = 0. Element_Formulation* PlateR4::make(int en.0*(1-pow(v_. int. n[0] = (1-zai)*(1-eta)/4. zai &= z[0]. 2/*nsd*/.h" static row_node_no = 5. int control_node_flag[4] = {TRUE. PlateR4(int. static const double Dv[3][3] = { {D_.0. the_gh_array[node_order(i*row_node_no-1)](2) = gh_on_Gamma_h::Dirichlet.0. X &= n*xl. 3.0. n[3] = (1-zai)*(1+eta)/4. {1.0. static const double t_ = 0. control_node_flag.0}. the_gh_array[node_order(i*row_node_no-1)](1) = gh_on_Gamma_h::Dirichlet. 0. bottom B. eta &= z[1]. static const double D_ = E_ * pow(t_.0 }. D_*(1-v_)/2.gd). row_node_no. i < row_node_no-1. } static const double E_ = 1. n = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE( "int. 0. {D_*v_. int.0 }. TRUE}. } dx_inv &= d(X). static const double v_ = 0.Chapter 4 Finite Element Method Primer #include "include\fe. w = ∂ w/ ∂ x = ∂ w/ ∂ y =0 1 ν 0 Et 3 ν 1 0 D = ------------------------12 ( 1 – ν 2 ) 1–ν 0 0 ----------2 coordinate transformation rule 438 Workbook of Applications in VectorSpace C++ Library . block(this.det()). ∂ w/ ∂ x =0 top B. const double*". Global_Discretization& gd) { return new PlateR4(en. the_gh_array[node_order(row_node_no*(row_node_no-1)+i)](1) = gh_on_Gamma_h::Dirichlet. Quadrature". i++) the_gh_array[node_order(i)](1) = gh_on_Gamma_h::Dirichlet. qp).25. H2 X. EP::element_pattern EP::ep = EP::QUADRILATERALS_4_NODES.0} }.2))). TRUE. } for(int i = 0.01.0}. {0. i++) { the_gh_array[node_order(i*row_node_no-1)](0) = gh_on_Gamma_h::Dirichlet. w = ∂ w/ ∂ x = ∂ w/ ∂ y =0 right B. for(int i = 0. the_gh_array[node_order(row_node_no*(row_node_no-1)+i)](2) = gh_on_Gamma_h::Dirichlet. ∂ w/ ∂ y =0 left B. n[1] = (1+zai)*(1-eta)/4. coord[0]). Global_Discretization&). Omega_h& omega_h) { __initialization(df.

C_sub = SUBMATRIX("int. x3 = x. Quadrature". 2. 2. C). stiff &= ((~B) * (D * B)) | dv. P[8] = x*y. x2 = x.rhs())) / ((C0)(mr. i < row_node_no. { H2 x = X[0]. for(int i = 0. j < row_node_no.u_h() = u. H0 Nxx = INTEGRABLE_MATRIX("int. H0 w_xx = INTEGRABLE_SUBMATRIX("int. Quadrature". zero = C0(0. Eta. y3 = y. Zai.Two Dimensional Problems { H2 Z(2. Omega_h oh.pow(2). 4•270 for C-matrix C-1 P= 1 x y x 2 xy y x 3 x 2 y xy 2 y 3 x 3 y xy 3 2 N = PC-1 B a = ( L∇ )Na. gh_on_Gamma_h gh(ndf. P[10] = x. } } Element_Formulation* Element_Formulation::type_list = 0.gh_on_gamma_h().0.u_h() = gd. (double*)0). } for(int i = 0. i++) { C0 x(xl[i][0]). P[9] = y. double f_0 = 1. oh). and L∇ = iajb = e T B T D B dΩe ke i ∫ a b j Ω ∂2 -------∂x 2 ∂2 -------∂y 2 ∂2 2 -----------∂x∂y Listing 4•21 Plate bending using nonconformming rectangular element (project workspace file “fe. i < nen. int.0). qp).0*(~w_xx[0][1])).dsw”.pow(3). 12. } C0 C_inv = C. 2/*nsd*/. i++) N[i] = P * C_inv(i).pow(3)*y. y2 = y.pow(3). gd.inverse(). static Element_Type_Register element_type_register_instance.pow(2). nen*ndf/*nenxndf*/. H0&". P[3] = x. Workbook of Applications in VectorSpace C++ Library 439 . H2 P = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE( "int.assembly(). mr.0*y)|zero |(-x2) |(-2*x*y)|(-3*y2)|(-x3) |(-3*x*y2) ) & (zero|one |zero|(2*x)|y |zero |(3*x2)|(2*x*y) |y2 |zero |(3*x2*y)|y3 ). Global_Discretization gd(oh. force &= (((H0)N)*f_0) | dv. int main() { int ndf = 3. P[1] = x.0. -dw/dy.pow(2). 3. i++) for(int j = 0.pow(2). P[11] = x*y. cout << "[w. oh).pow(2). (double*)0. gd. y = X[1]. y(xl[i][1]). qp).0). Eta &= Z[1]. 2*nen*ndf. for(int i = 0. int. int. C0 u = ((C0)(mr. Quadrature". N = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE( "int.pow(3). Nxx). int.lhs())). 12/*nenxndf*/.pow(3). i < nen*ndf. 12. C0 C(12. static PlateR4 plate_r4_instance(element_type_register_instance). for(int i = 0. P[7] = x.pow(3). i++) w_xx(i) = (~dx_inv) * dd(N)(i) * dx_inv. } Shape functions N Eq. int. P[6] = x. P[0] = 1. j++) cout << "#" << (i*row_node_no+j) << ": " << gd.pow(2)*y. 2. U_h uh(ndf. 2/*nsd*/. project “rectangular_plate_bending” with Macro definition “__GENERIC_NONCONFORMING_SHAPE_FUNCTION” set at compile time). return 0. qp). i < 12. C0&". P[5] = y. qp). Zai &= Z[0]. P[4] = x*y. C_sub(i) = ( one | x | y | x2 | (x*y) | y2 | x3 |(x2*y) | (x*y2) | y3 | (x3*y) | (x*y3) ) & (zero|zero|-one|zero |(-x)|(-2. H0 B = (~w_xx[0][0]) & (~w_xx[1][1]) & (2. P[2] = y. gh. one = C0(1. dw/dx]:" << endl. Matrix_Representation mr(gd).u_h()[i*row_node_no+j] << endl. uh).

and reference therein. Inc. vol. qp). 2/*nsd*/.0. (-1. 440 Workbook of Applications in VectorSpace C++ Library . Zai. Eta &= Z[1]. 1)}. “The finite element method”.0 (a) (b) Figure 4•58 Clamped boundary conditions and nodal deflections for rectangular plate bending elements (4 × 4 mesh are shown) using non-conformming shape function. Zai &= Z[0].. is straight forward as 1 2 3 4 5 6 H2 Z(2. (double*)0. ηa] = {(-1. -1). to be substituting in Eq. O. 4•262 with ( ξξ a + 1 ) ( ηη a + 1 ) ( 2 + ξξ a + ηη a – ξ 2 – η 2 ) – b η a ( ξξa + 1 ) ( ηη a + 1 ) 2 ( ηη a – 1 ) aξa ( ξξa + 1 ) 2 ( ξξa – 1 ) ( ηη a + 1 ) 1 N a ≡ -8 Eq. 4•274 where “2a” and “2b” are the lengths of a rectangular element. and the nodal normalized coordinates are [ξa. Implementation of Eq.L. we can substitute the explicit shape functions1 in Eq. int. qp). N = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE( "int. Taylor. Quadrature". 1991.C. 4•262.0 5 4 3 1.Chapter 4 Finite Element Method Primer w = ∂w/∂x = ∂w/∂y =0 ∂w/∂x = 0 200000 0 150000 0 100000 00 50000 00 0 1 2 3 4 51 2 ∂w/∂y =0 1. see p. (1. double a = fabs( ((double)(xl[0][0]-xl[1][0])) )/2. 1. McGraw-Hill. -1). 17 in Zienkiewicz. 2. nen*ndf/*nenxndf*/. 4•274. 4th ed. Alternatively. (1.. Eta. UK. 1). and R.

Two Dimensional Problems
7 b = fabs( ((double)(xl[2][1]-xl[1][1])) )/2.0, 8 zai[4] = {-1.0, 1.0, 1.0, -1.0}, eta[4] = {-1.0, -1.0, 1.0, 1.0}; 9 for(int i = 0; i < nen; i++) { 10 N[i*ndf] = (Zai*zai[i]+1)*(Eta*eta[i]+1)*(2+Zai*zai[i]+Eta*eta[i]-Zai.pow(2)-Eta.pow(2))/8.0; 11 N[i*ndf+2] = a*zai[i]*(Zai*zai[i]+1).pow(2)*(Zai*zai[i]-1)*(Eta*eta[i]+1)/8.0; 12 N[i*ndf+1] = -b*eta[i]*(Zai*zai[i]+1)*(Eta*eta[i]+1).pow(2)*(Eta*eta[i]-1)/8.0; 13 } On the other hand, the Eq. 4•272 is quite generic especially when no one is deriving an explicit formula like Eq. 4•274 for us. The computation is done on the same project (“rectangular_plate_bending” in project workspace file “fe.dsw”) with macro definition “__EXPLICIT_NONCONFORMING_SHAPE_FUNCTION” set at compile time. The solutions is certainly identical to the one with generic procedure for computing the shape function.

Conforming Rectangular Plate Element (16 degrees of freedom)
Instead of Eq. 4•265, we can extend the nodal variables to
wa  ∂w ----- ∂y  a  ∂w ----- ∂x  a ∂2 w  ------------  ∂x∂y a

ˆa ue ≡

Eq. 4•275

with four nodes at each corner of the rectangle we have totally 16 degree of freedoms. Therefore, a complete third-order polynomial can be used to represent the deflection w, with P defined as
P = 1 x y x 2 xy y x 3 x 2 y xy 2 y 3 x 3 y x 2 y xy 3 x 3 y x 2 y 3 x 3 y 3
2 2 2

Eq. 4•276

and parallel to the derivation of Eq. 4•269, we have
2 2 3 2 2 3 3 2 2 3 3 2 3 1 xa ya xa xa ya ya xa x a ya x a ya y a x a ya xa ya xa ya xa y 2a x a y 3a xa y 3a

Ca ≡

0 0 1

0

x a 2y a 0

2 xa

2 2x a y a 3y a 2 ya

3 xa

2 2 3 2 3 2x a y a 3x a y a 2x a y a 3x a y 2 a 3x a y 2 a 3 ya 2 2 3x a y 2 a 2x a y 3 a 3x a y 3 a 2 2 2 6x a y a 6x a y a 9x a y 2 a

0 1 0 2x a y a 0 0 0 0 1

2 0 3x a 2x a y a

2 2 0 3x a y a 2x a y a

Eq. 4•277

0

0

2x a

2y a

0

2 2 3x a 4x a y a 3y a

Eq. 4•276 and the inverse of Eq. 4•277 can be substituted in Eq. 4•272 to define the B-matrix. The explicit shape functions for the conforming rectangular element, , is defined as

Workbook of Applications in VectorSpace C++ Library

441

Chapter

4 Finite Element Method Primer
( ξ + ξ a ) 2 ( ξξ a – 2 ) ( η + η a ) 2 ( ηη a – 2 ) 1 – a ξ a ( ξ + ξ a ) 2 ( ξξ a – 1 ) ( η + η a ) 2 ( ηη a – 2 ) N a ≡ ----16 – b ( ξ + ξ ) 2 ( ξξ – 2 )η ( η + η ) 2 ( ηη – 1 ) a a a a a abξ a ( ξ + ξ a ) 2 ( ξξ a – 1 )η a ( η + η a ) 2 ( ηη a – 1 )

Eq. 4•278

where “2a” and “2b” are the lengths of the rectangular element, and the subscript a = 0, 1, 2, 3 are the nodal numbers (developed by Bogner et al.1,2). The same project “rectangular_plate_bending” can be used with macro definition “__EXPLICIT_CONFORMING_SHAPE_FUNCTION” set at compile time for using Eq. 4•278, or no macro definition set at compile time for its generic counterpart via Eq. 4•277. The results of center deflection of the conforming rectangular plate are shown in TABLE 4•9. .

Mesh 2×2 4×4 8×8 16 × 16 Exact

Center Deflection 363735 275114 242597 232124 226800

TABLE 4•9. Center deflection.

Triangular Plate Element (9 degrees of freedom)
For triangular element we use the area coordinates L0, L1, and L2. The polynomial has 9-terms to match the 9-dof on the three corner nodes. Therefore, P can be defined as3
2 2 2 P = L0 L 1 L2 L0 L1 L 1 L 2 L2 L0 L 0 L 1 L1 L 2 L 2 L0

Eq. 4•279

Three third order terms are chosen in addition to the first six complete second order terms. The explicit shape function for the first node is (with cyclic permutation of 0, 1, 2 for other two nodes)

1. see p. 49 in Zienkiewicz and R.L. Taylor, 1991, “The finite element method”, 4th ed., vol. 2. McGraw-Hill, Inc., UK, and reference therein. 2. see also p. 419, Table 9.1 for the “Hermite cubic element” in Reddy, J.N., 1993, “An introduction to the finite element method”, 2nd ed., McGraw-Hill, Inc., New York. 3. see p. 244 in Zienkiewicz, O.C., 1977, “The finite element method”, 3rd ed., McGraw-Hill, Inc., UK.

442

Workbook of Applications in VectorSpace C++ Library

Two Dimensional Problems
2 2 2 2 L0 + L 0 L1 + L 0 L2 – L 0 L1 – L0 L2

1  2    2 1 N 0 ≡ b 2  L 0 L 1 + -- L 0 L 1 L 2 – b 1  L 2 L 0 + -- L 0 L 1 L 2 2 2 1 2 2 1 c 2  L 0 L 1 + -- L 0 L 1 L 2 – c 1  L 2 L 0 + -- L 0 L 1 L 2     2 2

Eq. 4•280

where b0 = y1- y2, and c0 = x2-x1. The explicit shape function for the triangular element can be implemented as 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 H2 L(2, (double*)0, qp), N = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE( "int, int, Quadrature", 9, 2, qp), L0 = L[0], L1 = L[1], L2 = 1.0 - L0 - L1; double b0 = (double)(xl[1][1]-xl[2][1]), c0 = (double)(xl[2][0]-xl[1][0]), b1 = (double)(xl[2][1]-xl[0][1]), c1 = (double)(xl[0][0]-xl[2][0]), b2 = (double)(xl[0][1]-xl[1][1]), c2 = (double)(xl[1][0]-xl[0][0]); N[0] = L0+L0.pow(2)*L1+L0.pow(2)*L2-L0*L1.pow(2)-L0*L2.pow(2); N[1] = b2*(L0.pow(2)*L1+L0*L1*L2/2.0)-b1*(L2*L0.pow(2)+L0*L1*L2/2.0); N[2] = c2*(L0.pow(2)*L1+L0*L1*L2/2.0)-c1*(L2*L0.pow(2)+L0*L1*L2/2.0); N[3] = L1+L1.pow(2)*L2+L1.pow(2)*L0-L1*L2.pow(2)-L1*L0.pow(2); N[4] = b0*(L1.pow(2)*L2+L0*L1*L2/2.0)-b2*(L0*L1.pow(2)+L0*L1*L2/2.0); N[5] = c0*(L1.pow(2)*L2+L0*L1*L2/2.0)-c2*(L0*L1.pow(2)+L0*L1*L2/2.0); N[6] = L2+L2.pow(2)*L0+L2.pow(2)*L1-L2*L0.pow(2)-L2*L1.pow(2); N[7] = b1*(L2.pow(2)*L0+L0*L1*L2/2.0)-b0*(L1*L2.pow(2)+L0*L1*L2/2.0); N[8] = c1*(L2.pow(2)*L0+L0*L1*L2/2.0)-c0*(L1*L2.pow(2)+L0*L1*L2/2.0); // area coordinates // shape functions

// first node

// second node

// third node

Program Listing 4•22 implements the 9-dof triangular plate bending problem. The project “triangle_plate_bending” in project workspace file “fe.dsw” implements the 9-dofs triangular element for plate bending problem. The maximum deflection, for a (4 × 4) × 2 triangular mesh, is 205175.

Workbook of Applications in VectorSpace C++ Library

443

C.25. qp). row_node_no.l1.01. D_.C. l2 = 1. row_node_no. 1. const double*". int. {0. PlateT3(int. for(int i = 0. {1. } static const double E_ = 1. 4. { H1 l(2. w = ∂ w/ ∂ x = ∂ w/ ∂ y =0 1 ν 0 Et 3 ν 1 0 D = ------------------------12 ( 1 – ν 2 ) 1–ν 0 0 ----------2 coordinate transformation rule 444 Workbook of Applications in VectorSpace C++ Library . i < row_node_no-1. 0. l1 = l[1]. w = ∂ w/ ∂ x = ∂ w/ ∂ y =0 top B. n[2] = l2.l0 .inverse(). 0. for(int i = 1.0 } }. 0.3) / (12. n[1] = l1.0. 3.2))).Chapter 4 Finite Element Method Primer #include "include\fe. static const double Dv[3][3]={ {D_. block(this. i < row_node_no-1. static const double v_ = 0. 0.det()). D_*(1-v_)/2. bottom B.0. qp). Global_Discretization&). omega_h). 2.C. (double*)0. n[0] = l0.0}. D_*v_.0. Element_Formulation* PlateT3::make(int en.gd). . int.0}. i++) { the_gh_array[node_order(row_node_no*(row_node_no-1)+i)](0) = the_gh_array[node_order(row_node_no*(row_node_no-1)+i)](1) = the_gh_array[node_order(row_node_no*(row_node_no-1)+i)](2) = gh_on_Gamma_h::Dirichlet. }. i <= row_node_no. for(int i = 0. 3. i < row_node_no-1. X &= n*xl.0.0 . EP::element_pattern EP::ep = EP::SLASH_TRIANGLES. C0 D = MATRIX("int. l0 = l[0].0*(1-pow(v_. TRUE. Quadrature".0. Global_Discretization&). TRUE}. 1. 3. } } class PlateT3 : public Element_Formulation { public: PlateT3(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation *make(int. H1 X.0 }. i++) the_gh_array[node_order(i)](1) = gh_on_Gamma_h::Dirichlet. Dv[0]). Global_Discretization& gd) { return new PlateT3(en. } dx_inv &= d(X). {D_*v_. gd) { int ndf = 3. } for(int i = 0. control_node_flag. {0. static const double t_ = 0. Omega_h& omega_h) { __initialization(df.0. Global_Discretization& gd) : Element_Formulation(en. n = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int. Omega_h::Omega_h() { double coord[4][2] = {{0. } gh_on_Gamma_h::gh_on_Gamma_h(int df. i++) { the_gh_array[node_order(i*row_node_no-1)](0) = the_gh_array[node_order(i*row_node_no-1)](1) = the_gh_array[node_order(i*row_node_no-1)](2) = gh_on_Gamma_h::Dirichlet. {1. PlateT3::PlateT3(int en.0 }. static const double D_ = E_ * pow(t_. 0.C. TRUE.0}.0}}. H0 dx_inv. 16).∂ w/ ∂ y =0 left B.0.h" static row_node_no = 5. ∂ w/ ∂ x =0 right B. coord[0]). J dv(d(X). Quadrature qp(2. int control_node_flag[4] = {TRUE. i++) the_gh_array[node_order(i*row_node_no)](2) = gh_on_Gamma_h::Dirichlet.

pow(2)*L0+L0*L1*L2/2.u_h() = gd. dw/dx]:" << endl. Quadrature".pow(2)+L0*L1*L2/2.pow(2)*L2+L0*L1*L2/2.pow(2)-L2*L1. N[7] = b1*(L2. static Element_Type_Register element_type_register_instance. } } Element_Formulation* Element_Formulation::type_list = 0. H0 w_xx = INTEGRABLE_SUBMATRIX("int. Workbook of Applications in VectorSpace C++ Library 445 . } shape functions N b0 = y1. j++) cout << "#" << (i*row_node_no+j) << ": " << gd. N[1] = b2*(L0.pow(2). N[2] = c2*(L0. H0 Nxx = INTEGRABLE_MATRIX("int. Global_Discretization gd(oh.pow(2)*L0-L1*L2. b2 = (double)(xl[0][1]-xl[1][1]).0. double b0 = (double)(xl[1][1]-xl[2][1]). j < row_node_no.gh_on_gamma_h(). stiff &= ((~B) * (D * B)) | dv.0).pow(2)*L2+L1. L1 = L[1]. C0 u = ((C0)(mr.0). qp). N = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE( "int. static PlateT3 plate_t3_instance(element_type_register_instance). i++) for(int j = 0.0)-c0*(L1*L2.0). uh).assembly().pow(2)+L0*L1*L2/2. and c0 = x2-x1. 2. (double*)0. int main() { int ndf = 3. Nxx). gh_on_Gamma_h gh(ndf. i < nen*ndf. Quadrature". L2 = 1. qp).0*(~w_xx[0][1])).pow(2)*L1+L0.pow(2)+L0*L1*L2/2. N[3] = L1+L1. gd. i < row_node_no. c0 = (double)(xl[2][0]-xl[1][0]).0)-b1*(L2*L0.Two Dimensional Problems { H2 L(2. c1 = (double)(xl[0][0]-xl[2][0]). mr. oh).dsw”. b1 = (double)(xl[2][1]-xl[0][1]).L0 . force &= (((H0)N)*f_0) | dv.0 . 2. Matrix_Representation mr(gd).pow(2)*L1+L0*L1*L2/2. return 0. gh. H0&".u_h() = u. N[5] = c0*(L1. L0 = L[0]. Omega_h oh.rhs())) / ((C0)(mr. explicit shape functions ∂2 -------∂x 2 Ba = ( L∇ )N a .0). 2*nen*ndf.y2.0)-c2*(L0*L1. int.pow(2)*L2-L0*L1. and L∇ = iajb = e T B T D B dΩe ke i ∫ a b j Ω ∂2 -------∂y 2 ∂2 2 -----------∂x∂y Listing 4•22 9 dof triangular plate bending using nonconformming rectangular element (project workspace file “fe.pow(2)*L0+L0*L1*L2/2. N[4] = b0*(L1.pow(2)-L1*L0. for(int i = 0.0)-c1*(L2*L0. project “triangular_plate_bending”). for(int i = 0. i++) w_xx(i) = (~dx_inv) * dd(N)(i) * dx_inv.L1. H0 B = (~w_xx[0][0]) & (~w_xx[1][1]) & (2. qp).u_h()[i*row_node_no+j] << endl.pow(2)*L1-L2*L0.pow(2)*L2+L0*L1*L2/2. N[8] = c1*(L2.pow(2)+L0*L1*L2/2.lhs())). 9.pow(2)+L0*L1*L2/2.pow(2). U_h uh(ndf. int. N[6] = L2+L2.0). c2 = (double)(xl[1][0]-xl[0][0]). cout << "[w.0)-b0*(L1*L2.pow(2)*L1+L0*L1*L2/2.pow(2)*L0+L2. -dw/dy.0).pow(2). 2. N[0] = L0+L0.0)-b2*(L0*L1. oh). 2. gd.pow(2)-L0*L2. int.pow(2)+L0*L1*L2/2. double f_0 = 1.

4•281 A triangular element can be conceived with six degree of freedoms. Inc. we have w0 = α0. 1991. µ 1 = -------------.. 2. 4•283 where l0 is the length of the edge opposing to node number “0”. and R. w2 = α2 The normal derivatives to the node number “3” can be obtained according to the formula1 l0 ∂ ∂ ∂ ∂ ∂ ∂  ----. vol.+ µ  -------. Parallel to the derivation of Eq.-------. 4•284 Similarly we can define for the other two normal derivatives ( ∂ ⁄ ∂n ) 4 and ( ∂ ⁄ ∂n ) 5 .Chapter 4 Finite Element Method Primer Morley’s Triangular Plate Element (6 degrees of freedom) A complete quadratic polynomial has only six terms as P = L0 L 1 L2 L0 L1 L 1 L 2 L2 L0 Eq. with three deflection variables “w” on the corner nodes and three normal derivatives “ ∂ w/ ∂ n” on the three middle points of the triangle sides as depicted in Figure 4•59. 4•282 Eq. w2 (∂w/∂n)4 (∂w/∂n)3 w0 (∂w/∂n)5 w1 Figure 4•59 Morley’s six degrees of freedom triangular element.+ -------. p.. and µi is defined as 2 2 2 2 2 2 l 2 – l1 l0 – l2 l1 – l 0 µ 0 = -------------.. L1. 446 Workbook of Applications in VectorSpace C++ Library .– 2 -------. 4•269 for a generic shape function. and L2 are 1.– -------- 0  ∂L  ∂n 3 ∂L 1 4∆ ∂L 1 ∂L 2 ∂L0 2 Eq. Taylor. 4th ed. The derivatives of “Pα” with respect to L0. O.27 in Zienkiewicz. = -----. w1 = α1. “The finite element method”. and µ 2 = -------------2 2 2 l0 l1 l2 Eq. ∆ is the area of the triangle.L.C. UK.. McGraw-Hill.

.[ – 2α + ( 1 – µ )α + ( 1 + µ )α – α + α – α ] -----1 1 2 1 0 4 5 3  ∂n  4 4∆ l2 ∂w  -----.[ – 2α + ( 1 – µ )α + ( 1 + µ )α – α + α – α ] -----0 0 1 0 2 3 4 5  ∂n  3 4∆ l1  ∂w = -----.= α 2 + α 4 L 1 + α 5 L 0 ∂L 2 Eq. we have l0  ∂w = -----. pq iajb = e T B T D B dΩe ke = ke i ∫ a b j Ω Eq. 4•285 Therefore.[ – 2α + ( 1 – µ )α + ( 1 + µ )α – α + α – α ] 2 2 0 0 1 5 3 4  ∂n  5 4∆ Eq.Two Dimensional Problems ∂(P α) ---------------. C can be expressed as 1 0 0 – 2l 0 --------C≡ 4∆ l1 ( 1 + µ 1 ) -----------------------4∆ l2 ( 1 – µ2 ) ----------------------4∆ 0 1 0 l0 ( 1 – µ0 ) ----------------------4∆ – 2l 1 --------4∆ l2 ( 1 + µ2 ) -----------------------4∆ 0 0 1 l0 ( 1 + µ0 ) -----------------------4∆ l1 ( 1 – µ1 ) ----------------------4∆ – 2l 2 --------4∆ 0 0 0 0 0 0 0 0 0 –1 1 – 1 –1 –1 1 1 –1 – 1 Eq. 4•288 Recall it has been defined with a particular choice ∂w ∂w ˆ ˆ θ x = – -----. 4•290 Workbook of Applications in VectorSpace C++ Library 447 . 4•264. 4•286 Therefore. = -----. using Eq.= ( – n y )θ x + n x θ y ∂n ∂x ∂y Eq. 4•283.= α 0 + α 3 L 1 + α 5 L 2 ∂L 0 ∂(P α) ---------------.+ n y -----. 4•289 ˆ ˆ that improves the symmetry of plate theory equations. 4•287 The shape function is defined as N = PC-1.= n x -----.= α 1 + α 3 L 0 + α 4 L 2 ∂L 1 ∂( Pα) ---------------. and θ y = -----∂y ∂x Eq. The relation of θ n to θ x and θ y can be expressed as ∂w ∂w ∂w ˆ ˆ θ n = -----. We can still use the definition of stiffness matrix from Eq.

Program Listing 4•23 implements the Morley’s 6-dof triangular plate element. the B-matrix corresponding to θ n -dof is multiplied with a factor “(nx . 4•289. 448 Workbook of Applications in VectorSpace C++ Library . Therefore. The result of the computation are shown in TABLE 4•10. Center Deflection of Morley’s triangular plate element. ny]T is the outward unit surface normal on the mid-side node.Chapter 4 Finite Element Method Primer where n = [nx.ny)” to take care of the conventional choice in Eq. No.. of Elements (4 × 4) × 2 (8 × 8) × 2 (16 × 16) × 2 Exact Center Deflection 125704 165518 192789 226800 TABLE 4•10.

add(node). the_omega_eh_array. static const double D_ = E_ * pow(t_. ena[2] = ena[1]-2. } for(int i = 0. ∂ w/ ∂ n =0 left B. ∂ w/ ∂ n =0 right B. j < row_segment_no. v). const double*". omega_h).0. 2. Dv[0]). i < row_node_no-1. for(int i = 0. ena).C. the_omega_eh_array. i+=2) { the_gh_array[node_order(row_node_no*(row_node_no-1)+i)](0) = the_gh_array[node_order(row_node_no*(row_node_no-1)+i+1)](0) = gh_on_Gamma_h::Dirichlet.C. int en = i*row_segment_no*2+j*2. {D_*v_. 0. j++) { int nn = i*row_node_no+j.0*(1-pow(v_. } class PlateMorley6 : public Element_Formulation { public: PlateMorley6(Element_Type_Register a) : Element_Formulation(a) {} Element_Formulation *make(int.C. 0. i < row_node_no-1. } static const double E_ = 1. i+=2) the_gh_array[node_order(i*row_node_no)](0) = gh_on_Gamma_h::Dirichlet.0} }. w = ∂ w/ ∂ n =0 top B. bottom B. ena[3] = ena[2] + 1. int ena[6]. 0. D_*(1-v_)/2. ena[1] = nn+2. ena[0] = nn. 0. for(int i = 0. 0. i+=2) the_gh_array[node_order(i)](0) = gh_on_Gamma_h::Dirichlet. ena[5] = ena[0] +1. static const double v_ = 0. int. } } gh_on_Gamma_h::gh_on_Gamma_h(int df. i++) for(int j = 0.gd).0. Global_Discretization& gd) { return new PlateMorley6(en. v[0] = (double)j/(double)(row_node_no-1). static const double Dv[3][3] = { {D_. 0. i++) for(int j = 0. i < row_node_no-1. C0 D = MATRIX("int. ena[0] = nn. }. {0. 6.Two Dimensional Problems #include "include\fe.0 }. i < row_segment_no. Omega_h::Omega_h() { int row_segment_no = (row_node_no .2))).0. Global_Discretization&). 0. ena[2] = ena[1] + row_node_no*2.add(elem).25. v[1] = (double)i/(double)(row_node_no-1). } the_gh_array[node_order(row_node_no*row_node_no-1)](0) = gh_on_Gamma_h::Dirichlet. i < row_node_no. i < row_node_no. D_*v_.01. ena). static const double t_ = 0.0 }. 6. double v[2]. 3. elem = new Omega_eh(en+1. Omega_h& omega_h) { __initialization(df. Element_Formulation* PlateMorley6::make(int en. the_node_array.ν 1 0 12 ( 1 – ν 2 ) 1–ν 0 0 ----------2 Workbook of Applications in VectorSpace C++ Library 449 . j++) { int nn = i*row_node_no*2+j*2.3) / (12. w = ∂ w/ ∂ n =0 1 ν 0 Et 3 D = ------------------------. Node* node = new Node(nn.1)/2. Global_Discretization&).add(elem). Omega_eh* elem = new Omega_eh(en. ena[5] = ena[4]+1. for(int i = 1. } for(int i = 0. ena[3] = ena[1] + row_node_no. ena[1] = ena[0]+row_node_no*2+2. i+=2) { the_gh_array[node_order(i*row_node_no-1)](0) = the_gh_array[node_order((i+1)*row_node_no-1)](0) = gh_on_Gamma_h::Dirichlet.h" static row_node_no = 9. for(int i = 1.C. D_. 3. ena[4] = ena[3] -1. j < row_node_no. PlateMorley6(int. ena[4] = ena[0]+row_node_no.

C0 C = ( C0(1.2))/pow(l_1. Global_Discretization gd(oh. qp). L0 = L[0].l1. (double*)0. } } Element_Formulation* Element_Formulation::type_list = 0. n[2] = l2. uh).0) | C0(0. L1. d3 = l_0/(4. i++) N[i] = P * C_inv(i). 0.0) | C0(0. l1 = l[1].0) ) & (d3*( C0(-2. H0&". P[4] = L1*L2. n[1] = l1.0+mu_1) | C0(-2. l_1 = (double)norm(xl[2]-xl[0]). P[0] = L0.dsw”. 2. C0 C_inv = C. Nxx).0) | C0(0.L1. mu_1 = (pow(l_0. 6/*nenxndf*/. project “morley_plate_bending”). H0 dx_inv = d(X). for(int i = 0. j < row_node_no.0 . mu_0 = (pow(l_2. 2. H0 w_xx = INTEGRABLE_SUBMATRIX("int.0) )). return 0.0) ) & ( C0(0.0 .lhs())).l0 . int.0) | C0(-1. 2/*nsd*/.0+mu_2) | C0(-2.0) )) & (d5*( C0(1.inverse(). Global_Discretization& gd) : Element_Formulation(en. qp). } stiff &= ((~B) * (D * B)) | dv. i < 3. gh_on_Gamma_h gh(ndf. gh. int. H0 Nxx = INTEGRABLE_MATRIX("int. gd) { Quadrature qp(2. Quadrature".rhs())) / ((C0)(mr.2).0*area). (double*)0. Quadrature".0) | C0(0. int.0) | C0(0.2))/pow(l_2.0) | C0(1. mr.0) | C0(1. 0). l2 = 1. int.0). qp). H0 B = (~w_xx[0][0]) & (~w_xx[1][1]) & (2.2)-pow(l_0. oh). L2 = 1. gd. H1 l(2.2).0) | C0( 1. 16).0-mu_0) | C0(1.0) | C0(0. int. P[3] = L0*L1.0-mu_1) | C0(-1.2))/pow(l_0. qp).0) | C0(0.0) | C0(0. double l_0 = (double)norm(xl[1]-xl[2]).Chapter 4 Finite Element Method Primer coordinate transformation rule shape functions N natural coordinate L0. for(int i = 0. C0 x = MATRIX("int.0) | C0( 1. qp).0) | C0(-1. mu_2 = (pow(l_1.0) | C0(0. 2.u_h()[i*row_node_no+j] << endl. B(i+3) = B(i+3)*(nx-ny). Quadrature".0-mu_2) | C0(1. Omega_h oh. static PlateMorley6 plate_m6_instance(element_type_register_instance).2)-pow(l_1.assembly().0) | C0(-1.gh_on_gamma_h(). n[0] = l0.2).0) | C0(0. int main() { int ndf = 1. l0 = l[0]. xl. 2. P[5] = L2*L0. P[1] = L1. d5 = l_2/(4. 3. 2*nen*ndf. int.0) | C0(1.0*area). and L∇ = ∂2 -------∂x 2 ∂2 -------∂y 2 ∂2 2 -----------∂x∂y B (nx-ny) for θn . C0&. C0 t = xl[next]-xl[i]. l_2 = (double)norm(xl[0]-xl[1]).det()/2.0*area). C0 u = ((C0)(mr. } C C-1 N = PC-1 B a = ( L∇ )N a .0) | C0(0.2)-pow(l_2. Quadrature".0. to be compatible with stiffness matrix that is defined for ∂w ∂w ˆ ˆ θ x = – -----. unit = 1. force &= (((H0)N)*f_0) | dv. for(int i = 0. int. L2 PlateMorley6::PlateMorley6(int en. for(int i = 0. qp). 2. i < 6. L1 = L[1].0. n = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int. 3. i < nen*ndf.0) | C0(-1. int". { H2 L(2. H2 P = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE( "int. i++) w_xx(i) = (~dx_inv) * dd(N)(i) * dx_inv.0) | C0(1. Matrix_Representation mr(gd). 6. H0 unit(qp).u_h() = u. 2. and θ y = -----∂y ∂x iajb = e T B T D B dΩe ke i ∫ a b j Ω ˆ ˆ θ n = ( – n y )θ x + n x θ y Listing 4•23 Morley’s 6-dof triangular plate bending(project workspace file “fe.0) )) & (d4*( C0(1.0) | C0(0.. J dv(d(X). 450 Workbook of Applications in VectorSpace C++ Library .L0 . P[2] = L2. i++) for(int j = 0.0) | C0(0.0*(~w_xx[0][1])). H1 X = n*x. gd. i++) { int next = ((i == 2)? 0 : i+1). N = INTEGRABLE_VECTOR_OF_TANGENT_OF_TANGENT_BUNDLE( "int. d4 = l_1/(4. C0 nx = t[1]. ny = -t[0]. U_h uh(ndf.inverse(). t = t/norm(t).0+mu_0) | C0(-1. static Element_Type_Register element_type_register_instance.0) | C0(1. j++) cout << "#" << (i*row_node_no+j) << ": " << gd. double area = (double)(unit | dv). double f_0 = 1.u_h() = gd.0) ) & ( C0(0. oh). i < row_node_no.