Sie sind auf Seite 1von 172

CHAPTER

Five Advanced Finite Element Methods

5.1 Mixed and Hybrid Finite Element Methods


C1-continuity requires complicated shape function as for the plate bending problem in the end of the last
chapter. Mixed formulation reduced the order of differential equation and therefore its continuity requirement.
The mixed formulation makes the shape function much easier to be constructed. Mixed method is also important
in theoretical consideration that sometimes highly efficient but simple tricks in finite elements, for example, the
selective reduced integration method, can be better understood in their equivalent mixed methods.

5.1.1 Heat Conduction

Mixed Formulation
Eq. 4•96 to Eq. 4•98 from Chapter 4 is re-written for convenience as

∇•q = f Eq. 5•1

q = – κ ∇T Eq. 5•2

T = g on Γ g , and q • n = h on Γ h , Eq. 5•3

Eq. 5•1 states that the divergence of the heat flux, “q”, is equal to the internal heat source, f. Eq. 5•2 is the Fourier
law of heat conduction which assumed that the heat flux is linearly related to the negative gradient of the temper-

Workbook of Applications in VectorSpace C++ Library 451


Chapter 5 Advanced Finite Element Methods
ature, “T”. The κ (= κ δij for isotropy) is the thermal diffusivity matrix. These two equations are subjected to the
essential and natural boundary conditions T = g on Γ g , and –q • n = h on Γ h , respectively (in Eq. 5•3). n is
the outward unit surface normal at Γh. The inner product of the heat flux, “q”, with the surface normal, “n”, pro-
duces the projected component of heat flux in the direction of the outward unit surface normal, “n”. The
weighted residual statement for Eq. 5•1 with the natural boundary conditions is

∫ wT ( ∇•q – f ) dΩ – ∫ wT ( q • n – h ) dΓ = 0 Eq. 5•4


Ω Γh

Integrating by parts and applying divergence theorem on the first term yields

∫ ∇wT q dΩ + ∫ f dΩ – ∫ wΓ h dΓ = 0 Eq. 5•5


Ω Ω Γh

The weighted residual statement for Eq. 5•2 is

∫ wq ( κ –1 q + ∇T ) dΩ = 0 Eq. 5•6

Finite element approximation of (1) temperature field, “T”, and (2) heat flux, “q”, are defined as

T ≅ T h ≡ φ a Tˆ a and q ≅ q h ≡ φ qa q̂ a Eq. 5•7


T

a
where Tˆ and q̂ a are nodal variables (bases), and “a” is nodal index for temperature and heat flux nodes. φTa
and φ qa are two shape functions which can be different from each other. Substituting Eq. 5•7 into Eq. 5•5 and Eq.
5•6 we have the mixed finite element formulation for the heat conduction problem as1

T f
AC q̂
= 1 Eq. 5•8
C 0 Tˆ f2

where

A = ∫ φq ⊗ ( κ–1 φq ) dΩ Eq. 5•9


Ωe

C = ∫ ∇φT ⊗ φq dΩ Eq. 5•10


Ωe

T
f 1 = – Aĥ Γ eh
– C ĝ Γ eg
Eq. 5•11

1. p.319-324 in Zienkiewicz, O.C. and R.L. Taylor, 1991, “The finite element method”, 4th ed., vol. 1. McGraw-Hill, Inc.,
UK.

452 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods

f2 = – ∫ φT f dΩ + ∫ φT h dΓ – Cĥ e
Γh
Eq. 5•12
Ωe e
Γh

where h and g with “over-bar” are fixed nodal flux boundary conditions and fixed nodal temperature boundary
conditions, respectively, while “h” in Eq. 5•12 can be specified as a function on an element boundary. The fixed
nodal boundary conditions, h and g , in Eq. 5•11 and Eq. 5•12 are encountered frequently in finite element
method and are taken care of by “fe.lib” as default behaviors behind the scene. This is consistent with the treat-
ment of the result of the displacement boundary conditions as reaction, and substrates out of the nodal force term
as “ f -= K u ”.
By inspecting on Eq. 5•9 and Eq. 5•10, the derivatives of temperature field exist. C0-continuity of “T” on the
element interior and boundaries is required. This is needed to guarantees that Eq. 5•10 over the entire problem
domain is integrable. Otherwise, the integration goes to infinity at the discontinuities. Two triangular elements
are considered in the following computations. Figure 5•1a&b shows that two triangular elements with linear tem-
perature field with three corner nodes and either a constant heat flux with one node at the center of the element or
linear heat flux with three nodes at the Gaussian integration points. We notice that the temperature nodes on the
three corners is necessary to ensure the C0-continuity on the element boundaries, while the heat flux, with no
derivatives of heat flux in Eq. 5•9 and Eq. 5•10, can be discontinuous at the element boundaries. Therefore, the
constant heat flux element has one node at the center of the element (Figure 5•1a), and the linear heat flux ele-
ment has three nodes at the Gaussian integration points (Figure 5•1b). Actually, requiring heat flux to be C0-con-
tinuity on the element boundaries, had we use three corner nodes for the heat flux, may cause physically
incorrect conditions.1
Recall in Section 4.2.5, the mixed formulation for one dimensional beam bending problem, we have extended
“fe.lib” with the object-oriented modeling for developing matrix substructuring to solve system of equations in
submatrices similar to Eq. 5•8.

T = 30oC

q=0 q=0

(a) Constant (b) Linear (c)


heat flux heat flux
q-nodes T-nodes T = 0oC
Figure 5•1 Triangluar elements with linear temperature with three corner nodes, and either
constant heat flux with one node at the center or linear heat flux with three nodes at three Gauss
integration points.

1. e.g. as discussed in p.327 in Zienkiewicz, O.C. and R.L. Taylor, 1991, “The finite element method”, 4th ed., vol. 1.
McGraw-Hill, Inc., UK.

Workbook of Applications in VectorSpace C++ Library 453


Chapter 5 Advanced Finite Element Methods
The Program Listing 5•1 implements the constant heat flux triangular element for the mixed formulation. For
global discretization {Ωh, qh}, with its element discretization as Ωqe, the heat flux is constant over entire domain
of Ωqe. Only one node at the center of the element is needed. However we still need to specify the three corner
nodes in order to (1) compute the coordinates of the center node, (2) define coordinate transformation rule, and
(3) perform integration. Therefore, the three additional corner nodes of Ωqe are defined as geometrical nodes.
The geometrical nodes has no variable associated with them. A simple trick using “fe.lib” is to disable all geo-
metrical nodes by specifying all these nodes to have Dirichlet type boundary condition, so they will be left out of
the global stiffness matrix. The A-submatrix are defined by

1 double k_x = 1.0, k_y = 1.0, –1


// κ = κ δ ij = κ 0
–1 –1
2 k_inv[2][2] = { {1.0/k_x, 0.0 }, , for isotropy
–1
3 {0.0, 1.0/k_y }}; 0 κ
4 C0 K_inv = MATRIX("int, int, const double*", 2, 2, k_inv[0]);
5 Heat_Mixed_Formulation::Heat_Mixed_Formulation(int en, Global_Discretization& gd) :
6 Element_Formulation_Couple(en, gd) {
7 Quadrature qp(2, 4);
8 H1 L(2, (double*)0, qp),
9 n = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 3, 2, qp),
10 L0 = L[0], L1 = L[1], L2 = 1.0 - L0 - L1; // “area coordinates” for a triangle
11 n[0] = L0; n[1] = L1; n[2] = L2;
12 C0 x = MATRIX("int, int, C0&, int, int", 3, 2, xl, 0, 0);
13 H1 X = n*x;
14 J dv(d(X).det()/2.0);
15 H0 N = INTEGRABLE_VECTOR("int, Quadrature", 4, qp);
16 N[0] = N[1] = N[2] = 0.0; N[3] = 1.0;
17 H0 N_q = ((~N) || C0(0.0)) &
18 (C0(0.0) || (~N) ); // A = ∫ φq ⊗ ( κ – 1 φ q ) dΩ
19 stiff &= ((~N_q) * (K_inv * N_q)) | dv; Ωe
20 }

The area coordinates for triangle, L0, L1, and L2, are used for coordinate transformation and integration. The first
three nodes are geometrical nodes at three corners and the fourth-node is the q-node at the center. A reference
matrix “x” (line 12) is constructed to refer to the first three coordinates of “xl”, which leaves out the q-node. To
compute the Jacobian we notice that there is a factor of 1/2 for a triangular element comparing to the a quadrilat-
eral element where the factor = 1 (note that a factor of 1/6 is to be used for a 3-D tetrahedra element). The q-
shape function, “N” (line 16), is defined that the first three shape functions corresponding to three geometrical
nodes are zero; i.e., “N[0] = N[1] = N[2] = 0”. And the fourth shape function is one; i.e., “N[3] = 1”. The ele-
ment stiffness matrix so constructed will have the size of 8 × 8, with only the 2 × 2 submatrix at the lower-right
corner, corresponding to the center q-node, contains no zero components. All the geometrical nodes are to be
specified with Dirichlet type boundary condition. Dummy variables corresponding to these geometrical nodes
will all be eliminated from the global matrix and the global vector. Therefore, the no trivial 2 × 2 element subma-
trix will enter the global stiffness matrix.
The C-submatrix can be defined as

454 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods

#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 row_t_node_no = 4; static const double h_e = 1.0;
Omega_h_i::Omega_h_i(int i) : Omega_h(0) {
if(i == 0) {
double xl[3][2], v[2]; Node *node;
Ωq
for(int j = 0; j < row_t_node_no-1; j++) make center q-nodes
for(int k = 0; k < row_t_node_no-1; k++) {
int node_no = (j*(row_t_node_no-1) + k) *2;
xl[0][0] = (double)k; xl[0][1] = (double)j;
corner coordinates
xl[1][0] = (double)(k+1); xl[1][1] = (double)j;
xl[2][0] = (double)(k+1); xl[2][1] = (double)(j+1); geometrical center coordinates
for(int l = 0; l < 2; l++) v[l] = (xl[0][l] + xl[1][l] + xl[2][l])/3.0;
node = new Node(node_no, 2, v); node_array().add(node);
xl[0][0] = (double)k; xl[0][1] = (double)j;
xl[1][0] = (double)(k+1); xl[1][1] = (double)(j+1);
xl[2][0] = (double)k; xl[2][1] = (double)(j+1);
or(int l = 0; l < 2; l++) v[l] = (xl[0][l] + xl[1][l] + xl[2][l])/3.0;
node = new Node(node_no+1, 2, v); node_array().add(node);
}
for(int j = 0; j < row_t_node_no; j++)
for(int k = 0; k < row_t_node_no; k++) {
corner geometrical nodes
int nn = j*row_t_node_no+k+(row_t_node_no-1)*2*(row_t_node_no-1);
v[0] = ((double)k)*h_e; v[1] = ((double)j)*h_e;
node = new Node(nn, 2, v); node_array().add(node);
}
for(int j = 0; j < row_t_node_no-1; j++) make heat flux elements, Ωqe
for(int k = 0; k < row_t_node_no-1; k++) {
int element_no = ((row_t_node_no-1)*j + k)*2,
center_node_no = element_no,
first_corner_node_no = j*row_t_node_no+k +(row_t_node_no-1)*2*(row_t_node_no-1);
int ena[4];
ena[0] = first_corner_node_no+1+row_t_node_no; ena[1] = first_corner_node_no;
ena[2] = first_corner_node_no+1; ena[3] = center_node_no;
Omega_eh* elem = new Omega_eh(element_no, 0, 0, 4, ena);
omega_eh_array().add(elem);
ena[0] = first_corner_node_no; ena[1] =
first_corner_node_no+1+row_t_node_no;
ena[2] = first_corner_node_no+row_t_node_no; ena[3] = center_node_no+1;
elem = new Omega_eh(element_no+1, 0, 0, 4, ena); omega_eh_array().add(elem); ΩΤ
}
} else if(i == 1) {
double v[2]; make T-corner nodes
for(int j = 0; j < row_t_node_no; j++)
for(int k = 0; k < row_t_node_no; k++) {
int nn = j*row_t_node_no+k; v[0] = ((double)k)*h_e; v[1] = ((double)j)*h_e;
Node* node = new Node(nn, 2, v); node_array().add(node);
} make temperature elements, ΩTe
for(int j = 0; j < row_t_node_no-1; j++)
for(int k = 0; k < row_t_node_no-1; k++) {
int element_no = ((row_t_node_no-1)*j + k)*2,
first_corner_node_no = j*row_t_node_no+k, ena[3];
ena[0] = first_corner_node_no+1+row_t_node_no;
ena[1] = first_corner_node_no; ena[2] = first_corner_node_no+1;
Omega_eh* elem = new Omega_eh(element_no, 0, 0, 3, ena);
omega_eh_array().add(elem);

Workbook of Applications in VectorSpace C++ Library 455


Chapter 5 Advanced Finite Element Methods
ena[0] = first_corner_node_no; ena[1] = first_corner_node_no+1+row_t_node_no;
ena[2] = first_corner_node_no+row_t_node_no;
elem = new Omega_eh(element_no+1, 0, 0, 3, ena); 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) { Γh
for(int j = (row_t_node_no-1)*6*(row_t_node_no-1);
j < (row_t_node_no*row_t_node_no+(row_t_node_no-1)*6*(row_t_node_no-1)); j++)
disable all geometrical nodes on Ωq
for(int k = 0; k < 2; k++) {
the_gh_array[node_order(j)](k) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(j)][k] = 0.0;
}
} else if(i == 1) { Γg
for(int j = 0; j < row_t_node_no; j++) { top boundary: T = 30o
the_gh_array[node_order(row_t_node_no*(row_t_node_no-1)+j)](0) =
gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(row_t_node_no*(row_t_node_no-1)+j)][0] =
((double)(row_t_node_no-1))*10.0; bottom boundary: T = 0o
the_gh_array[node_order(j)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(j)][0] = 0.0;
}
}
}
static const int q_ndf = 2; static Omega_h_i oh_q(0);
static gh_on_Gamma_h_i q_gh(0, q_ndf, oh_q); static U_h q_h(q_ndf, oh_q);
static Global_Discretization q_gd(oh_q, q_gh, q_h);
static const int T_ndf = 1; static Omega_h_i oh_T(1);
static gh_on_Gamma_h_i T_gh(1, T_ndf, oh_T); static U_h T_h(T_ndf, oh_T);
static Global_Discretization T_gd(oh_T, T_gh, T_h);
static Global_Discretization_Couple gdc(T_gd, q_gd);

class Heat_Mixed_Formulation : public Element_Formulation_Couple {


public:
Heat_Mixed_Formulation(Element_Type_Register a) : Element_Formulation_Couple(a) {} for diagonal submatrix A
Element_Formulation *make(int, Global_Discretization&);
Heat_Mixed_Formulation(int, Global_Discretization&);
Element_Formulation_Couple *make(int, Global_Discretization_Couple&); for off-diagonal submatrix C
Heat_Mixed_Formulation(int, Global_Discretization_Couple&);
};
Element_Formulation* Heat_Mixed_Formulation::make(int en, Global_Discretization& gd) {
A-submatrix element formulation
return new Heat_Mixed_Formulation(en,gd);
}
Heat_Mixed_Formulation::Heat_Mixed_Formulation(int en, Global_Discretization& gd) :
Element_Formulation_Couple(en, gd) {
Quadrature qp(2, 4);
H1 L(2, (double*)0, qp), area coordinates for triangle
n = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 3, 2, qp),
L0 = L[0], L1 = L[1], L2 = 1.0 - L0 - L1;
linear triangular shape function for
n[0] = L0; n[1] = L1; n[2] = L2; coordinate transformation
C0 x = MATRIX("int, int, C0&, int, int", 3, 2, xl, 0, 0);
H1 X = n*x;
N ≡ φ q= 1.0 (constant over element)
J dv(d(X).det()/2.0);
H0 N = INTEGRABLE_VECTOR("int, Quadrature", 4, qp); N[0], N[1], N[2] are geometrical nodes
N[0] = N[1] = N[2] = 0.0; N[3] is constant over element domain
N[3] = 1.0;

456 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
double k_x = 1.0, k_y = 1.0, k_inv[2][2] = { {1.0/k_x, 0.0}, { 0.0, 1.0/k_y}};
C0 K_inv = MATRIX("int, int, const double*", 2, 2, k_inv[0]);

∫ φ q ⊗ ( κ – 1 φ q ) dΩ
H0 N_q = ((~N) || C0(0.0)) &
(C0(0.0)|| (~N)); A =
stiff &= ((~N_q) * (K_inv * N_q)) | dv; Ωe
}
Element_Formulation_Couple* Heat_Mixed_Formulation::make(
int en, Global_Discretization_Couple& gdc) { C-submatrix element formulation
return new Heat_Mixed_Formulation(en,gdc);
}
Heat_Mixed_Formulation::Heat_Mixed_Formulation(int en, Global_Discretization_Couple& gdc)
: Element_Formulation_Couple(en, gdc) {
Quadrature qp(2, 4);
H1 L(2, (double*)0, qp),
n ≡ φ T linear triangular shape function
n = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE(
"int, int, Quadrature", 3/*nen*/, 2/*nsd*/, qp), for both coordinate transformation and
L0 = L[0], L1 = L[1], L2 = 1.0 - L0 - L1; n[0] = L0; n[1] = L1; n[2] = L2; temperature field shape function
H1 X = n*xl;
H0 nx = d(n) * d(X).inverse();
J dv(d(X).det()/2.0); N ≡ φ q= 1.0 (constant over element)
H0 N = INTEGRABLE_VECTOR("int, Quadrature", 4/*nen*/, qp); for heat flux field shape function
N[0] = N[1] = N[2] = 0.0; N[3] = 1.0;
H0 N_q = ((~N) || C0(0.0)) &
(C0(0.0)|| (~N)); C = ∫ ∇φT ⊗ φq dΩ
stiff &= (nx * N_q) | dv; Ωe
}
Element_Formulation* Element_Formulation::type_list = 0;
static Element_Type_Register element_type_register_instance;
static Heat_Mixed_Formulation
heat_mixed_formulation_instance(element_type_register_instance);

static Matrix_Representation mr(q_gd);


static Matrix_Representation_Couple mrc(gdc, 0, 0, &(mr.rhs()), &mr);
int main() {
mrc.assembly();
mr.assembly();
C0 A = ((C0)(mr.lhs())),
f_1 = ((C0)(mr.rhs())),
C = ((C0)(mrc.lhs())),
f_2 = ((C0)(mrc.rhs()));
Cholesky dA(A); range space method for quadratic
C0 Ainv = dA.inverse(),
CAinvCt = C*Ainv*(~C);
programming problem
Cholesky dCAinvCt(CAinvCt);
C0 T = dCAinvCt*((C*Ainv*f_1)-f_2), Tˆ = (CA-1CT)-1(CA-1f1-f2)
q = dA*(f_1-(~C)*T);
q̂ = A-1 (f1- CT Tˆ )
q_h = q;
q_h = q_gd.gh_on_gamma_h();
cout << "heat flow:" << endl;
for(int i = 0; i < q_h.total_node_no(); i++)
cout << q_h[i] << endl;
T_h = T;
T_h = T_gd.gh_on_gamma_h();
cout << "temperature:" << endl << T_h;
return 0;
}

Listing 5•1 Substructure method for the mixed formulation of the heat conduction problem with constant
heat flux (project: “mixed_heat_conduction” in project workspace file “fe.dsw”).

Workbook of Applications in VectorSpace C++ Library 457


Chapter 5 Advanced Finite Element Methods
1 Heat_Mixed_Formulation::Heat_Mixed_Formulation(int en, Global_Discretization_Couple& gdc)
2 : Element_Formulation_Couple(en, gdc) {
3 Quadrature qp(2, 4);
4 H1 L(2, (double*)0, qp),
5 n = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE(
6 "int, int, Quadrature", 3/*nen*/, 2/*nsd*/, qp),
7 L0 = L[0], L1 = L[1], L2 = 1.0 - L0 - L1;
8 n[0] = L0; n[1] = L1; n[2] = L2;
9 H1 X = n*xl;
10 H0 nx = d(n) * d(X).inverse();
11 J dv(d(X).det()/2.0);
12 H0 N = INTEGRABLE_VECTOR("int, Quadrature", 4/*nen*/, qp);
13 N[0] = N[1] = N[2] = 0.0; N[3] = 1.0;
14 H0 N_q = ( (~N) || C0(0.0)) &
15 (C0(0.0) || (~N)); // C = ∫ ∇φ T ⊗ φ q dΩ
16 stiff &= (nx * N_q) | dv; Ωe
17 }

The element stiffness matrix so generated has the size of 3 × 8. The three rows (number of equations) corre-
sponding to three temperature nodes at the corner. After the element to global mapping, the first 6 columns (cor-
responding to the number of dummy variables) for the geometrical nodes will not enter the global stiffness
matrix. Only the last 2 columns (corresponding to the number of variables) for the center q-node will survive.
After the submatrices have been formed, the solution of system of equation in Eq. 5•8 is the subject of con-
straint optimization problem (see Section 2.3.3 in Chapter 2). In the context of finite element problem, the objec-
tive functional for optimization is quadratic. The problem is further restricted to a quadratic programming
problem, in which only one step along the search path is needed to reach the exact solution (see introduction and
its example in page 145). We discuss the range space method and null space method (see page 149) to solve Eq.
5•8 in the followings.
From first equation of Eq. 5•8 we have

A q̂ + CT Tˆ = f1 Eq. 5•13

Considering A is symmetrical positive definitive, therefore it can be inverted, we can solve for q by

q̂ = A-1 f1 - A-1 CT Tˆ Eq. 5•14

Substituting Eq. 5•14 into second equation of Eq. 5•8, C q̂ = f2, we have

C A-1 f1 - C A-1 CT Tˆ = f2 Eq. 5•15

Therefore, “ Tˆ ” can also be solved considering that the similarity transformation of A-1 as “C A-1 CT” preserves
the symmetrical positive definitiveness of A-1. An alternative view is that “C A-1 CT” is the projection of inverse

458 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
of the Hessian, A-1, into the range of the constraint space, C. Providing that “C A-1 CT” is non-singular we may
invert it to solve for “T” as

Tˆ = (C A-1 CT)-1 (C A-1 f1 - f2) Eq. 5•16

After we obtain the nodal temperature solution “ Tˆ ”, the nodal heat flux solution “ q̂ ” can be solved from Eq.
5•13 as

q̂ = A-1 ( f1 - CT Tˆ ) Eq. 5•17

Eq. 5•16 and Eq. 5•17 is the formula for the range space method. We notice that the size of A-square submatrix
is the number of q̂ -free-variables, denotes as “n q”. The row-size of the C-submatrix is number of Tˆ -free-vari-
ables, denotes as “nT” (C-submatrix has size of nT × nq). Assuming full-row-rank condition for both A and C sub-
matrices. The rank of A-1 is “nq” and the column rank of CT is “nT”. In order to have “C A-1 CT” non-singular for
the inversion of “C A-1 CT” to be possible, we must have

nq ≥ nT Eq. 5•18

Based on the above simplistic linear algebraic discussion, Eq. 5•18 can be used to conceptually performing
“patch test” in the design of “q-T” two-field mixed finite element.1 However, a mathematically more rigorous
theorem on the existence and uniqueness of the constraint optimization problem (in the abstract form of the sad-
dle-point problem) is known as LBB-condition (coined by Ladyzhenskaya-Babuska-Brezzi and is also known as
inf-sup condition).2 The implementation of the range space method is

1 int main() {
2 Matrix_Representation mr(q_gd);
3 Matrix_Representation_Couple mrc(gdc, 0, 0, &(mr.rhs()), &mr);
4 mrc.assembly();
5 mr.assembly();
6 C0 A = ((C0)(mr.lhs())), f_1 = ((C0)(mr.rhs())),
7 C = ((C0)(mrc.lhs())), f_2 = ((C0)(mrc.rhs()));
8 Cholesky dA(A); // Cholesky decomposition on Hessian A
9 C0 Ainv = dA.inverse(),
10 CAinvCt = C*Ainv*(~C);
11 Cholesky dCAinvCt(CAinvCt); // Cholesky decomposition on C A-1 CT
12 C0 T = dCAinvCt*((C*Ainv*f_1)-f_2),
13 q = dA*(f_1-(~C)*T);
14 q_h = q; // update free degree of freedom
15 q_h = q_gd.gh_on_gamma_h(); // update fixed degree of freedom
16 cout << "heat flow:" << endl;

1. p.324-327 in Zienkiewicz, O.C. and R.L. Taylor, 1989, “The finite element method”, 4th ed., vol. 1. McGraw-Hill, Inc.,
UK.
2. see Brezzi, F, and M. Fortin, 1991, “Mixed and hybrid finite element method”, Spring-Berlag New York, Inc.

Workbook of Applications in VectorSpace C++ Library 459


Chapter 5 Advanced Finite Element Methods
17 for(int i = 0; i < q_h.total_node_no(); i++)
18 cout << q_h[i] << endl;
19 T_h = T; // update free degree of freedom
20 T_h = T_gd.gh_on_gamma_h(); // update fixed degree of freedom
21 cout << "temperature:" << endl << T_h;
22 return 0;
23 }
We note by passing that in range space method the multiplication of matrices such as “C A-1 CT” may
severely increase the condition number of the resultant matrix comparing to that of A-1. We would like to have
the condition number kept to be the same as that of the A-1. This can be done by performing QR-decomposition
on C. The collection of orthogonal column vectors, say Y, from the range space of Q decomposed from C, can
be used in place of C. That is to replace every instance of C in Eq. 5•16 and Eq. 5•17 with Y. Such that the con-
dition number of “Y A-1 YT” is no worse than A-1, since column vectors in Y are orthonormal. This treatment is
important when the problem size increases.
For readers who are familiar with the null space method discussed in Chapter 2, the solution to Eq. 5•8 can
be implemented simply as

1 static Matrix_Representation mr(q_gd);


2 static Matrix_Representation_Couple mrc(gdc, 0, 0, &(mr.rhs()), &mr);
3 int main() {
4 mrc.assembly();
5 mr.assembly();
6 C0 A = ((C0)(mr.lhs())), f_1 = ((C0)(mr.rhs())),
7 C = ((C0)(mrc.lhs())), f_2 = ((C0)(mrc.rhs()));
8 QR ct(~C); // QR decomposition on constraint space of CT
9 C0 Q = ct.Q(); // CT = Q R
10 C0 Z(A.row_length(), (A.row_length()-C.row_length()), (double*)0); // null space of C
11 for(int i = 0; i < Z.col_length(); i++) Z(i) = Q(C.row_length()+i);
12 C0 A0 = (~Z)*A*Z; // project A on to the null space of the constraint space
13 Cholesky dA0(A0); // cholesky decomposition on A0
14 C0 A0_inv = dA0.inverse();
15 C0 q = Z*A0_inv*(~Z)*f_1;
16 q_h = q; // update free degree of freedom
17 q_h = q_gd.gh_on_gamma_h(); // update fixed degree of freedom
18 cout << "heat flow:" << endl;
19 for(int i = 0; i < q_h.total_node_no(); i++)
20 cout << q_h[i] << endl;
21 C0 T = (C*(~C)).inverse()*C*(f_1-A*q);
22 T_h = T; // update free degree of freedom
23 T_h = T_gd.gh_on_gamma_h(); // update fixed degree of freedom
24 cout << "temperature:" << endl << T_h;
25 return 0;
26 }

460 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
Both the range space and null space method reproduce the exact solution up to round-off error for the current
problem. The nodal temperature solutions at the second row is Tˆ = 10 oC, and the third row is Tˆ = 20 oC. All
heat flux center nodes have the values of q̂ = [0, -10]T per unit length. The default method in Program Listing
5•1 is the range space method. The null space method can be activated by setting macro definition
“__NULL_SPACE_METHOD” at compile time.
The linear heat flux with three nodes at the Gaussian integration points (see Figure 5•1b) can be activated by
setting macro definition “__TEST_THREE_NODES_DISCONTINUOUS_HEAT_FLUX” at compile time. The
triangular element in this implementation used shape functions degenerated from bilinear 4-node element. The
results of this linear heat flux element is identical to that of the constant heat flux element. Considering that T-
field only for a 3-node triangular element vary linearly. The heat flux, computed from the Fouries law, is the
derivative of the linear T-field, which is constant. Therefore, the linear heat flux element does not give any
improvement in the accuracy of the solution comparing to the constant heat flux element. Actually, the linear
heat flux element produces the same result as the constant heat flux element. This is known as the limitation prin-
ciple by Fraeijs de Veubeke.
With the assistance of object-oriented modeling provided in “fe.lib” for handling the matrix substructuring, it
may seems to be a piece of cake to implement the multiple-field mixed formulation. It is not so for most existing
finite element programs. It has been remarked that the full-scale multiple-field formulations are rarely imple-
mented for practical computation.1
The discontinuous heat flux field, with nodes reside only inside an element, not only avoid physically incor-
rect conditions as mentioned earlier, but it also has the advantage that the heat flux field can be eliminated at the
element level. Every heat flux node belongs only to its containing element; i.e., there are no shared heat flux
nodes with the neighboring elements. Therefore, from Eq. 5•16 that

Tˆ = (C A-1 CT)-1 (C A-1 f1 - f2)

We can redefine element stiffness matrix and element force vector as

ke = Ce Ae-1 CeT, and fe= Ce Ae-1 f1e - f2e Eq. 5•19

respectively, with

f 2e = – ∫ φ T f dΩ Eq. 5•20
Ωe

and,

∫ φT h dΓ – C e ĥ
Γ he
= 0 Eq. 5•21
Γ he

1. see p.206 in Hughes, T. J.R., “The finite element method: linear static and dynamic finite element analysis”, Prentice-Hall,
Inc., Englewood Cliffs, New Jersey.

Workbook of Applications in VectorSpace C++ Library 461


Chapter 5 Advanced Finite Element Methods
Since in this discontinuous heat flux element no heat flux boundary condition can be specified, the temperature
boundary conditions in Eq. 5•8 are simply

T
f1 = - C ĝ Γg
Eq. 5•22

Substituting the element level vector of Eq. 5•22 into the second equation in Eq. 5•19, we have

T T
fe = - Ce Ae-1 C e ĝ e Γ ge
- f2e= - Ce Ae-1 C e ĝ e Γ ge
- f2e

= -ke ĝ e Γ ge
- f2e Eq. 5•23

The first term in the right-hand-side is consistent with standard implementation of finite element on the essential
boundary conditions. The mixed form of Eq. 5•19 can be easily implemented which does not require the mecha-
nism provided in the “fe.lib” to express matrix substructuring. Program Listing 5•2 implements Eq. 5•19 (project
“mixed_T_heat_conduction” in project workspace file “fe.dsw”). The temperature solutions of the computation
is certainly identical to the full-fledged mixed formulation. The program is significant simplified comparing to
the full-scale mixed formulation.

462 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods

#include "include\fe.h"
static row_node_no = 4;
EP::element_pattern EP::ep = EP::SLASH_TRIANGLES;
Omega_h::Omega_h() {
double coord[4][2] = {{0.0, 0.0}, {3.0, 0.0}, {3.0, 3.0}, {0.0, 3.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 j = 0; j < row_node_no; j++) {
the_gh_array[node_order(row_node_no*(row_node_no-1)+j)](0) =
the_gh_array[node_order(j)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(row_node_no*(row_node_no-1)+j)][0] =
((double)(row_node_no-1))*10.0;
the_gh_array[node_order(j)][0] = 0.0;
}
}
class HeatMixedT3 : public Element_Formulation {
public:
HeatMixedT3(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
HeatMixedT3(int, Global_Discretization&);
};
Element_Formulation* HeatMixedT3::make(int en, Global_Discretization& gd) {
return new HeatMixedT3(en,gd); }
double k_x = 1.0, k_y = 1.0, k_inv[2][2] = { {1.0/k_x, 0.0}, { 0.0, 1.0/k_y}};
C0 K_inv = MATRIX("int, int, const double*", 2, 2, k_inv[0]);
HeatMixedT3::HeatMixedT3(int en, Global_Discretization& gd) : Element_Formulation(en, gd) {
Quadrature qp(2, 4);
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;
H1 X = n*xl; H0 nx = d(n) * d(X).inverse(); J dv(d(X).det()/2.0);
H0 N = INTEGRABLE_SCALAR("Quadrature", qp); N = 1.0;
C = ∫ ∇φT ⊗ φq dΩ
H0 N_q = ((~N) || C0(0.0)) & Ωe
(C0(0.0)|| (~N));
C0 C = (nx * N_q) | dv,
A = ((~N_q) * (K_inv * N_q)) | dv,
A = ∫ φq ⊗ ( κ –1 φq ) dΩ
Ωe
A_inv = A.inverse();
stiff &= C*A_inv*(~C); k = (CA-1CT)
}
Element_Formulation* Element_Formulation::type_list = 0;
Element_Type_Register element_type_register_instance;
static HeatMixedT3 heatmixedt3_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()));
uh = u; uh = gh;
cout << uh << endl;
return 0;
}

Listing 5•2 Mixed formulation with discontinuous temperature field (C0 continuity dropped) reduces to
temperature field only formulation (project: “mixed_t_field_heat_conduction”).

Workbook of Applications in VectorSpace C++ Library 463


Chapter 5 Advanced Finite Element Methods
Hourglass Element
For the irreducible formulation in Chapter 4, 2 × 2 Gaussian integration points are used to compute element
stiffness matrix for bilinear 4-node element.

ke = ∫ ( ( ∇N )T κ ∇N )dΩ = ∫ ( BT κ B )dΩ Eq. 5•24


Ωe Ωe

where

∂N a
----------
b xa ∂x
Ba ≡ ≡ Eq. 5•25
b ya ∂N a
----------
∂y

and a = 0, 1, 2, 3. A reduced integration (1 Gauss point at the center of the element) will result in rank deficiency
of the element stiffness matrix. The rank of the element stiffness matrix is the number of integration points times
the number of independent relations. In the case of heat conduction, the number of independent relations is the
number of equations relating the heat flux [qx, qy] and the temperature gradients [∂T/∂x, ∂T/∂y] by Fourier law
of heat conduction in 2-D. Therefore, the rank of the stiffness matrix for 1-point Gauss integration rule is 2 (=
2 × 1). For the element stiffness matrix of size 4 × 4 in Eq. 5•24, the 1-point Gauss integration leads to rank defi-
ciency of 2 (= 4-2). We can consider that the 1-point integration stiffness matrix is span by bx = {bxa, bya} in Eq.
5•25, which are bases in 4. The two spurious zero energy modes in the null space are constant solution mode
with nodal solution of sa = [1, 1, 1, 1] and hourglass mode with nodal solution of ha = [-1, 1, -1, 1]. The hour-
glass mode is illustrated in Figure 5•2. The two zero energy modes are orthogonal to bx

b x s = 0 , and b x h = 0 Eq. 5•26


i i

where we also denote x0 = x, and x1 = y. We notice that for isoparametric coordinate transformation xi = Na xia ,
we have the relation

1
T
0.5
0
-0.5 η
-1
1

0.5
.5

-0.5

-1
ξ
-1
-0.5
0
0.5
1

Figure 5•2 The hourglass mode for heat conduction problem.

464 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
∂N
b x x j = ------- x = δ ij Eq. 5•27
i ∂x i j

The constant nodal solution sa is considered “proper”, since a constant temperature field produces no heat flux is
as expected, while the hourglass mode ha has gradient far from zero (see Figure 5•2) but no heat flux production,
which is considered “improper”. Therefore, we expect the 4-node element stiffness to produce a rank 3 matrix.
The strategy is to use a so-called trial hourglass mode, Ψa , to construct a correct-ranked stiffness ke in a way that
is very economical to compute as

ke = ke(1-point) + ke(hourglass) Eq. 5•28

The first term on the right hand side, ke(1-point) is standard stiffness matrix computed with only one Gaussian
integration point. The second term ke(hourglass) is to be constructed with the trial hourglass mode Ψa. The general
form of the trial hourglass mode, Ψa , in 4 is span by the four bases [bxa, bya, sa, ha] as

Ψa = a0 bxa + a1 bya + a2 sa+ ha Eq. 5•29

Ψa is required to be orthogonal with an arbitrary linear temperature field of Ta

Ta = c0 xa + c1ya + c2 sa Eq. 5•30

For Ψa to be always orthogonal with arbitrary coefficients, ci , in Eq. 5•30, we have

Ψa xa = 0 , Ψa ya = 0, and Ψa sa = 0 Eq. 5•31

From last part of Eq. 5•31, no component of Ψa is in sa, so in Eq. 5•29 a2 = 0, and we have this equation re-writ-
ten as

Ψa = a0 bxa + a1 bya + ha Eq. 5•32

Substituting Eq. 5•32 into Ψa xa = 0 in the first part of Eq. 5•31, and using bxa xa = 1, and bya xa = 0 as indicated
in Eq. 5•27, we have

a0 = - h a x a Eq. 5•33

Similarly, substituting Eq. 5•32 into Ψa ya = 0 in the second part of Eq. 5•31, and using bya ya = 1, and bxa ya = 0
as in Eq. 5•27, we have

a1 = - h a y a Eq. 5•34

Therefore, we show that

Ψa = ha - (ha xa) bxa - (ha ya) bya Eq. 5•35

The hourglass stiffness can be defined as 1

Workbook of Applications in VectorSpace C++ Library 465


Chapter 5 Advanced Finite Element Methods
kJ ( b • b )
k e ( hourglass ) ≡ ----------------------- ( Ψ ⊗ Ψ ) Eq. 5•36
12

where Ψa is Ψa normalized to ||Ψa|| = 2, b = [bx, by]T, and J ≡ det ( ∂x ⁄ ∂ ξ ) . Program Listing 5•3 implements the
hourglass element for heat conduction (project “hourglass_heat” in project ˜ workspace file”fe.dsw”).
We measure the time spent in the computation of stiffness matrix for the heat conduction element with irre-
ducible formulation in Chapter 4. It takes 3.5 seconds to assemble the stiffness on an obsolete 166 MHz PC. For
the mixed formulation in the last section, although we gain significant freedom in terms of formulation, the
assemble of diagonal and off-diagonal stiffness matrices (A and C) takes 6.2 seconds on the same computer. The
hourglass element takes only 0.5 second to assemble the stiffness.

1. see similar derivation for elasticity in p.251-254 in Hughes, T. J.R., “The finite element method: linear static and dynami c
finite element analysis”, Prentice-Hall, Inc., Englewood Cliffs, New Jersey, and references therein.

466 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods

#include "include\fe.h"
EP::element_pattern EP::ep = EP::QUADRILATERALS_4_NODES;
Omega_h::Omega_h() {
double coord[4][2] = {{0.0, 0.0}, {3.0, 0.0}, {3.0, 3.0}, {0.0, 3.0}};
int control_node_flag[4] = {1, 1, 1, 1};
block(this, 4, 4, 4, control_node_flag, coord[0]);
}
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
__initialization(df, omega_h);
int row_node_no = 4;
for(int i = 0; i < row_node_no; i++) {
the_gh_array[node_order(i)](0) = gh_on_Gamma_h::Dirichlet; bottom B.C. = 0oC
the_gh_array[node_order(row_node_no*(row_node_no-1)+i)](0) = top B.C. = 30oC
gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(row_node_no*(row_node_no-1)+i)][0] = 30.0;
}
}
class HourGlassHeatQ4 : public Element_Formulation { public:
HourGlassHeatQ4(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
HourGlassHeatQ4(int, Global_Discretization&);
};
Element_Formulation* HourGlassHeatQ4::make(int en, Global_Discretization& gd) {
return new HourGlassHeatQ4(en,gd);
}
HourGlassHeatQ4::HourGlassHeatQ4(int en, Global_Discretization& gd) :
Element_Formulation(en, gd) {
Quadrature qp(2, 1); 1-point Gaussian integration for 2-D
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; C0 K_standard = (Nx * k_ * (~Nx)) | dv;
ke(1-point) from irreducible formulation
C0 h(4, (double*)0); h[3] = 1.0; Ψa = ha - (ha xa) bxa - (ha ya) bya
C0 phi = h - Nx.quadrature_point_value(0)*((~xl)*h); Ψa is Ψa normalized to ||Ψa|| =2
double factor = 2.0/norm(phi); phi *= factor;
H0 b = Nx(0) & Nx(1);
b = [bx, by]T
double j = (double)(d(X).det().quadrature_point_value(0)); J ≡ det ( ∂x ⁄ ∂ ξ )
C0 K_hourglass = (k_*j*((~b)*b) | dv) /12.0 * (phi%phi); kJ ( b • b )
k e ( hourglass ) ≡ ----------------------- ( Ψ ⊗ Ψ )
stiff &= K_standard + K_hourglass; 12
}
Element_Formulation* Element_Formulation::type_list = 0; ke = ke(1-point) + ke(hourglass)
Element_Type_Register element_type_register_instance;
static HourGlassHeatQ4 hourglassheatq4_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()));
uh = u; uh = gh;
cout << uh << endl;
return 0;
}

Listing 5•3 One point Gauss integration stiffness matrix with hourglass stabilizer element (project:
“hourglass_heat” in project workspace file “fe.dsw”).

Workbook of Applications in VectorSpace C++ Library 467


Chapter 5 Advanced Finite Element Methods
5.1.2 Mixed Formulation for Plane Elasticity
In the irreducible formulation for plane elasticity, the displacement field, u, is the only variable that the vari-
ation of the Lagrangian functional ( δ ( u )), derived from the equilibrium equations, is taken as

∫ ( Lδu ) σ dΩ – ∫ δu b dΩ – ∫ δuT h dΓ
T
δ (u) = T
Eq. 5•37
Ω Ω Γh

where the body force is denoted as b, the strain is defined as ε = Lu , and the differential operator L in matrix
form as


------ 0
∂x
L ≡ 0 ----- ∂ Eq. 5•38
-
∂y
∂ ∂
------ ------
∂y ∂x

The traction boundary condition is t = h on Γh. In the mixed formulation, in addition to the variational approxi-
mation on the equilibrium equations. we will also use the variational approximation to both the constitutive
equations and strain-displacement relations, separately. The interpolation functions Φ(x) in finite element
approximation of displacement field is taken as

u e ≡ Φ ea ( x )û ea Eq. 5•39

where Φ ea ( x ) is interpolation functions, and û ea is nodal displacements. The subscript “e” denotes the element
level.

Hellinger-Reissner Variational Principle


In addition to the variational principle based on equilibrium equation in Eq. 5•37, we consider the constitu-
tive equation

σ = D ε = D Lu Eq. 5•40

We also add stress field, σ, as additional variable in the Lagrangian functional such that

∫ δ σ T ( Lu – D –1 σ ) dΩ = 0 Eq. 5•41

Eq. 5•37 and Eq. 5•41 are the Euler-Lagrange equations corresponding to the Lagrangian functional.

( σ, u ) = --- ∫ σ T D –1 σ dΩ + ∫ u T ( L T σ + b ) dΩ – ∫ u T ( n σ – h ) dΓ
1
Eq. 5•42
2
Ω Ω Γh

468 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
where t = nσ (the Cauchy’s formula). The Euler-Lagrange equations are obtained by taking the directional deriv-
atives of the Lagrangian functional with respect to σ and u, then, make them equal to zero. Eq. 5•42 is known as
the Hellinger-Reissner variational principle. The finite element approximation of the stress field has interpola-
tion function Ψ(x) in

σe ≡ Ψea ( x ) σ̂e
a
Eq. 5•43

By inspecting Eq. 5•37 and Eq. 5•41, stress field has no derivatives taken on it. Therefore, the C0-continuity
requirement on the element boundaries, to ensure the integral equation does not give infinity, can be dropped.
The interpolation functions Ψea ( x ), in contrast to Φ ea ( x ) in Eq. 5•39, can be taken as piece-wise continuous func-
tions across the entire problem domain. For example, for four stress nodes taken at Gauss integration points with
the natural coordinates

 1 1 1 1 1 1 1 1 
[ ξ a, η a ] =  – -------, – ------- , -------, – ------- , -------, ------- , – -------, -------  Eq. 5•44
 3 3 3 3 3 3 3 3 

where a = 0, 1, 2, 3. The shape functions for these four nodes are

1
Ψ ea ( x ) ≡ --- ( 1 + 3ξ a ξ ) ( 1 + 3η a η ) Eq. 5•45
4

If such discontinuous (at the element boundaries) interpolation is taken, the stress field can be approximated glo-
bally. Because there is no inter-element dependency. The subscript “e” on Ψ can be dropped. The matrix form of
Eq. 5•37 and Eq. 5•41, at element level, is

AC
T
σ̂ =
f1
Eq. 5•46
C 0 û f2

where

A = – ∫ Ψ ⊗ ( D Ψ ) dΩ
–1
Eq. 5•47
Ωe

C = ∫ B ⊗ Ψ dΩ Eq. 5•48
Ωe

f1 = – A ( n σ )
T
Γ eσ
–C u Γ eu
Eq. 5•49

f2 = ∫Φ b dΩ + ∫Φ ( n σ ) dΓ – C ( n σ ) Eq. 5•50
Γ eσ
Ωe Γ eσ

Workbook of Applications in VectorSpace C++ Library 469


Chapter 5 Advanced Finite Element Methods
where B = LΦ. Again, if discontinuous interpolation on Ψ were taken, all terms involving Γσ dropped out in Eq.
5•11 and Eq. 5•12, which are then significantly simplified. To avoid singular conditions as discussed in Eq. 5•18
we should have the condition to avoid singularity that

nσ ≥ nu Eq. 5•51

Two quadrilateral elements with four σ-nodes and eight u-nodes (Q 4/8)1 are used to compute the beam bending
problem in the higher-order path test in the Chapter 4. The numbers of degree of freedom for the two fields are
nσ = 8 × 3 = 24, nu = 13 × 2-4 = 22, which satisfied the conceptual patch test criterion in Eq. 5•51. The implemen-
tation of Eq. 5•8 to Eq. 5•12 is shown in Program Listing 5•4 (project “hellinger_reissner_variational_principle”
in project workspace file “fe.dsw”). They present no new difficulty from the Program Listing 5•1. The solution
of the tip-deflection is 0.75, which is exact.

1. p.331 in Zienkiewicz, O.C. and R.L. Taylor, 1989, “The finite element method”, 4th ed., vol. 1. McGraw-Hill, Inc., UK.

470 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods

#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 row_node_no = 5; static const int row_segment_no = row_node_no-1;
static const double L_ = 10.0; static const double c_ = 1.0;
static const double h_e_ = L_/((double)row_segment_no);
static const double E_ = 1.e3; static const double v_ = 0.3;
Omega_h_i::Omega_h_i( int i) : Omega_h(0) {
if(i == 0) { Ωσ
double inv_sqrt3 = 1.0/sqrt(3.0), v[2], xl[4][2], zai, eta; Node *node;
xl[0][0] = 0.0; xl[0][1] = 0.0; xl[1][0] = 2.0*h_e_; xl[1][1] = 0.0; 1st element
xl[2][0] = 2.0*h_e_; xl[2][1] = 2.0*c_; xl[3][0] = 0.0; xl[3][1] = 2.0*c_; coordinates of four corner nodes
1 1
zai = - inv_sqrt3; eta = - inv_sqrt3; physcial coordinates at ( – -------, – ------- )
for(int j = 0; j < 2; j++) v[j] =(1.0-zai)*(1.0-eta)/4.0*xl[0][j]+(1.0+zai)*(1.0-eta)/4.0*xl[1][j]+ 3 3
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(0, 2, v); node_array().add(node);
1 1
zai = inv_sqrt3; eta = - inv_sqrt3; physcial coordinates at ( -------, – ------- )
for(int j = 0; j < 2; j++) v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+ 3 3
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(1, 2, v); node_array().add(node);
1 1
zai = inv_sqrt3; eta = inv_sqrt3; physcial coordinates at ( -------, ------- )
for(int j = 0; j < 2; j++) v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+ 3 3
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(2, 2, v); node_array().add(node);
1 1
zai = - inv_sqrt3; eta = inv_sqrt3; physcial coordinates at ( – -------, ------- )
for(int j = 0; j < 2; j++) v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ 1.0+zai)*(1.0-eta)/4.0*xl[1][j]+ 3 3
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(3, 2, v); node_array().add(node);
xl[0][0] = 2.0*h_e_; xl[0][1] = 0.0; xl[1][0] = 4.0*h_e_; xl[1][1] = 0.0; 2nd element
xl[2][0] = 4.0*h_e_; xl[2][1] = 2.0*c_; xl[3][0] = 2.0*h_e_; xl[3][1] = 2.0*c_;
zai = - inv_sqrt3; eta = - inv_sqrt3;
for(int j = 0; j < 2; j++) v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(4, 2, v); node_array().add(node);
zai = inv_sqrt3; eta = - inv_sqrt3;
for(int j = 0; j < 2; j++) v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(5, 2, v); node_array().add(node);
zai = inv_sqrt3; eta = inv_sqrt3;
for(int j = 0; j < 2; j++) v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(6, 2, v); node_array().add(node);
zai = - inv_sqrt3; eta = inv_sqrt3;
for(int j = 0; j < 2; j++) v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(7, 2, v); node_array().add(node);
v[0] = 0.0; v[1] = 0.0; node = new Node(8, 2, v); node_array().add(node); node # 8-20 are geometrical nodes
v[0] = 1.0*h_e_; node = new Node(9, 2, v); node_array().add(node); (serendipity)
v[0] = 2.0*h_e_; node = new Node(10, 2, v); node_array().add(node);
v[0] = 3.0*h_e_; node = new Node(11, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(12, 2, v); node_array().add(node);
v[0] = 0.0; v[1] = 1.0*c_; node = new Node(13, 2, v); node_array().add(node);
v[0] = 2.0*h_e_; node = new Node(14, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(15, 2, v); node_array().add(node);
v[0] = 0.0; v[1] = 2.0*c_; node = new Node(16, 2, v); node_array().add(node);
v[0] = 1.0*h_e_; node = new Node(17, 2, v); node_array().add(node);

Workbook of Applications in VectorSpace C++ Library 471


Chapter 5 Advanced Finite Element Methods
v[0] = 2.0*h_e_; node = new Node(18, 2, v); node_array().add(node);
v[0] = 3.0*h_e_; node = new Node(19, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(20, 2, v); node_array().add(node);
int ena[12]; Omega_eh *elem;
ena[0] = 8; ena[1] = 10; ena[2] = 18; ena[3] = 16; ena[4] = 9; ena[5] = 14; last four nodes are σ-nodes
ena[6] = 17; ena[7] = 13; ena[8] = 0; ena[9] = 1; ena[10] = 2; ena[11] = 3;
elem = new Omega_eh(0, 0, 0, 12, ena); omega_eh_array().add(elem);
ena[0] = 10; ena[1] = 12; ena[2] = 20; ena[3] = 18; ena[4] = 11; ena[5] = 15;
ena[6] = 19; ena[7] = 14; ena[8] = 4; ena[9] = 5; ena[10] = 6; ena[11] = 7;
elem = new Omega_eh(1, 0, 0, 12, ena); omega_eh_array().add(elem);
} else if (i == 1) { // Omega_u
double v[2]; Node *node;
v[0] = 0.0; v[1] = 0.0; node = new Node(0, 2, v); node_array().add(node);
v[0] = h_e_; node = new Node(1, 2, v); node_array().add(node);
v[0] = 2.0*h_e_; node = new Node(2, 2, v); node_array().add(node);
v[0] = 3.0*h_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] = 2.0*h_e_; node = new Node(6, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(7, 2, v); node_array().add(node);
v[0] = 0.0; v[1] = 2.0*c_; node = new Node(8, 2, v); node_array().add(node);
v[0] = h_e_; node = new Node(9, 2, v); node_array().add(node);
v[0] = 2.0*h_e_; node = new Node(10, 2, v); node_array().add(node);
v[0] = 3.0*h_e_; node = new Node(11, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(12, 2, v); node_array().add(node);
int ena[8]; Omega_eh *elem;
ena[0] = 0; ena[1] = 2; ena[2] = 10; ena[3] = 8; ena[4] = 1; ena[5] = 6; ena[6] = 9; ena[7] = 5;
elem = new Omega_eh(0, 0, 0, 8, ena); omega_eh_array().add(elem);
ena[0] = 2; ena[1] = 4; ena[2] = 12; ena[3] = 10; ena[4] = 3; ena[5] = 7; ena[6] = 11; ena[7] = 6;
elem = new Omega_eh(1, 0, 0, 8, ena); 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) {
for(int j = 8; j <= 20; j++) σ B.C.
for(int k = 0; k < df; k++) { disable all geometrical nodes
the_gh_array[node_order(j)](k) = gh_on_Gamma_h::Dirichlet; }
} else if(i == 1) { u B.C.
the_gh_array[node_order(4)](0) = the_gh_array[node_order(7)](0) = fixed B.C. at right-end
the_gh_array[node_order(12)](0) = the_gh_array[node_order(7)](1) =
gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(8)](0) = gh_on_Gamma_h::Neumann; Bending Moment on left-end
the_gh_array[node_order(8)][0] = -5.0;
the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Neumann;
the_gh_array[node_order(0)][0] = 5.0;
}
}

static const int sigma_ndf = 3; static Omega_h_i oh_sigma(0);


static gh_on_Gamma_h_i sigma_gh(0, sigma_ndf, oh_sigma);
static U_h sigma_h(sigma_ndf, oh_sigma);
static Global_Discretization sigma_gd(oh_sigma, sigma_gh, sigma_h);
static const int u_ndf = 2; static Omega_h_i oh_u(1);
static gh_on_Gamma_h_i u_gh(1, u_ndf, oh_u);
static U_h u_h(u_ndf, oh_u);
static Global_Discretization u_gd(oh_u, u_gh, u_h);
static Global_Discretization_Couple gdc(u_gd, sigma_gd);

472 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
class ElasticQ84_Mixed_Formulation : public Element_Formulation_Couple { Q 4/8 element definition
public:
ElasticQ84_Mixed_Formulation(Element_Type_Register a):Element_Formulation_Couple(a) {}
Element_Formulation *make(int, Global_Discretization&);
ElasticQ84_Mixed_Formulation(int, Global_Discretization&);
Element_Formulation_Couple *make(int, Global_Discretization_Couple&);
ElasticQ84_Mixed_Formulation(int, Global_Discretization_Couple&);
};

Element_Formulation* ElasticQ84_Mixed_Formulation::make(int en,Global_Discretization& gd){


return new ElasticQ84_Mixed_Formulation(en,gd); }
diagonal A-matrix definition
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]);
ElasticQ84_Mixed_Formulation::ElasticQ84_Mixed_Formulation(
int en, Global_Discretization& gd) : Element_Formulation_Couple(en, gd) {
Quadrature qp(2, 9);
H1 Z(2, (double*)0, qp),
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE(int, int, Quadrature", 8, 2, qp),
Serendipity shape functions for integra-
Zai, Eta; Zai &= Z[0]; Eta &= Z[1]; ion and coordinate transformation
N[0] = (1.0-Zai)*(1.0-Eta)/4.0; N[1] = (1.0+Zai)*(1.0-Eta)/4.0; Step 1: initial four corner nodes
N[2] = (1.0+Zai)*(1.0+Eta)/4.0; N[3] = (1.0-Zai)*(1.0+Eta)/4.0;
N[4] = (1.0-Zai.pow(2))*(1.0-Eta)/2.0; N[5] = (1.0-Eta.pow(2))*(1.0+Zai)/2.0;
N[6] = (1.0-Zai.pow(2))*(1.0+Eta)/2.0; N[7] = (1.0-Eta.pow(2))*(1.0-Zai)/2.0; Step 2: add four edge nodes
N[0] = N[0] - (N[4]+N[7])/2.0; N[1] = N[1] - (N[4]+N[5])/2.0; Step 3: correction of four corner nodes
N[2] = N[2] - (N[5]+N[6])/2.0; N[3] = N[3] - (N[6]+N[7])/2.0;
C0 x = MATRIX("int, int, C0&, int, int", 8, 2, xl, 0, 0);
the presence of four edge nodes
H1 X = N*x; J dv(d(X).det());
double sqrt3 = sqrt(3.0); C0 zero(0.0);
H0 n = INTEGRABLE_VECTOR("int, Quadrature", 4/*nen*/, qp), n8, n9, n10, n11;
H0 zai, eta;
Ψ with four nodes at four Gauss
zai &= ((H0)Z[0]); eta &= ((H0)Z[1]); integration points
n[0] = (1.0-sqrt3*zai)*(1.0-sqrt3*eta)/4.0; n[1] = (1.0+sqrt3*zai)*(1.0-sqrt3*eta)/4.0; N0-7 =0 (corresponding to geom. nodes)
n[2] = (1.0+sqrt3*zai)*(1.0+sqrt3*eta)/4.0;
n8 &= n[0]; n9 &= n[1]; n10 &= n[2];
n[3] = (1.0-sqrt3*zai)*(1.0+sqrt3*eta)/4.0;
n11 &= n[3];
N8-11 = Ψa
H0 N_sig = ( (n8 | zero | zero | n9 | zero | zero | n10 | zero | zero | n11 | zero | zero ) & Nσ
(zero | n8 | zero | zero | n9 | zero | zero | n10 | zero | zero | n11 | zero ) &
(zero |(zero | n8) | zero | zero | n9 | zero | zero | n10 | zero | zero | n11 ));
Matrix::Decomposition_Method = Matrix::Cholesky_Decomposition;
C0 D_inv = D.inverse(); D-1
Matrix::Decomposition_Method = Matrix::LU_Decomposition; A = – ∫ Ψ ⊗ ( D –1 Ψ ) dΩ
stiff &= MATRIX("int, int", 36, 36); Ωe
C0 stiff_sub = MATRIX("int, int, C0&, int, int", 12, 12, stiff, 24, 24);
stiff_sub = -((~N_sig) * (D_inv * N_sig)) | dv;
}

Element_Formulation_Couple* ElasticQ84_Mixed_Formulation::make(
off-diagonal C-matrix definition
int en, Global_Discretization_Couple& gdc) {
return new ElasticQ84_Mixed_Formulation(en,gdc);
}
ElasticQ84_Mixed_Formulation::ElasticQ84_Mixed_Formulation(
int en, Global_Discretization_Couple& gdc) : Element_Formulation_Couple(en, gdc) {
Quadrature qp(2, 9);
H1 Z(2, (double*)0, qp),
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 8, 2, qp),
Zai, Eta;
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;

Workbook of Applications in VectorSpace C++ Library 473


Chapter 5 Advanced Finite Element Methods
N[4] = (1.0-Zai.pow(2))*(1.0-Eta)/2.0; N[5] = (1.0-Eta.pow(2))*(1.0+Zai)/2.0;
N[6] = (1.0-Zai.pow(2))*(1.0+Eta)/2.0; N[7] = (1.0-Eta.pow(2))*(1.0-Zai)/2.0;
N[0] = N[0] - (N[4]+N[7])/2.0; N[1] = N[1] - (N[4]+N[5])/2.0;
N[2] = N[2] - (N[5]+N[6])/2.0; N[3] = N[3] - (N[6]+N[7])/2.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, 2, Nx),
wx, wy, B; ∂
------ 0
wx &= w_x[0][0]; wy &= w_x[0][1]; ∂x
C0 zero(0.0);
B = 0 ----- ∂ N
B &= (~wx || zero) & -
(zero || ~wy ) & ∂y
(~wy || ~wx ); ∂ ∂
------ ------
double sqrt3 = sqrt(3.0); ∂y ∂x
H0 n = INTEGRABLE_VECTOR("int, Quadrature", 12/*nen*/, qp),
n8, n9, n10, n11, zai, eta;
zai &= (H0)Z[0]; eta &= (H0)Z[1];
n[0] = (1.0-sqrt3*zai)*(1.0-sqrt3*eta)/4.0; n[1] = (1.0+sqrt3*zai)*(1.0-sqrt3*eta)/4.0;
n[2] = (1.0+sqrt3*zai)*(1.0+sqrt3*eta)/4.0; n[3] = (1.0-sqrt3*zai)*(1.0+sqrt3*eta)/4.0;
n8 &= n[0]; n9 &= n[1]; n10 &= n[2]; n11 &= n[3];
H0 N_sig = ((n8 | zero | zero | n9 | zero | zero | n10 | zero | zero | n11 | zero | zero ) &
(zero | n8 | zero | zero | n9 | zero | zero | n10 | zero | zero | n11 | zero ) &
(zero |(zero | n8) | zero | zero | n9 | zero | zero | n10 | zero | zero | n11 ));
stiff &= MATRIX("int, int", 16, 36);
C0 stiff_sub = MATRIX("int, int, C0&, int, int", 16, 12, stiff, 0, 24);
C = ∫ B ⊗ Ψ dΩ
Ωe
stiff_sub = ((~B)*N_sig) | dv;
}
Element_Formulation* Element_Formulation::type_list = 0;
static Element_Type_Register element_type_register_instance;
static ElasticQ84_Mixed_Formulation
elasticq84_mixed_formulation_instance(element_type_register_instance);
static Matrix_Representation mr(sigma_gd);
static Matrix_Representation_Couple mrc(gdc, 0, 0, &(mr.rhs()), &mr);
int main() {
mr.assembly();
mrc.assembly();
C0 A = ((C0)(mr.lhs())), f_1 = ((C0)(mr.rhs())),
C = ((C0)(mrc.lhs())), f_2 = ((C0)(mrc.rhs()));
Cholesky dnH(-A); take care of negative definitiveness of A
C0 Ainv = -(dnH.inverse());
C0 CAinvCt = C*Ainv*(~C);
LU dCAinvCt(CAinvCt);
cout << "projected Hessian condition number: " << dCAinvCt.cond() << endl; û = (CA-1CT)-1(CA-1f1-f2)
C0 u = dCAinvCt*(C*(-(dnH*f_1)) - f_2);
C0 sigma = -(dnH*(f_1-(~C)*u));
sigma_h = sigma; sigma_h = sigma_gd.gh_on_gamma_h();
cout << "stress(sig-x, sig-y, sig-xy):" << endl; σ̂ = A-1 (f1- CT û )
for(int i = 0; i < sigma_h.total_node_no(); i++)
cout << sigma_h[i] << endl;
u_h = u;
u_h = u_gd.gh_on_gamma_h();
cout << "displacement(u-x, u-y):" << endl << u_h;
return 0;
}

Listing 5•4 Substructure solution for the Hellinger-Reissner variational principle for plane elasticity
(project: “hellinger_reissner_variational_formulation” in project workspace file “fe.dsw”).

474 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
Hu-Washizu Variational Principle
In addition to Eq. 5•37 , the constitutive equations and the strain-displacement relations can be used such as

σ = Dε , and ε = Lu Eq. 5•52

The variational approximation to these two equations are

∫ δ εT ( D ε – σ ) dΩ = 0, and ∫ δ σ T ( Lu – ε ) dΩ = 0 Eq. 5•53


Ω Ω

Eq. 5•53 and Eq. 5•37 are the Euler-Lagange equations of the Lagrangian functional

( ε, σ, u ) = --- ∫ ε T D ε dΩ – ∫ σ T ( ε – Lu ) dΩ – ∫ uT b dΩ – ∫ uT h dΓ
1
Eq. 5•54
2
Ω Ω Ω Γh

The Lagrangian functional in Eq. 5•54 is known as the Hu-Washizu variational principle. The finite element
approximation to the strain field uses the interpolation functions Ξ(x) as

ε e ≡ Ξea ( x ) ε ea Eq. 5•55

The matrix form, at element level, of Eq. 5•37 and Eq. 5•53 is

A CT 0 ε̂ f1
Eq. 5•56
C 0 ET σ̂ = f2
0 E 0 f3

where

A = ∫ Ξ ⊗ ( D Ξ ) dΩ Eq. 5•57
Ωe

E = ∫ B ⊗ Ψ dΩ Eq. 5•58
Ωe

C = – ∫ Ξ ⊗ Ψ dΩ Eq. 5•59
Ωe

f1 = A( n ε ) – CT( nσ) Eq. 5•60


Γ eε Γ eσ

Workbook of Applications in VectorSpace C++ Library 475


Chapter 5 Advanced Finite Element Methods

f2 = –C ( n ε ) – ETu Eq. 5•61


Γ eε
Γ eu

f3 = ∫Φ b dΩ + ∫Φ ( n σ ) dΓ – E ( n σ ) Eq. 5•62
Γ eσ
Ωe Γ eσ

The condition for non-singular matrices is1

n ε + n u ≥ n σ , and n σ ≥ n u Eq. 5•63

The Program Listing 5•5 implements Eq. 5•56 to Eq. 5•62 (project “hu_washizu_variational_principle” in
project workspace file “fe.dsw”). With the same patch test problem for the Hellinger-Reissner variational princi-
ple in the previous section. We use shape functions with four nodes at Gaussian integration points for both stress
(σ) and strain (ε) fields; i.e., Ξ = Ψ. This results in C-matrix in Eq. 5•59 to be symmetrical negative definitive.
Care should be taken, if Cholesky decomposition is used, which is applicable to a symmetrical positive defini-
tive matrix. The displacement (u) shape function Φ is an eight-nodes serendipity element. These choices satisfy
the condition in Eq. 5•63. The coding of matrix substructuring technique supported by “fe.lib” becomes a little
more elaborated with three-fields (ε, σ, u) instead of two-fields (σ, u).
The modification from two-fields problem is minor, however. For the definitions of discretized global
domain and boundary have three index entries as

1 Omega_h_i::Omega_h_i(int i) : Omega_h(0) { // Ωhi


2 if(i == 0 || i == 1) { // Ωhε or Ωhσ
3 ...
4 } else if(i == 2) { // Ωhu
5 ...
6 }
7 gh_on_Gamma_h_i::gh_on_Gamma_h_i(int i, int df, Omega_h& omega_h) : gh_on_Gamma_h() { // Γhi
8 gh_on_Gamma_h::__initialization(df, omega_h);
9 if(i == 0) { // Γhε
10 ...
11 } else if(i == 1) { // Γhσ
12 ...
13 } else if(i == 2) { // Γhu
14 ...
15 }
16 }

The instantiation of global discretized couples are

1. see discussion in p.333-334 in Zienkiewicz, O.C. and R.L. Taylor, 1989, “The finite element method”, 4th ed., vol. 1.
McGraw-Hill, Inc., UK.

476 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods

#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 row_node_no = 5; static const int row_segment_no = row_node_no-1;
static const double L_ = 10.0; static const double c_ = 1.0;
static const double h_e_ = L_/((double)row_segment_no);
static const double E_ = 1.e3; static const double v_ = 0.3;
Omega_h_i::Omega_h_i(int i) : Omega_h(0) {
if(i == 0 || i == 1) { define Ωε, and Ωσ
double inv_sqrt3 = 1.0/sqrt(3.0), v[2], xl[4][2], zai, eta;
Node *node;
xl[0][0] = 0.0; xl[0][1] = 0.0; xl[1][0] = 2.0*h_e_; xl[1][1] = 0.0; elem # 0 nodal coordinates
xl[2][0] = 2.0*h_e_; xl[2][1] = 2.0*c_; xl[3][0] = 0.0; xl[3][1] = 2.0*c_;
zai = - inv_sqrt3; eta = - inv_sqrt3;
for(int j = 0; j < 2; j++)
1st Gauss point natural coordinates
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+ 1st Gauss point physical coordinates
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(0, 2, v); node_array().add(node);
zai = inv_sqrt3; eta = - inv_sqrt3;
2nd Gauss point natural coordinates
for(int j = 0; j < 2; j++) 2nd Gauss point physical coordinates
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+(1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+(1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(1, 2, v); node_array().add(node);
zai = inv_sqrt3; eta = inv_sqrt3; 3rd Gauss point natural coordinates
for(int j = 0; j < 2; j++) 3rd Gauss point physical coordinates
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(2, 2, v); node_array().add(node);
zai = - inv_sqrt3; eta = inv_sqrt3; 4th Gauss point natural coordinates
for(int j = 0; j < 2; j++)
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
4th Gauss point physical coordinates
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(3, 2, v); node_array().add(node);
xl[0][0] = 2.0*h_e_; xl[0][1] = 0.0; xl[1][0] = 4.0*h_e_; xl[1][1] = 0.0;
xl[2][0] = 4.0*h_e_; xl[2][1] = 2.0*c_; xl[3][0] = 2.0*h_e_; xl[3][1] = 2.0*c_;
elem # 1 nodal coordinates
zai = - inv_sqrt3; eta = - inv_sqrt3; 1st Gauss point natural coordinates
for(int j = 0; j < 2; j++) 1st Gauss point physical coordinates
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(4, 2, v); node_array().add(node);
zai = inv_sqrt3; eta = - inv_sqrt3; 2nd Gauss point natural coordinates
for(int j = 0; j < 2; j++)
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
2nd Gauss point physical coordinates
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(5, 2, v); node_array().add(node);
zai = inv_sqrt3; eta = inv_sqrt3;
for(int j = 0; j < 2; j++)
3rd Gauss point natural coordinates
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+ 3rd Gauss point physical coordinates
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(6, 2, v); node_array().add(node);
zai = - inv_sqrt3; eta = inv_sqrt3;
4th Gauss point natural coordinates
for(int j = 0; j < 2; j++) 4th Gauss point physical coordinates
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(7, 2, v); node_array().add(node);

Workbook of Applications in VectorSpace C++ Library 477


Chapter 5 Advanced Finite Element Methods
v[0] = 0.0; v[1] = 0.0; node = new Node(8, 2, v); node_array().add(node); “geometrical nodes” supply four corner
v[0] = 1.0*h_e_; node = new Node(9, 2, v); node_array().add(node);
v[0] = 2.0*h_e_; node = new Node(10, 2, v); node_array().add(node); nodes coordinates to the element level
v[0] = 3.0*h_e_; node = new Node(11, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(12, 2, v); node_array().add(node);
v[0] = 0.0; v[1] = 1.0*c_; node = new Node(13, 2, v); node_array().add(node);
v[0] = 2.0*h_e_; node = new Node(14, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(15, 2, v); node_array().add(node);
v[0] = 0.0; v[1] = 2.0*c_; node = new Node(16, 2, v); node_array().add(node);
v[0] = 1.0*h_e_; node = new Node(17, 2, v); node_array().add(node);
v[0] = 2.0*h_e_; node = new Node(18, 2, v); node_array().add(node);
v[0] = 3.0*h_e_; node = new Node(19, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(20, 2, v); node_array().add(node);
int ena[12]; Omega_eh *elem;
ena[0] = 8; ena[1] = 10; ena[2] = 18; ena[3] = 16; ena[4] = 9; ena[5] = 14; define elements
ena[6] = 17; ena[7] = 13; ena[8] = 0; ena[9] = 1; ena[10] = 2; ena[11] = 3; first 8 nodes are geometrical nodes
elem = new Omega_eh(0, 0, 0, 12, ena); omega_eh_array().add(elem); last four nodes are real nodes
ena[0] = 10; ena[1] = 12; ena[2] = 20; ena[3] = 18; ena[4] = 11; ena[5] = 15;
ena[6] = 19; ena[7] = 14; ena[8] = 4; ena[9] = 5; ena[10] = 6; ena[11] = 7;
elem = new Omega_eh(1, 0, 0, 12, ena); omega_eh_array().add(elem);
} else if(i == 2) { Ωu
double v[2]; Node *node;
v[0] = 0.0; v[1] = 0.0; node = new Node(0, 2, v); node_array().add(node); 8-node serendipity element
v[0] = h_e_; node = new Node(1, 2, v); node_array().add(node); define nodes
v[0] = 2.0*h_e_; node = new Node(2, 2, v); node_array().add(node);
v[0] = 3.0*h_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] = 2.0*h_e_; node = new Node(6, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(7, 2, v); node_array().add(node);
v[0] = 0.0; v[1] = 2.0*c_; node = new Node(8, 2, v); node_array().add(node);
v[0] = h_e_; node = new Node(9, 2, v); node_array().add(node);
v[0] = 2.0*h_e_; node = new Node(10, 2, v); node_array().add(node);
v[0] = 3.0*h_e_; node = new Node(11, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(12, 2, v); node_array().add(node);
int ena[8]; Omega_eh *elem; define elements
ena[0] = 0; ena[1] = 2; ena[2] = 10; ena[3] = 8; ena[4] = 1; ena[5] = 6; ena[6] = 9; ena[7] = 5;
elem = new Omega_eh(0, 0, 0, 8, ena); omega_eh_array().add(elem);
ena[0] = 2; ena[1] = 4; ena[2] = 12; ena[3] = 10; ena[4] = 3; ena[5] = 7; ena[6] = 11; ena[7] = 6;
elem = new Omega_eh(1, 0, 0, 8, ena); 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); Ωε B.C.
if(i == 0) { disable all geometrical nodes
for(int j = 8; j <= 20; j++)
for(int k = 0; k < df; k++) the_gh_array[node_order(j)](k) = gh_on_Gamma_h::Dirichlet;
} else if(i == 1) { Ωσ B.C.
for(int j = 8; j <= 20; j++) disable all geometrical nodes
for(int k = 0; k < df; k++) the_gh_array[node_order(j)](k) = gh_on_Gamma_h::Dirichlet;
} else if(i == 2) {
the_gh_array[node_order(4)](0) = the_gh_array[node_order(7)](0) = Wu boundary condition
the_gh_array[node_order(12)](0) = the_gh_array[node_order(7)](1) =
gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(8)](0) = the_gh_array[node_order(0)](0) =
gh_on_Gamma_h::Neumann; Simpson’s integration rule:
the_gh_array[node_order(8)][0] = -5.0; {-15 *2/3, 0*4/3, 15*2./3}
the_gh_array[node_order(0)][0] = 5.0;
} = {-5, 0, 5}
}

478 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
static const int epsilon_ndf = 3; static Omega_h_i oh_epsilon(0); Ωε
static gh_on_Gamma_h_i epsilon_gh(0, epsilon_ndf, oh_epsilon);
static U_h epsilon_h(epsilon_ndf, oh_epsilon);
g.d. ε for defining A matrix
static Global_Discretization *epsilon_type = new Global_Discretization; εh = {εx, εy, γ}T
static Global_Discretization epsilon_gd(oh_epsilon, epsilon_gh, epsilon_h, epsilon_type);
static const int sigma_ndf = 3; static Omega_h_i oh_sigma(1);
static gh_on_Gamma_h_i sigma_gh(1, sigma_ndf, oh_sigma);
Ωσ
static U_h sigma_h(sigma_ndf, oh_sigma); σh = {σx, σy, τ}T
static Global_Discretization sigma_gd(oh_sigma, sigma_gh, sigma_h);
static const int u_ndf = 2; static Omega_h_i oh_u(2);
static gh_on_Gamma_h_i u_gh(2, u_ndf, oh_u); static U_h u_h(u_ndf, oh_u);
Ωu , uh = {u, v}T
static Global_Discretization *u_type = new Global_Discretization; g.d. u for defining irreducible K matrix
static Global_Discretization u_gd(oh_u, u_gh, u_h, u_type);
static Global_Discretization_Couple *sigma_epsilon = new Global_Discretization_Couple();
static Global_Discretization_Couple gdc_sigma_epsilon(sigma_gd, epsilon_gd, sigma_epsilon);
g.d.couple {σ-ε} for defining C matrix
static Global_Discretization_Couple *u_sigma = new Global_Discretization_Couple();
static Global_Discretization_Couple gdc_u_sigma(u_gd, sigma_gd, u_sigma); g.d.couple {u-σ} for defining E matrix
class ElasticQ84_Mixed_Formulation : public Element_Formulation_Couple {
public:
ElasticQ84_Mixed_Formulation(Element_Type_Register a):Element_Formulation_Couple(a) {}
Element_Formulation *make(int, Global_Discretization&); diagonals A & K matrices
ElasticQ84_Mixed_Formulation(int, Global_Discretization&);
Element_Formulation_Couple *make(int, Global_Discretization_Couple&);
ElasticQ84_Mixed_Formulation(int, Global_Discretization_Couple&); off-diagonals C & E matrices
};
Element_Formulation* ElasticQ84_Mixed_Formulation::make(
int en,Global_Discretization& gd) { return new ElasticQ84_Mixed_Formulation(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]);
ElasticQ84_Mixed_Formulation::ElasticQ84_Mixed_Formulation(
int en, Global_Discretization& gd) : Element_Formulation_Couple(en, gd) { diagonals matrices
if(gd.type() == epsilon_type) { A matrix formulation
Quadrature qp(2, 9);
H1 Z(2, (double*)0, qp), Zai, Eta,
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 8, 2, qp);
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;
eight nodes serendipity shape function
N[4] = (1.0-Zai.pow(2))*(1.0-Eta)/2.0; N[5] = (1.0-Eta.pow(2))*(1.0+Zai)/2.0;
N[6] = (1.0-Zai.pow(2))*(1.0+Eta)/2.0; N[7] = (1.0-Eta.pow(2))*(1.0-Zai)/2.0;
N[0] = N[0] - (N[4]+N[7])/2.0; N[1] = N[1] - (N[4]+N[5])/2.0;
N[2] = N[2] - (N[5]+N[6])/2.0; N[3] = N[3] - (N[6]+N[7])/2.0;
C0 x = MATRIX("int, int, C0&, int, int", 8, 2, xl, 0, 0);
H1 X = N*x; J dv(d(X).det()); double sqrt3 = sqrt(3.0); C0 zero(0.0);
H0 n = INTEGRABLE_VECTOR("int, Quadrature", 4, qp), n8, n9, n10, n11, zai, eta;
zai &= ((H0)Z[0]); eta &= ((H0)Z[1]);
n[0] = (1.0-sqrt3*zai)*(1.0-sqrt3*eta)/4.0; n[1] = (1.0+sqrt3*zai)*(1.0-sqrt3*eta)/4.0;
n[2] = (1.0+sqrt3*zai)*(1.0+sqrt3*eta)/4.0; n[3] = (1.0-sqrt3*zai)*(1.0+sqrt3*eta)/4.0;
n8 &= n[0]; n9 &= n[1]; n10 &= n[2]; n11 &= n[3];
H0 N_epsilon = ((n8 | zero | zero | n9 | zero | zero | n10 | zero | zero | n11 | zero | zero ) &
εh = {εx, εy, γ}T
(zero | n8 | zero | zero | n9 | zero | zero | n10 | zero | zero | n11 | zero ) &
(zero |(zero | n8) | zero | zero | n9 | zero | zero | n10 | zero | zero | n11 ));

∫ Ξ ⊗ ( D Ξ ) dΩ
stiff &= MATRIX("int, int", 36, 36);
C0 stiff_sub = MATRIX("int, int, C0&, int, int", 12, 12, stiff, 24, 24);
A =
stiff_sub = ((~N_epsilon) * (D * N_epsilon)) | dv; Ωe
} else { K matrix; for iterative method to esti-
Quadrature qp(2, 9);
H1 Z(2, (double*)0, qp), , Zai, Eta
mate initial u values
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int, int, Quadrature", 8, 2, qp);

Workbook of Applications in VectorSpace C++ Library 479


Chapter 5 Advanced Finite Element Methods
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;
N[4] = (1.0-Zai.pow(2))*(1.0-Eta)/2.0; N[5] = (1.0-Eta.pow(2))*(1.0+Zai)/2.0;
N[6] = (1.0-Zai.pow(2))*(1.0+Eta)/2.0; N[7] = (1.0-Eta.pow(2))*(1.0-Zai)/2.0;
N[0] -= (N[4]+N[7])/2.0; N[1] -= (N[4]+N[5])/2.0;
N[2] -= (N[5]+N[6])/2.0; N[3] -= (N[6]+N[7])/2.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, 2, Nx), wx, wy, B;
wx &= w_x[0][0]; wy &= w_x[0][1]; C0 zero(0.0);
B &= (~wx || zero) & (zero || ~wy ) & (~wy || ~wx );
K is a standard stiffness matrix
stiff &= ((~B)*D*B)|dv;
}
}
Element_Formulation_Couple* ElasticQ84_Mixed_Formulation::make(int en,
Global_Discretization_Couple& gdc) { return new ElasticQ84_Mixed_Formulation(en,gdc); } off-diagonal matrices
ElasticQ84_Mixed_Formulation::ElasticQ84_Mixed_Formulation(int en,
Global_Discretization_Couple& gdc) : Element_Formulation_Couple(en, gdc) {
if(gdc.type() == sigma_epsilon) {
C matrux formulation
Quadrature qp(2, 9);
H1 Z(2, (double*)0, qp), Zai, Eta,
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature",8,2, qp);
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;
N[4] = (1.0-Zai.pow(2))*(1.0-Eta)/2.0; N[5] = (1.0-Eta.pow(2))*(1.0+Zai)/2.0;
N[6] = (1.0-Zai.pow(2))*(1.0+Eta)/2.0; N[7] = (1.0-Eta.pow(2))*(1.0-Zai)/2.0;
N[0] = N[0] - (N[4]+N[7])/2.0; N[1] = N[1] - (N[4]+N[5])/2.0;
N[2] = N[2] - (N[5]+N[6])/2.0; N[3] = N[3] - (N[6]+N[7])/2.0;
H1 X = N*xl; J dv(d(X).det());
double sqrt3 = sqrt(3.0); C0 zero(0.0);
H0 n = INTEGRABLE_VECTOR("int, Quadrature", 4, qp), n8, n9, n10, n11, zai, eta;
zai &= ((H0)Z[0]); eta &= ((H0)Z[1]);
n[0] = (1.0-sqrt3*zai)*(1.0-sqrt3*eta)/4.0; n[1] = (1.0+sqrt3*zai)*(1.0-sqrt3*eta)/4.0;
n[2] = (1.0+sqrt3*zai)*(1.0+sqrt3*eta)/4.0; n[3] = (1.0-sqrt3*zai)*(1.0+sqrt3*eta)/4.0;
n8 &= n[0]; n9 &= n[1]; n10 &= n[2]; n11 &= n[3];
H0 N_sig = ((n8 | zero | zero | n9 | zero | zero | n10 | zero | zero | n11 | zero | zero ) & σh = {σx, σy, τ}T
(zero | n8 | zero | zero | n9 | zero | zero | n10 | zero | zero | n11 | zero ) &
(zero |(zero | n8) | zero | zero | n9 | zero | zero | n10 | zero | zero | n11 ));
H0 N_epsilon; N_epsilon &= N_sig;
stiff &= MATRIX("int, int", 36, 36); C = – ∫ Ξ ⊗ Ψ dΩ
C0 stiff_sub = MATRIX("int, int, C0&, int, int", 12, 12, stiff, 24, 24);
Ωe
stiff_sub = -((~N_sig)*N_epsilon) | dv;
} else { E matrux formulation
Quadrature qp(2, 9);
H1 Z(2, (double*)0, qp), Zai, Eta,
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE( "int, int, Quadrature",8,2, qp);
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;
N[4] = (1.0-Zai.pow(2))*(1.0-Eta)/2.0; N[5] = (1.0-Eta.pow(2))*(1.0+Zai)/2.0;
N[6] = (1.0-Zai.pow(2))*(1.0+Eta)/2.0; N[7] = (1.0-Eta.pow(2))*(1.0-Zai)/2.0;
N[0] = N[0] - (N[4]+N[7])/2.0; N[1] = N[1] - (N[4]+N[5])/2.0;
N[2] = N[2] - (N[5]+N[6])/2.0; N[3] = N[3] - (N[6]+N[7])/2.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, 2, Nx), wx, wy, B;
wx &= w_x[0][0]; wy &= w_x[0][1]; C0 zero(0.0);
B &= (~wx || zero) &
(zero || ~wy ) &
(~wy || ~wx );

480 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
double sqrt3 = sqrt(3.0);
H0 n = INTEGRABLE_VECTOR("int, Quadrature", 4, qp), n8, n9, n10, n11, zai, eta;
zai &= ((H0)Z[0]); eta &= ((H0)Z[1]);
n[0] = (1.0-sqrt3*zai)*(1.0-sqrt3*eta)/4.0; n[1] = (1.0+sqrt3*zai)*(1.0-sqrt3*eta)/4.0;
n[2] = (1.0+sqrt3*zai)*(1.0+sqrt3*eta)/4.0; n[3] = (1.0-sqrt3*zai)*(1.0+sqrt3*eta)/4.0;

σh = {σx, σy, τ}T


n8 &= n[0]; n9 &= n[1]; n10 &= n[2]; n11 &= n[3];
H0 N_sig = ((n8 | zero | zero | n9 | zero | zero | n10 | zero | zero | n11 | zero | zero ) &
(zero | n8 | zero | zero | n9 | zero | zero | n10 | zero | zero | n11 | zero ) &
(zero |(zero | n8) | zero | zero | n9 | zero | zero | n10 | zero | zero | n11 ));
stiff &= MATRIX("int, int", 16, 36);
C0 stiff_sub = MATRIX("int, int, C0&, int, int", 16, 12, stiff, 0, 24); E = ∫ B ⊗ Ψ dΩ
stiff_sub = ((~B)*N_sig) | dv; Ωe
}
}
Element_Formulation* Element_Formulation::type_list = 0;
static Element_Type_Register element_type_register_instance;
static ElasticQ84_Mixed_Formulation
elasticq84_mixed_formulation_instance(element_type_register_instance);
static Matrix_Representation mr(epsilon_gd); for A matrix
static Matrix_Representation_Couple mrcC(gdc_sigma_epsilon, 0, 0, &(mr.rhs()), &mr); for C matrix
static Matrix_Representation_Couple mrcE(gdc_u_sigma, 0, 0, &(mrcC.rhs()), &mrcC); for E matrix
int main() {
mrcC.assembly();
mr.assembly();
mrcE.assembly();
C0 A = ((C0)(mr.lhs())),
f_1 = ((C0)(mr.rhs())),
C = ((C0)(mrcC.lhs())),
f_2 = ((C0)(mrcC.rhs())),
E = ((C0)(mrcE.lhs())),
f_3 = ((C0)(mrcE.rhs()));
Cholesky dnC(-C);
C0 Cinv = -(dnC.inverse()), C-1
CinvA = Cinv*A,
CinvACinv = Cinv*A*Cinv,
ECinvACinv = E*CinvACinv, EC-1 AC-1 ET
ECinvACinvEt = ECinvACinv*(~E); û = (EC-1 AC-1 ET)-1
Cholesky dECinvACinvEt(ECinvACinvEt);
C0 u = dECinvACinvEt * (f_3 - (E*Cinv)*f_1 + ECinvACinv*f_2); (f3 - EC-1 f1 + EC-1 AC-1 f2)
C0 epsilon = -(dnC*(f_2-(~E)*u));
C0 sigma = -(dnC*(f_1-A*epsilon));
epsilon_h = epsilon;
ε̂ = C-1(f2-ET û ) and σ̂ = C-1(f1-Aε̂ )
epsilon_h = epsilon_gd.gh_on_gamma_h();
cout << "strains(epsilon-x, epsilon-y, epsilon-xy):" << endl;
for(int i = 0; i < epsilon_h.total_node_no(); i++)
cout << epsilon_h[i] << endl;
sigma_h = sigma;
sigma_h = sigma_gd.gh_on_gamma_h();
cout << "stress(sig-x, sig-y, sig-xy):" << endl;
for(int i = 0; i < sigma_h.total_node_no(); i++)
cout << sigma_h[i] << endl;
u_h = u; u_h = u_gd.gh_on_gamma_h();
cout << "displacement(u-x, u-y):" << endl<< u_h << endl;
return 0;
}

Listing 5•5 Substructure method for the three-field Hu-Washizu variational principle for plane elasticity
(project: “hu_wahsizu_variational_formulation” in project workspace file “fe.dsw”).

Workbook of Applications in VectorSpace C++ Library 481


Chapter 5 Advanced Finite Element Methods
1 static const int epsilon_ndf = 3; // ndfε
2 static Omega_h_i oh_epsilon(0); // Ωhε
3 static gh_on_Gamma_h_i epsilon_gh(0, epsilon_ndf, oh_epsilon); // Γhε
4 static U_h epsilon_h(epsilon_ndf, oh_epsilon); // εx, εy εxy
5 static Global_Discretization *epsilon_type = new Global_Discretization;
6 static Global_Discretization epsilon_gd(oh_epsilon, epsilon_gh, epsilon_h, epsilon_type);
7 static const int sigma_ndf = 3; // ndfσ
8 static Omega_h_i oh_sigma(1); // Ωhσ
9 static gh_on_Gamma_h_i sigma_gh(1, sigma_ndf, oh_sigma); // Γhσ
10 static U_h sigma_h(sigma_ndf, oh_sigma); // σx, σy σxy
11 static Global_Discretization sigma_gd(oh_sigma, sigma_gh, sigma_h);
12 static const int u_ndf = 2; // ndfu
13 static Omega_h_i oh_u(2); // Ωhu
14 static gh_on_Gamma_h_i u_gh(2, u_ndf, oh_u); // Γhu
15 static U_h u_h(u_ndf, oh_u); // ux, uy
16 static Global_Discretization *u_type = new Global_Discretization;
17 static Global_Discretization u_gd(oh_u, u_gh, u_h, u_type);
18 static Global_Discretization_Couple *sigma_epsilon = new Global_Discretization_Couple(); // σ−ε
19 static Global_Discretization_Couple gdc_sigma_epsilon(sigma_gd, epsilon_gd, sigma_epsilon);
20 static Global_Discretization_Couple *u_sigma = new Global_Discretization_Couple(); // u-σ
21 static Global_Discretization_Couple gdc_u_sigma(u_gd, sigma_gd, u_sigma);

Three pointers to Global_Discretization and Global_Discretization_Coupe, epsilon_type, u_type, and


sigma_epsilon, are to be used in Element_Formulation class definitions to switch to corresponding segments of
submatrices definitions.

1 class ElasticQ84_Mixed_Formulation : public Element_Formulation_Couple {


2 public:
3 ElasticQ84_Mixed_Formulation(Element_Type_Register a) : Element_Formulation_Couple(a) {}
4 Element_Formulation *make(int, Global_Discretization&); // diagonal submatrices
5 ElasticQ84_Mixed_Formulation(int, Global_Discretization&);
6 Element_Formulation_Couple *make(int, Global_Discretization_Couple&);// off-diagonal submatrices
7 ElasticQ84_Mixed_Formulation(int, Global_Discretization_Couple&);
8 };
9 Element_Formulation* ElasticQ84_Mixed_Formulation::make(int en, Global_Discretization& gd) {
10 return new ElasticQ84_Mixed_Formulation(en,gd);
11 }
12 ElasticQ84_Mixed_Formulation::ElasticQ84_Mixed_Formulation(int en, Global_Discretization& gd) :
13 Element_Formulation_Couple(en, gd) {
14 if(gd.type() == epsilon_type) { // define A-Matrix; ε field
15 ...
16 } else { // K-matrix; u-field, for iterative method to estimate initial u values, Z&T p. 361, Eq. 12.99
17 ...
18 }
19 }

482 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
20 Element_Formulation_Couple* ElasticQ84_Mixed_Formulation::make(
21 int en, Global_Discretization_Couple& gdc) { return new ElasticQ84_Mixed_Formulation(en,gdc); }
22 ElasticQ84_Mixed_Formulation::ElasticQ84_Mixed_Formulation(
23 int en, Global_Discretization_Couple& gdc) : Element_Formulation_Couple(en, gdc) {
24 if(gdc.type() == sigma_epsilon) { // define C-Matrix; σ−ε fields
25 ...
26 } else { // define E-matrix; u-σ fields
27 ...
28 }
29 }

Line 16 is to construct the “convergence acceleration matrix” if iterative method is used.1 The global solution is
proceeded with û to be solved first as

û = (EC-1 AC-1 ET)-1 (f3 - EC-1 f1 + EC-1 AC-1 f2) Eq. 5•64

After û is obtained, we can substituting û in the following equation

ε̂ = C-1 (f2 - ET û ) Eq. 5•65

Then, σ̂ is solved by substituting ε̂ in

σ̂ = C-1 (f1 - A ε̂ ) Eq. 5•66

The Matrix_Representations are in instantiated in the followings with the global substructuring solution

1 int main() {
2 Matrix_Representation mr(epsilon_gd);
3 Matrix_Representation_Couple mrcC(gdc_sigma_epsilon, 0, 0, &(mr.rhs()), &mr);
4 Matrix_Representation_Couple mrcE(gdc_u_sigma, 0, 0, &(mrcC.rhs()), &mrcC);
5 mrcC.assembly();
6 mr.assembly();
7 mrcE.assembly();
8 C0 A = ((C0)(mr.lhs())), f_1 = ((C0)(mr.rhs())),
9 C = ((C0)(mrcC.lhs())), f_2 = ((C0)(mrcC.rhs())),
10 E = ((C0)(mrcE.lhs())), f_3 = ((C0)(mrcE.rhs()));
11 Cholesky dnC(-C); // decomposition; C is symmetrical negative definite
12 C0 Cinv = -(dnC.inverse()); // û = (EC-1AC-1ET)-1 (f3-EC-1f1+ EC-1AC-1f2)
13 C0 CinvA = Cinv*A;
14 C0 CinvACinv = CinvA*Cinv;

1. see next section and p. 361 in Zienkiewicz, O.C. and R.L. Taylor, 1989, “The finite element method”, 4th ed., vol. 1.
McGraw-Hill, Inc., UK.

Workbook of Applications in VectorSpace C++ Library 483


Chapter 5 Advanced Finite Element Methods
15 C0 ECinvACinv = E*CinvACinv;
16 C0 ECinvACinvEt = ECinvACinv*(~E);
17 Cholesky dECinvACinvEt(ECinvACinvEt);
18 C0 u = dECinvACinvEt * (f_3 - (E*Cinv)*f_1 + ECinvACinv*f_2);
19 C0 epsilon = -(dnC*(f_2-(~E)*u)); // ε̂ = C-1(f2-ET û )
20 C0 sigma = -(dnC*(f_1-A*epsilon)); // σ̂ = C-1(f1-A ε̂ )
21 epsilon_h = epsilon; // update free degree of freedom
22 epsilon_h = epsilon_gd.gh_on_gamma_h(); // update fixed degree of freedom
23 cout << "strains(epsilon-x, epsilon-y, epsilon-xy):" << endl;
24 for(int i = 0; i < epsilon_h.total_node_no(); i++)
25 cout << epsilon_h[i] << endl;
26 sigma_h = sigma; // update free degree of freedom
27 sigma_h = sigma_gd.gh_on_gamma_h(); // update fixed degree of freedom
28 cout << "stress(sig-x, sig-y, sig-xy):" << endl;
29 for(int i = 0; i < sigma_h.total_node_no(); i++)
30 cout << sigma_h[i] << endl;
31 u_h = u; // update free degree of freedom
32 u_h = u_gd.gh_on_gamma_h(); // update fixed degree of freedom
33 cout << "displacement(u-x, u-y):" << endl << u_h;
34 return 0;
35}

The tip-deflection solution for the bending problem is the same as the one solved in the higher-order patch test
case. They are both exact.

Iterative Method for Hu-Washizu Mixed Formulation


Starting from zero initial values

σ̂ ε̂
0 0
= = û 0 = 0 Eq. 5•67

The irreducible stiffness matrix K is defined as

K = ∫ B T DBdΩ Eq. 5•68


The iterative procedure starts from using the stiffness matrix from the irreducible formulation as the conver-
gence acceleration matrix

Kδû = r n Eq. 5•69

where δ û = û n+1 - û n, therefore, the next iteration solution û n+1 can be obtained. The computation of û 1 from
û 0(=0) is the same as the solution from the standard irreducible formulation. rn is defined as the residual of the
third equation in Eq. 5•56 as

484 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods

r n ≡ E T σ̂ – f 3
n
Eq. 5•70

the next iterative solutions for strain ε̂ n+1, and stress σ̂ n+1 are computed from

ε̂ σ̂ = D ε̂
n+1 n+1 n+1
= C T Eû n + 1, then Eq. 5•71

We test this iterative procedure by setting the Poisson ratio ν = 0.5-10-12, which is nearly incompressible. In
addition, the plane strain is assumed. The same project “hu_washizu_variational_principle” in project workspace
file “fe.dsw” with macro definitions “__TEST_NEARLY_INCOMPRESSIBLE_PLANE_STRAIN” and
“__TEST_AUGMENTED_LAGRANGIAN_ITERATIVE_METHOD” are set at the compile time. The initial
tip-deflection solution is the same as the one from irreducible formulation, which is expected not to be the final
answer due to the nearly incompressibility under plane strain condition. After several iterations the solution con-
verged and terminated with a energy norm smaller than 10-15 times its initial energy.

Incompressible u-p Formulation


From Eq. 4•216 in Chapter 4, the pressure-volumetric strain constitutive equation for nearly incompressible
material (ν → 0.5) is

p ≡ Kεv = K m • ε Eq. 5•72

A weighted residual statement to Eq. 5•72 is

m • ε – ---- dΩ
p
∫ ( δp ) T K
Eq. 5•73

where δp is the variation of p. From Eq. 4•220 in Chapter 4, we have the element stiffness matrix

k = ∫ ε ( δu ) T σ ( u )dΩ = ∫ ε ( δu )T [ σd ( u ) + mp ( u ) ]dΩ
Ω Ω

∫ ε ( δu ) T µ  D0 – --- m ⊗ m ε ( u ) + mp ( u ) dΩ
2
=
 
Eq. 5•74
3

where the variation of u is denoted as δu. Notice that the equilibrium equation is the equilibrium of internal
forces and external forces. The weak statement of the equilibrium equation gives

∫ ε ( δu ) T µ  D 0 – --- m ⊗ m ε ( u ) + mp ( u ) dΩ =
2
3 ∫ ( δu ) T b dΩ + ∫ ( δu ) T t dΓ Eq. 5•75
Ω Ω Γ

The displacement u and pressure p are approximated as

Workbook of Applications in VectorSpace C++ Library 485


Chapter 5 Advanced Finite Element Methods

u = N u û , and p = N p p̂ Eq. 5•76

Substituting into Eq. 5•75 and Eq. 5•72 gives in matrix form at element level is

A C T û = f 1 Eq. 5•77
C V p̂ f2

where

2
A = ∫ BTµ D 0 – --- m ⊗ m BdΩ
3
Eq. 5•78
Ωe

C = ∫ ( mNp ) T BdΩ Eq. 5•79


Ωe

Np ⊗ Np
V = – ∫ --------------------- dΩ Eq. 5•80
K
Ωe

f1 = ∫ NuT b dΩ + ∫ NuT t dΓ – Au Γ e
– CTp Γe
Eq. 5•81
Ωe Γe

f 2 = – Cu Γe
– Vp Γe
Eq. 5•82

The solution to substructuring of Eq. 5•77 is proceeded from its second equation, using symmetrical negative
definitiveness of V,

p̂ = V – 1 ( f2 – Cû ) Eq. 5•83

Substituting into the first equation of Eq. 5•77 gives

Aû + C T V –1 ( f 2 – Cû ) = f 1 Eq. 5•84

That is,

û = [ A – C T V –1 C ] – 1 [ f1 – C T V –1 f 2 ] Eq. 5•85

Therefore, we first solve û , and then substituting û back to Eq. 5•83 for recovering p̂ . Program Listing 5•6
(project “incompressible_u_p_formulation” in project workspace file “fe.dsw”) implements the Q 4/9 element
(with an additional center u-node) and test problem in the higher-order patch test.

486 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
#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 row_node_no = 5; static const int row_segment_no = row_node_no-1;
static const double L_ = 10.0; static const double c_ = 1.0;
static const double h_e_ = L_/((double)row_segment_no); // half element size
static const double E_ = 1.e3; 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_); plane stress modification
static const double K_ = lambda_bar+2.0/3.0*mu_;
Omega_h_i::Omega_h_i(int i) : Omega_h(0) {
if(i == 0) { double v[2]; Node *node; define Ωu
v[0] = 0.0; v[1] = 0.0; node = new Node(0, 2, v); node_array().add(node); 1st row
v[0] = h_e_; node = new Node(1, 2, v); node_array().add(node);
v[0] = 2.0*h_e_; node = new Node(2, 2, v); node_array().add(node);
v[0] = 3.0*h_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);
2nd row
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_; node = new Node(11, 2, v); node_array().add(node); 3rd row
v[0] = 2.0*h_e_; node = new Node(12, 2, v); node_array().add(node);
v[0] = 3.0*h_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);
int ena[9]; Omega_eh *elem; Lagrangian 9-nodes element
ena[0] = 0; ena[1] = 2; ena[2] = 12; ena[3] = 10; ena[4] = 1;
ena[5] = 7; ena[6] = 11; ena[7] = 5; ena[8] = 6;
elem = new Omega_eh(0, 0, 0, 9, ena); omega_eh_array().add(elem);
ena[0] = 2; ena[1] = 4; ena[2] = 14; ena[3] = 12; ena[4] = 3;
ena[5] = 9; ena[6] = 13; ena[7] = 7; ena[8] = 8;
elem = new Omega_eh(1, 0, 0, 9, ena); omega_eh_array().add(elem);
} else if(i == 1) { double inv_sqrt3 = 1.0/sqrt(3.0), v[2], xl[4][2], zai, eta; Node *node;
define Ωp
xl[0][0] = 0.0; xl[0][1] = 0.0; xl[1][0] = 2.0*h_e_; xl[1][1] = 0.0; elem # 0 nodal coordinates
xl[2][0] = 2.0*h_e_; xl[2][1] = 2.0*c_; xl[3][0] = 0.0; xl[3][1] = 2.0*c_;
zai = - inv_sqrt3; eta = - inv_sqrt3;
for(int j = 0; j < 2; j++)
1st Gauss point natural coordinates
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+ 1st Gauss point physical coordinates
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(0, 2, v); node_array().add(node);
zai = inv_sqrt3; eta = - inv_sqrt3;
2nd Gauss point natural coordinates
for(int j = 0; j < 2; j++) 2nd Gauss point physical coordinates
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(1, 2, v); node_array().add(node);
zai = inv_sqrt3; eta = inv_sqrt3; 3rd Gauss point natural coordinates
for(int j = 0; j < 2; j++) 3rd Gauss point physical coordinates
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(2, 2, v); node_array().add(node);
zai = - inv_sqrt3; eta = inv_sqrt3; 4th Gauss point natural coordinates
for(int j = 0; j < 2; j++)
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
4th Gauss point physical coordinates
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(3, 2, v); node_array().add(node);

Workbook of Applications in VectorSpace C++ Library 487


Chapter 5 Advanced Finite Element Methods
xl[0][0] = 2.0*h_e_; xl[0][1] = 0.0; xl[1][0] = 4.0*h_e_; xl[1][1] = 0.0; elem # 1 nodal coordinates
xl[2][0] = 4.0*h_e_; xl[2][1] = 2.0*c_; xl[3][0] = 2.0*h_e_; xl[3][1] = 2.0*c_;
zai = - inv_sqrt3; eta = - inv_sqrt3;
1st Gauss point natural coordinates
for(int j = 0; j < 2; j++) 1st Gauss point physical coordinates
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(4, 2, v); node_array().add(node);
zai = inv_sqrt3; eta = - inv_sqrt3; 2nd Gauss point natural coordinates
for(int j = 0; j < 2; j++) 2nd Gauss point physical coordinates
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(5, 2, v); node_array().add(node);
zai = inv_sqrt3; eta = inv_sqrt3; 3rd Gauss point natural coordinates
for(int j = 0; j < 2; j++)
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
3rd Gauss point physical coordinates
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(6, 2, v); node_array().add(node); 4th Gauss point natural coordinates
zai = - inv_sqrt3; eta = inv_sqrt3;
for(int j = 0; j < 2; j++)
4th Gauss point physical coordinates
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(7, 2, v); node_array().add(node);
v[0] = 0.0; v[1] = 0.0; node = new Node(8, 2, v); node_array().add(node);
v[0] = 1.0*h_e_; node = new Node(9, 2, v); node_array().add(node); geometrical nodes
v[0] = 2.0*h_e_; node = new Node(10, 2, v); node_array().add(node);
v[0] = 3.0*h_e_; node = new Node(11, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(12, 2, v); node_array().add(node);
v[0] = 0.0; v[1] = 1.0*c_; node = new Node(13, 2, v); node_array().add(node);
v[0] = 1.0*h_e_; node = new Node(14, 2, v); node_array().add(node);
v[0] = 2.0*h_e_; node = new Node(15, 2, v); node_array().add(node);
v[0] = 3.0*h_e_; node = new Node(16, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(17, 2, v); node_array().add(node);
v[0] = 0.0; v[1] = 2.0*c_; node = new Node(18, 2, v); node_array().add(node);
v[0] = 1.0*h_e_; node = new Node(19, 2, v); node_array().add(node);
v[0] = 2.0*h_e_; node = new Node(20, 2, v); node_array().add(node);
v[0] = 3.0*h_e_; node = new Node(21, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(22, 2, v); node_array().add(node);
int ena[13]; Omega_eh *elem;
ena[0] = 8; ena[1] = 10; ena[2] = 20; ena[3] = 18; ena[4] = 9; ena[5] = 15;
last four nodes are real pressure-field
ena[6] = 19; ena[7] = 13; ena[8] = 14; ena[9] = 0; ena[10] = 1; ena[11] = 2; ena[12] = 3; nodes
elem = new Omega_eh(0, 0, 0, 13, ena); omega_eh_array().add(elem);
ena[0] = 10; ena[1] = 12; ena[2] = 22; ena[3] = 20; ena[4] = 11; ena[5] = 17;
ena[6] = 21; ena[7] = 15; ena[8] = 16; ena[9] = 4; ena[10] = 5; ena[11] = 6; ena[12] = 7;
elem = new Omega_eh(1, 0, 0, 13, ena); 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(4)](0) = the_gh_array[node_order(9)](0) =
the_gh_array[node_order(14)](0)=the_gh_array[node_order(9)](1)=
Dirichlet B.C.
gh_on_Gamma_h::Dirichlet; Nuemann B.C.
the_gh_array[node_order(10)][0] = -5.0; the_gh_array[node_order(0)][0] = 5.0;
} else if(i == 1) {
for(int j = 8; j <= 22; j++) for(int k = 0; k < df; k++) {
disable all geometrical nodes
the_gh_array[node_order(j)](k) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(j)][k] = 0.0;
}
}
}

488 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
static const int u_ndf = 2; static Omega_h_i oh_u(0); Ωu
static gh_on_Gamma_h_i u_gh(0, u_ndf, oh_u); static U_h u_h(u_ndf, oh_u);
static const int p_ndf = 1; static Omega_h_i oh_p(1);
u = [u, v]T
static gh_on_Gamma_h_i p_gh(1, p_ndf, oh_p); static U_h p_h(p_ndf, oh_p); Ωp
static Global_Discretization *u_type = new Global_Discretization;
static Global_Discretization u_gd(oh_u, u_gh, u_h, u_type);
static Global_Discretization *p_type = new Global_Discretization;
static Global_Discretization p_gd(oh_p, p_gh, p_h, p_type);
static Global_Discretization_Couple *p_u_type = new Global_Discretization_Couple; {p-u} global discretization couple
static Global_Discretization_Couple gdc(p_gd, u_gd, p_u_type);
class Incompressible_ElasticQ94_Mixed_Formulation : public Element_Formulation_Couple {
public:
Incompressible_ElasticQ94_Mixed_Formulation(Element_Type_Register a) :
Element_Formulation_Couple(a) {}
Element_Formulation *make(int, Global_Discretization&);
Incompressible_ElasticQ94_Mixed_Formulation(int, Global_Discretization&);
Element_Formulation_Couple *make(int, Global_Discretization_Couple&);
Incompressible_ElasticQ94_Mixed_Formulation(int, Global_Discretization_Couple&);
};
Element_Formulation*
Incompressible_ElasticQ94_Mixed_Formulation::make(int en, Global_Discretization& gd) { diagonal submatrices
return new Incompressible_ElasticQ94_Mixed_Formulation(en,gd);
}
Incompressible_ElasticQ94_Mixed_Formulation::Incompressible_ElasticQ94_Mixed_Formulation
(int en, Global_Discretization& gd) : Element_Formulation_Couple(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;
Lagragian 9-node element
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;
C0 x = MATRIX("int, int, C0&, int, int", 9, 2, xl, 0, 0);
H1 X = N*x; J dv(d(X).det());
if(gd.type() == u_type) {
H0 Nx = d(N) * d(X).inverse();
double d_0[3][3] = { {2.0, 0.0, 0.0}, {0.0, 2.0, 0.0}, {0.0, 0.0, 1.0}};
C0 D_0 = MATRIX("int, int, const double*", 3, 3, d_0[0]);
double m_0[3] = {1.0, 1.0, 0.0}; C0 m = VECTOR("int, const double*", 3, m_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];
2
∫ BTµ
B &= (~Wx || C0(0.0) ) &
A = D 0 – --- m ⊗ m BdΩ
(C0(0.0) || ~Wy )& 3
(~Wy || ~Wx ); Ωe
stiff &= ((~B) * (mu_*(D_0-2.0/3.0*(m%m)) * B)) | dv;
} else if(gd.type() == p_type) {
double sqrt3 = sqrt(3.0);
H0 n = INTEGRABLE_VECTOR("int, Quadrature", 4/*nen*/, qp), zai, eta;
zai &= ((H0)Z[0]); eta &= ((H0)Z[1]);
n[0] = (1.0-sqrt3*zai)*(1.0-sqrt3*eta)/4.0; n[1] = (1.0+sqrt3*zai)*(1.0-sqrt3*eta)/4.0;
n[2] = (1.0+sqrt3*zai)*(1.0+sqrt3*eta)/4.0; n[3] = (1.0-sqrt3*zai)*(1.0+sqrt3*eta)/4.0;
stiff &= MATRIX("int, int", 13, 13);
C0 stiff_sub = MATRIX("int, int, C0&, int, int", 4, 4, stiff, 9, 9); N p ⊗ Np
stiff_sub = -((n * (~n)) | dv)/K_; V = – ∫ --------------------- dΩ
K
} Ωe
}

Workbook of Applications in VectorSpace C++ Library 489


Chapter 5 Advanced Finite Element Methods
Element_Formulation_Couple* Incompressible_ElasticQ94_Mixed_Formulation::make(
int en, Global_Discretization_Couple& gdc) {
return new Incompressible_ElasticQ94_Mixed_Formulation(en,gdc);
}
Incompressible_ElasticQ94_Mixed_Formulation::Incompressible_ElasticQ94_Mixed_Formulation
(int en, Global_Discretization_Couple& gdc) : Element_Formulation_Couple(en, gdc) {
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;
C0 x = MATRIX("int, int, C0&, int, int", 9, 2, xl, 0, 0);
H1 X = N*x; H0 Nx = d(N) * d(X).inverse(); J dv(d(X).det());
H0 w_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, 2, Nx), wx, wy, B;
wx &= w_x[0][0]; wy &= w_x[0][1];
C0 zero(0.0);
B &= (~wx || zero) & (zero || ~wy ) & (~wy || ~wx );
double sqrt3 = sqrt(3.0);
H0 Zero(qp); Zero = 0.0;
H0 n = INTEGRABLE_VECTOR("int, Quadrature", 4/*nen*/, qp), n9, n10, n11, n12, zai, eta;
zai &= ((H0)Z[0]); eta &= ((H0)Z[1]);
n[0] = (1.0-sqrt3*zai)*(1.0-sqrt3*eta)/4.0; n[1] = (1.0+sqrt3*zai)*(1.0-sqrt3*eta)/4.0;
n[2] = (1.0+sqrt3*zai)*(1.0+sqrt3*eta)/4.0; n[3] = (1.0-sqrt3*zai)*(1.0+sqrt3*eta)/4.0;
n9 &= n[0]; n10 &= n[1]; n11 &= n[2]; n12 &= n[3];
H0 mN_p = ((n9 |n10 |n11 |n12 ) & (n9 |n10 |n11 |n12 ) & (Zero |Zero |Zero |Zero ) );
stiff &= MATRIX("int, int", 13, 18);
C0 stiff_sub = MATRIX("int, int, C0&, int, int", 4, 18, stiff, 9, 0);
C = ∫ ( mNp )T BdΩ
Ωe
stiff_sub = ((~mN_p)*B) | dv;
}
Element_Formulation* Element_Formulation::type_list = 0;
static Element_Type_Register element_type_register_instance;
static Incompressible_ElasticQ94_Mixed_Formulation
incompressible_elasticq94_mixed_formulation_instance(element_type_register_instance);
static Matrix_Representation mr_u(u_gd); static Matrix_Representation mr_p(p_gd);
static Matrix_Representation_Couple mrc(gdc, 0, &(mr_p.rhs()), &(mr_u.rhs()) , &mr_u);
int main() {
mr_u.assembly(); mr_p.assembly(); mrc.assembly();
C0 A = ((C0)(mr_u.lhs())), f_1 = ((C0)(mr_u.rhs())), C = ((C0)(mrc.lhs())),
V = ((C0)(mr_p.lhs())), f_2 = ((C0)(mr_p.rhs()));
Cholesky dnV(-V);
C0 V_inv = -(dnV.inverse()), CtV_invC = (~C)*(V_inv*C),
K = A-CtV_invC, f = f_1-(~C)*(-(dnV*f_2)); û = [ A – C T V –1 C ] –1 [ f 1 – C T V –1 f 2 ]
QR dK(K); C0 u = dK*f;
u_h = u; u_h = u_gd.gh_on_gamma_h();
cout << "displacement(u-x, u-y):" << endl << u_h << endl;
C0 p = -(dnV*(f_2-C*u)); p̂ = V –1 ( f 2 – Cû )
p_h = p; p_h = p_gd.gh_on_gamma_h();
cout << "pressure:" << endl << p_h << endl;
return 0;
}

Listing 5•6 Incompressible u-p formulation for plane elasticity (project :


“incompressible_u_p_formulation” in project workspace file “fe.dsw”).

490 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
Incompressible Formulation: Eq. 5•77 is for the nearly incompressible formulation ( ν → 0.5). For ν = 0.5 in Eq.
5•80, V = 0, because K = ∞. Therefore, we have, for incompressible formulation, 1

A C T û = f 1 Eq. 5•86
C 0 p̂ f2

Due to the zero diagonals, the solution procedure is different from the nearly incompressible case; i.e., the inver-
sion of V in Eq. 5•83 to Eq. 5•85 is not permissible now. From first equation of Eq. 5•86, we have

Aû + C T p̂ = f1 ⇒ û = A –1 [ f1 – C T p̂ ] Eq. 5•87

Substituting into second equation of Eq. 5•86

CA –1 [ f 1 – C T p̂ ] = f2 ⇒ p̂ = [ CA –1 C T ] – 1 [ CA –1 f 1 – f2 ] Eq. 5•88

After p̂ is solved by the second part of Eq. 5•88, û can be recovered from second part of Eq. 5•87. The tip-
deflection is “-0.5625”. This incompressible formulation is computed with macro definition “__TEST_INCOM
PRESSIBLE_PLANE_STRAIN” set at the compile time for the same project
“incompressible_u_p_formulation” in project workspace file “fe.dsw”.

Displacement-Only Mixed Formulation: If pressure field is taken as discontinuous field, the pressure can be elim-
inated at the element level for both the nearly incompressible and incompressible cases, since no element shares
pressure node with the other element, and therefore no inter-element dependency. For example, in the nearly
incompressible case Eq. 5•85 can be written as

k e û e = f e Eq. 5•89

where the re-defined element stiffness matrix element force vector are

k e ≡ a e – ( c e ) T ( v e ) –1 c e, and f e = f 1e – ( c e ) T ( v e ) –1 f 2e Eq. 5•90

The Program Listing 5•7 implements the present u-only simplified mixed formulation (project “mixed_u_only”
in project workspace file “fe.dsw”). The element stiffness matrix of Q 4/9 mixed formulation is equivalent to that
of the selective reduced integration Lagrangian 9-node element (in page 406 of Chapter 4 when “ElasticQ9” ele-
ment with selective reduced integration is used. see Figure 5•3). This is known as the equivalence theorem in a
more general context.2 Notice that due to the ill-condition number in the nearly incompressible plain strain case,
the full-scale mixed solution and the displacement-only solution may differ numerically.

1. see p. 202-203 in 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. 221-223 in Hughes, T.J.R., 1987, “The finite element method: linear static and dynamic finite element analysis”,
Prentice-Hall, Inc., Englewood Cliffs, New Jersey.

Workbook of Applications in VectorSpace C++ Library 491


Chapter 5 Advanced Finite Element Methods

selective reduced integration


for standard irreducible formulation mixed formulation Q4/9

volumetric stiffness deviatoric stiffness


Gauss point locations u nodes
u nodes p nodes

Figure 5•3 The equivalence of the selective reduced integration and the mixed formulation.

492 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods

#include "include\fe.h"
static const double E_ = 1.e3;
static const double v_ = 0.5-1.e-12;
static const double K_ = E_/3.0/(1-2.0*v_);
static const double a_ = E_*(1-v_)/(1+v_)/(1-2*v_);
static const double Dv[3][3]= {{a_, a_*v_/(1-v_), 0.0 },
{a_*v_/(1-v_), a_, 0.0 },
{0.0, 0.0, a_*(1-2*v_)/2.0/(1-v_)} };
C0 D = MATRIX("int, int, const double*", 3, 3, Dv[0]);
static const double lambda_ = v_*E_/((1+v_)*(1-2*v_));
static const double mu_ = E_/(2*(1+v_));
EP::element_pattern EP::ep = EP::LAGRANGIAN_9_NODES;
Omega_h::Omega_h() {
int control_node_flag[4] = {1, 1, 1, 1};
double x[4][2] = {{0.0, 0.0}, {10.0, 0.0}, {10.0, 2.0}, {0.0, 2.0}};
block(this, 3, 5, 4, control_node_flag, x[0]);
}
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
__initialization(df, omega_h);
double f_ = 15.0;
for(int i = 0; i < row_node_no; i++) {
the_gh_array[node_order((i+1)*5-1)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order((i+1)*5-1)][0] = 0.0;
the_gh_array[node_order(5)](1)= gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(5)](1)= 0.0;
double h_ = 1.0;
the_gh_array[node_order(10)](0) = gh_on_Gamma_h::Nuemann;
the_gh_array[node_order(10)][0] = f_*(1.0/3.0)*h_;
the_gh_array[node_order(0)](0)= gh_on_Gamma_h::Nuemann;
the_gh_array[node_order(0)][0] = -f_*(1.0/3.0)*h_;

}
class ElasticQ9 : public Element_Formulation {
public:
ElasticQ9(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
ElasticQ9(int, Global_Discretization&);
};
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),
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 9, 2, qp),
Zai, Eta;
9-nodes Lagrangian shape functions
Zai &= Z[0]; Eta &= Z[1];
N[0]=(1-Zai)*(1-Eta)/4;
N[1]=(1+Zai)*(1-Eta)/4; Step1: initial four corner nodes
N[2]=(1+Zai)*(1+Eta)/4;
N[3] =(1-Zai)*(1+Eta)/4;
N[8] = (1-Zai.pow(2))*(1-Eta.pow(2)); Step 2: add center nodes
N[0] -= N[8]/4; N[1] -= N[8]/4; Step 3:modification of four corner
N[2] -= N[8]/4; N[3] -= N[8]/4;
nodes
N[4] = ((1-Zai.pow(2))*(1-Eta)-N[8])/2;
N[5] = ((1-Eta.pow(2))*(1+Zai)-N[8])/2; due to the presence of the center node
N[6] = ((1-Zai.pow(2))*(1+Eta)-N[8])/2; Step 4:add four edge nodes and correct
N[7] = ((1-Eta.pow(2))*(1-Zai)-N[8])/2;
for the presence of the center node

Workbook of Applications in VectorSpace C++ Library 493


Chapter 5 Advanced Finite Element Methods
N[0] -= (N[4]+N[7])/2; Step 5: modification of the four corner
N[1] -= (N[4]+N[5])/2; nodes due to the presence of four
N[2] -= (N[5]+N[6])/2;
N[3] -= (N[6]+N[7])/2; edge nodes
H1 X = N*xl;
H0 Nx = d(N) * d(X).inverse();
J dV(d(X).det());
double d_0[3][3] = { {2.0, 0.0, 0.0},
{0.0, 2.0, 0.0},
{0.0, 0.0, 1.0}};
C0 D_0 = MATRIX("int, int, const double*", 3, 3, d_0[0]);
double m_0[3] = {1.0, 1.0, 0.0};
C0 m = VECTOR("int, const double*", 3, m_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];
double sqrt3 = sqrt(3.0);
B &= (~Wx || C0(0.0)) &
(C0(0.0) || ~Wy ) &
(~Wy || ~Wx ); 2
C0 a = ((~B) * (mu_*(D_0-2.0/3.0*(m%m)) * B)) | dV;
H0 n = INTEGRABLE_VECTOR("int, Quadrature", 4/*nen*/, qp),
ae = ∫ BT µ D 0 – --- m ⊗ m BdΩ
3
Ωe
zai, eta;
zai &= ((H0)Z[0]); eta &= ((H0)Z[1]);
n[0] = (1.0-sqrt3*zai)*(1.0-sqrt3*eta)/4.0; shape functions with four nodes
n[1] = (1.0+sqrt3*zai)*(1.0-sqrt3*eta)/4.0; at Gauss-points
n[2] = (1.0+sqrt3*zai)*(1.0+sqrt3*eta)/4.0;
n[3] = (1.0-sqrt3*zai)*(1.0+sqrt3*eta)/4.0;
Np ⊗ Np
C0 v = -((n * (~n)) | dV)/K_;
v e = – ∫ --------------------- dΩ
Cholesky dnv(-v); K
C0 v_inv = -(dnv.inverse()); Ωe
H0 Zero(qp);
Zero = 0.0;
H0 mN_p = ((n[0] | n[1] | n[2] | n[3] ) &

∫ ( mNp ) T BdΩ
(n[0] | n[1] | n[2] | n[3] ) &
(Zero | Zero | Zero | Zero ) ); ce =
C0 c = ((~mN_p)*B) | dV; Ωe
stiff &= a-(~c)*v_inv*c;
}
Element_Formulation* Element_Formulation::type_list = 0; ke ≡ a e – ( c e ) T ( v e ) –1 c e
Element_Type_Register element_type_register_instance;
static ElasticQ9 elasticq9_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;
}

Listing 5•7 Displacement only mixed formulation with discontinuous pressure field (project:
“mixed_u_only” in project workspace file “fe.dsw”).

494 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
Incompressible u-p-εv Formulation
The volumetric strain-displacement relation Eq. 4•215 in Chapter 4 is

εv = m T ε = m T Lu Eq. 5•91

where ε = [εx, εy, γxy]T, m = [1, 1, 0]T, and


------ 0
∂x
L = 0 ----- ∂ Eq. 5•92
-
∂y
∂ ∂
------ ------
∂y ∂x

The pressure-volumetric strain constitutive equation is defined in Eq. 5•72 as

Kε v – p = 0 Eq. 5•93

Weighted residual statements can be obtained by applying pressure variation δp to Eq. 5•91 and volumetric strain
variation δεv to Eq. 5•93 together with the weak statement (displacement variation δu) to the equilibrium equa-
tion (Eq. 5•75) we have

∫ ε ( δu ) T µ  D 0 – --- m ⊗ m ε ( δu ) + mp ( u ) dΩ =
2
 3  ∫ δu T b dΩ + ∫ δuT t dΓ Eq. 5•94
Ω Ω Γ

∫ δp [ εv – m T Lu ] dΩ = 0 Eq. 5•95

∫ δεv [ Kεv – p ] dΩ = 0 Eq. 5•96


The volumetric strain field is approximated as

ε v = N v ε̂v Eq. 5•97

Eq. 5•94 to Eq. 5•96 can be rewritten in matrix form as

Workbook of Applications in VectorSpace C++ Library 495


Chapter 5 Advanced Finite Element Methods

A CT 0 û f1
C 0 E T p̂ = f 2 Eq. 5•98
0 E H ε̂v f3

where

2
A = ∫ BT µ D 0 – --- m ⊗ m BdΩ
3
Eq. 5•99
Ωe

C = ∫ ( mNp )T BdΩ Eq. 5•100


Ωe

E = – ∫ [ N v ⊗ N p ]dΩ Eq. 5•101


Ωe

H = ∫ K [ Nv ⊗ Nv ]dΩ Eq. 5•102


Ωe

f1 = ∫ NuT b dΩ + ∫ NuT t dΓ – Au Γ e
– CTp Γe
Eq. 5•103
Ωe Γe

f 2 = – Cu Γe
–E T εv Eq. 5•104
Γe

f3 = – Ep Γe
– Hε v Eq. 5•105
Γe

In the process of solution, E-matrix will need to be invertible. If we take Np = Nv = Ψe(x), “-E” becomes

–E = ∫ [ Ψe ⊗ Ψe ]dΩ Eq. 5•106


Ωe

which is symmetrical positive definitive and the inversion of “-E” can be computed by Cholesky decomposition.
With such choice of Np = Nv, from the third equation of Eq. 5•98, we have

ε̂ v = H –1 ( f 3 + Ep̂ ) Eq. 5•107

Substituting into second equation of Eq. 5•98

496 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
Cû – E T H –1 ( f 3 + Ep̂ ) = f 2 ⇒ p̂ = ( E T H –1 E ) –1 ( Cû – E T H –1 f3 – f 2 ) Eq. 5•108

Substituting p̂ into the first equation

Aû + C T ( E T H –1 E ) –1 ( Cû – E T H –1 f3 – f 2 ) = f1 Eq. 5•109

That is

û = [ A + C T ( E T H –1 E ) –1 C ] – 1 [ f1 + C T ( E T H –1 E ) –1 ( E T H –1 f3 – f 2 ) ] Eq. 5•110

After we obtain û , p̂ can be recovered from second part of Eq. 5•108. Then ε̂v is computed directly from second
equation of Eq. 5•98. The Program Listing 5•8 implements the u-p-εv three-field incompressible formulation
(project “incompressible_u_p_epsilon_v_formulation” in project workspace file “fe.dsw”).
If Ψe(x) is taken as discontinuous fields, the variables p and εv can be eliminated at the element level and f3 =
0 because no boundary conditions can be imposed for p and εv. Then, we withheld the subtraction of essential
displacement boundary conditions from the second equation in Eq. 5•98, therefore, f2 = 0. From the second equa-
tion we have

ε̂ v = – ( E T ) –1 Cû = Wû, where W ≡ – ( E T ) – 1 C Eq. 5•111

From third equation in Eq. 5•98 we have

p̂ = – E –1 Hε̂ v = E –1 H ( E T ) – 1 Cû Eq. 5•112

Substituting Eq. 5•112 into Eq. 5•98

Aû + C T E –1 H ( E T ) –1 Cû = f 1 Eq. 5•113

or

[ A + C T E –1 H ( E T ) –1 C ]û = Aû = f1 Eq. 5•114

where

A ≡ A + C T E – 1 H ( E T ) –1 C = A + W T HW Eq. 5•115

Therefore a modified stiffness matrix A, based on the Hu-Washizu variational principle can be used to compute
the displacement field directly. The implementation of the A solution procedure is

Workbook of Applications in VectorSpace C++ Library 497


Chapter 5 Advanced Finite Element Methods

#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 row_node_no = 5; static const int row_segment_no = row_node_no-1;
static const double L_ = 10.0; static const double c_ = 1.0; half element size
static const double h_e_ = L_/((double)row_segment_no);
static const double E_ = 1.e3; 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_)); plane stress modification
static const double lambda_bar = 2*lambda_*mu_/(lambda_+2*mu_);
static const double K_ = lambda_bar+2.0/3.0*mu_;
Omega_h_i::Omega_h_i(int i) : Omega_h(0) { define Ωu
if(i == 0) { double v[2]; Node *node; 1st row
v[0] = 0.0; v[1] = 0.0; node = new Node(0, 2, v); node_array().add(node);
v[0] = h_e_; node = new Node(1, 2, v); node_array().add(node);
v[0] = 2.0*h_e_; node = new Node(2, 2, v); node_array().add(node);
v[0] = 3.0*h_e_; node = new Node(3, 2, v); node_array().add(node); 2nd row
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] = 2.0*h_e_; node = new Node(6, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(7, 2, v); node_array().add(node);
v[0] = 0.0; v[1] = 2.0*c_; node = new Node(8, 2, v); node_array().add(node);
3rd row
v[0] = h_e_; node = new Node(9, 2, v); node_array().add(node);
v[0] = 2.0*h_e_; node = new Node(10, 2, v); node_array().add(node);
v[0] = 3.0*h_e_; node = new Node(11, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(12, 2, v); node_array().add(node);
Serendipity 8-nodes element
int ena[8]; Omega_eh *elem;
ena[0] = 0; ena[1] = 2; ena[2] = 10; ena[3] = 8; ena[4] = 1; ena[5] = 6; ena[6] = 9; ena[7] = 5;
elem = new Omega_eh(0, 0, 0, 8, ena); omega_eh_array().add(elem);
ena[0] = 2; ena[1] = 4; ena[2] = 12; ena[3] = 10; ena[4] = 3; ena[5] = 7; ena[6] = 11; ena[7] = 6; define Ωp and Ωε
elem = new Omega_eh(1, 0, 0, 8, ena); omega_eh_array().add(elem); v
} else if(i == 1 || i == 2) { double inv_sqrt3 = 1.0/sqrt(3.0), v[2], xl[4][2], zai, eta; Node *node; elem # 0 nodal coordinates
xl[0][0] = 0.0; xl[0][1] = 0.0; xl[1][0] = 2.0*h_e_; xl[1][1] = 0.0;
xl[2][0] = 2.0*h_e_; xl[2][1] = 2.0*c_; xl[3][0] = 0.0; xl[3][1] = 2.0*c_;
1st Gauss point natural coordinates
zai = - inv_sqrt3; eta = - inv_sqrt3;
for(int j = 0; j < 2; j++) 1st Gauss point physical coordinates
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
2nd Gauss point natural coordinates
node = new Node(0, 2, v); node_array().add(node);
zai = inv_sqrt3; eta = - inv_sqrt3; 2nd Gauss point physical coordinates
for(int j = 0; j < 2; j++)
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(1, 2, v); node_array().add(node); 3rd Gauss point natural coordinates
zai = inv_sqrt3; eta = inv_sqrt3; 3rd Gauss point physical coordinates
for(int j = 0; j < 2; j++)
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(2, 2, v); node_array().add(node); 4th Gauss point natural coordinates
zai = - inv_sqrt3; eta = inv_sqrt3;
4th Gauss point physical coordinates
for(int j = 0; j < 2; j++)
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(3, 2, v); node_array().add(node);
elem # 1 nodal coordinates
xl[0][0] = 2.0*h_e_; xl[0][1] = 0.0; xl[1][0] = 4.0*h_e_; xl[1][1] = 0.0;
xl[2][0] = 4.0*h_e_; xl[2][1] = 2.0*c_; xl[3][0] = 2.0*h_e_; xl[3][1] = 2.0*c_; 1st Gauss point natural coordinates
zai = - inv_sqrt3; eta = - inv_sqrt3;

498 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
for(int j = 0; j < 2; j++) 1st Gauss point physical coordinates
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(4, 2, v); node_array().add(node);
zai = inv_sqrt3; eta = - inv_sqrt3; 2nd Gauss point natural coordinates
for(int j = 0; j < 2; j++)
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
2nd Gauss point physical coordinates
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(5, 2, v); node_array().add(node);
zai = inv_sqrt3; eta = inv_sqrt3;
for(int j = 0; j < 2; j++)
3rd Gauss point natural coordinates
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+ 3rd Gauss point physical coordinates
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(6, 2, v); node_array().add(node);
zai = - inv_sqrt3; eta = inv_sqrt3;
4th Gauss point natural coordinates
for(int j = 0; j < 2; j++) 4th Gauss point physical coordinates
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(7, 2, v); node_array().add(node);
geometrical nodes
v[0] = 0.0; v[1] = 0.0; node = new Node(8, 2, v); node_array().add(node);
v[0] = 1.0*h_e_; node = new Node(9, 2, v); node_array().add(node);
v[0] = 2.0*h_e_; node = new Node(10, 2, v); node_array().add(node);
v[0] = 3.0*h_e_; node = new Node(11, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(12, 2, v); node_array().add(node);
v[0] = 0.0; v[1] = 1.0*c_; node = new Node(13, 2, v); node_array().add(node);
v[0] = 2.0*h_e_; node = new Node(14, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(15, 2, v); node_array().add(node);
v[0] = 0.0; v[1] = 2.0*c_; node = new Node(16, 2, v); node_array().add(node);
v[0] = 1.0*h_e_; node = new Node(17, 2, v); node_array().add(node);
v[0] = 2.0*h_e_; node = new Node(18, 2, v); node_array().add(node);
v[0] = 3.0*h_e_; node = new Node(19, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(20, 2, v); node_array().add(node);
int ena[12]; Omega_eh *elem; last four nodes are real pressure-field
ena[0] = 8; ena[1] = 10; ena[2] = 18; ena[3] = 16; ena[4] = 9; ena[5] = 14;
ena[6] = 17; ena[7] = 13; ena[8] = 0; ena[9] = 1; ena[10] = 2; ena[11] = 3;
nodes
elem = new Omega_eh(0, 0, 0, 12, ena); omega_eh_array().add(elem);
ena[0] = 10; ena[1] = 12; ena[2] = 20; ena[3] = 18; ena[4] = 11; ena[5] = 15;
ena[6] = 19; ena[7] = 14; ena[8] = 4; ena[9] = 5; ena[10] = 6; ena[11] = 7;
elem = new Omega_eh(1, 0, 0, 12, ena); 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(4)](0) = the_gh_array[node_order(7)](0) =
the_gh_array[node_order(12)](0) = the_gh_array[node_order(7)](1) =
gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(8)][0] = -5.0; the_gh_array[node_order(0)][0] = 5.0;
Dirichlet B.C.
} else if(i == 1 || i == 2) { Nuemann B.C.
for(int j = 8; j <= 20; j++) for(int k = 0; k < df; k++) {
the_gh_array[node_order(j)](k) = gh_on_Gamma_h::Dirichlet;
}
disable all geometrical nodes
}
}
static const int u_ndf = 2; static Omega_h_i oh_u(0);
static gh_on_Gamma_h_i u_gh(0, u_ndf, oh_u); static U_h u_h(u_ndf, oh_u);
Ωu u = [u, v]T
static const int p_ndf = 1; static Omega_h_i oh_p(1); Ωp , p
static gh_on_Gamma_h_i p_gh(1, p_ndf, oh_p); static U_h p_h(p_ndf, oh_p);
static const int epsilon_v_ndf = 1; static Omega_h_i oh_epsilon_v(2);
static gh_on_Gamma_h_i epsilon_v_gh(2, epsilon_v_ndf, oh_epsilon_v);
Ωε , ε v
v
static U_h epsilon_v_h(epsilon_v_ndf, oh_epsilon_v);

Workbook of Applications in VectorSpace C++ Library 499


Chapter 5 Advanced Finite Element Methods
static Global_Discretization *u_type = new Global_Discretization; Ωu
static Global_Discretization u_gd(oh_u, u_gh, u_h, u_type);
static Global_Discretization *p_type = new Global_Discretization;
u = [u, v]T
static Global_Discretization p_gd(oh_p, p_gh, p_h, p_type); Ωp
static Global_Discretization *epsilon_v_type = new Global_Discretization;
static Global_Discretization
epsilon_v_gd(oh_epsilon_v, epsilon_v_gh, epsilon_v_h, epsilon_v_type);
static Global_Discretization_Couple *p_u = new Global_Discretization_Couple(); {p-u} global discretization couple
static Global_Discretization_Couple gdc_p_u(p_gd, u_gd, p_u); {εv-p} global discretization couple
static Global_Discretization_Couple *epsilon_v_p = new Global_Discretization_Couple();
static Global_Discretization_Couple gdc_epsilon_v_p(epsilon_v_gd, p_gd, epsilon_v_p);
class Incompressible_3_field_ElasticQ84_Mixed_Formulation :
public Element_Formulation_Couple {
public:
Incompressible_3_field_ElasticQ84_Mixed_Formulation(Element_Type_Register a) :
Element_Formulation_Couple(a) {}
Element_Formulation *make(int, Global_Discretization&);
Incompressible_3_field_ElasticQ84_Mixed_Formulation(int, Global_Discretization&);
Element_Formulation_Couple *make(int, Global_Discretization_Couple&);
Incompressible_3_field_ElasticQ84_Mixed_Formulation(
int, Global_Discretization_Couple&);
};
Element_Formulation* Incompressible_3_field_ElasticQ84_Mixed_Formulation::make(
int en, Global_Discretization& gd) { diagonal submatrices
return new Incompressible_3_field_ElasticQ84_Mixed_Formulation(en,gd); }
static const double d0[3][3] = { {mu_*(2.0-2.0/3.0), mu_*-2.0/3.0, 0.0},
{mu_*-2.0/3.0, mu_*(2.0-2.0/3.0), 0.0},
{0.0, 0.0, mu_} };
C0 D_dev = MATRIX("int, int, const double*", 3, 3, d0[0]);
Incompressible_3_field_ElasticQ84_Mixed_Formulation::
Incompressible_3_field_ElasticQ84_Mixed_Formulation(
int en, Global_Discretization& gd) : Element_Formulation_Couple(en, gd) {
Quadrature qp(2, 9); Lagragian 9-nodes element
H1 Z(2, (double*)0, qp), , Zai, Eta,
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature",8,2,qp);
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;
N[4] = (1.0-Zai.pow(2))*(1.0-Eta)/2.0; N[5] = (1.0-Eta.pow(2))*(1.0+Zai)/2.0;
N[6] = (1.0-Zai.pow(2))*(1.0+Eta)/2.0; N[7] = (1.0-Eta.pow(2))*(1.0-Zai)/2.0;
N[0] = N[0] - (N[4]+N[7])/2.0; N[1] = N[1] - (N[4]+N[5])/2.0;
N[2] = N[2] - (N[5]+N[6])/2.0; N[3] = N[3] - (N[6]+N[7])/2.0;
if(gd.type() == u_type) {
H1 X = N*xl; H0 Nx = d(N) * d(X).inverse(); J dv(d(X).det());
2
H0 w_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, 2/*nsd*/, Nx), wx, wy, B;
wx &= w_x[0][0]; wy &= w_x[0][1]; C0 zero(0.0);
A = ∫ BTµ D 0 – --- m ⊗ m BdΩ
3
Ωe
B &= (~wx || zero) & (zero || ~wy ) & (~wy || ~wx );
stiff &= ((~B)*D_dev*B)|dv;
} else {
C0 x = MATRIX("int, int, C0&, int, int", 8, 2, xl, 0, 0);
H1 X = N*x; J dv(d(X).det()); double sqrt3 = sqrt(3.0);
H0 N_v = INTEGRABLE_VECTOR("int, Quadrature", 4/*nen*/, qp), zai, eta;
zai &= ((H0)Z[0]); eta &= ((H0)Z[1]);
N_v[0] = (1.0-sqrt3*zai)*(1.0-sqrt3*eta)/4.0; N_v[1] = (1.0+sqrt3*zai)*(1.0-sqrt3*eta)/4.0;
N_v[2] = (1.0+sqrt3*zai)*(1.0+sqrt3*eta)/4.0; N_v[3] = (1.0-sqrt3*zai)*(1.0+sqrt3*eta)/4.0;
stiff &= MATRIX("int, int", 12, 12);
C0 stiff_sub = MATRIX("int, int, C0&, int, int", 4, 4, stiff, 8, 8);
stiff_sub = ((N_v*(~N_v))|dv)*K_;
H = ∫ K [ Nv ⊗ Nv ]dΩ
Ωe
}
}

500 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
Element_Formulation_Couple* Incompressible_3_field_ElasticQ84_Mixed_Formulation::
make(int en, Global_Discretization_Couple& gdc) {
return new Incompressible_3_field_ElasticQ84_Mixed_Formulation(en,gdc); }
Incompressible_3_field_ElasticQ84_Mixed_Formulation::Incompressible_3_field_ElasticQ84_Mi
xed_Formulation(int en, Global_Discretization_Couple& gdc) :
Element_Formulation_Couple(en, gdc) { Quadrature qp(2, 9); H1 Z(2, (double*)0, qp),Zai,Eta,
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 8, 2, qp);
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;
N[4] = (1.0-Zai.pow(2))*(1.0-Eta)/2.0; N[5] = (1.0-Eta.pow(2))*(1.0+Zai)/2.0;
N[6] = (1.0-Zai.pow(2))*(1.0+Eta)/2.0; N[7] = (1.0-Eta.pow(2))*(1.0-Zai)/2.0;
N[0] = N[0] - (N[4]+N[7])/2.0; N[1] = N[1] - (N[4]+N[5])/2.0;
N[2] = N[2] - (N[5]+N[6])/2.0; N[3] = N[3] - (N[6]+N[7])/2.0;
C0 x = MATRIX("int, int, C0&, int, int", 8, 2, xl, 0, 0); H1 X = N*x; J dv(d(X).det());
H0 N_p = INTEGRABLE_VECTOR("int, Quadrature", 4/*nen*/, qp), Zero(qp); Zero = 0.0;
H0 zai, eta; double sqrt3 = sqrt(3.0); zai &= ((H0)Z[0]); eta &= ((H0)Z[1]);
N_p[0] = (1.0-sqrt3*zai)*(1.0-sqrt3*eta)/4.0; N_p[1] = (1.0+sqrt3*zai)*(1.0-sqrt3*eta)/4.0;
N_p[2] = (1.0+sqrt3*zai)*(1.0+sqrt3*eta)/4.0; N_p[3] = (1.0-sqrt3*zai)*(1.0+sqrt3*eta)/4.0;
if(gdc.type() == p_u) { H0 Nx = d(N) * d(X).inverse(); C0 zero(0.0);
H0 w_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, 2/*nsd*/, Nx), wx, wy, B;
wx &= w_x[0][0]; wy &= w_x[0][1]; B &= (~wx || zero) & (zero || ~wy ) & (~wy || ~wx );
C = ∫ ( mNp ) T BdΩ
Ωe
H0 mN_p = (~N_p) & (~N_p) & (Zero | Zero | Zero | Zero);
stiff &= MATRIX("int, int", 12, 16);
C0 stiff_sub=MATRIX("int, int, C0&, int, int", 4, 16, stiff, 8, 0); stiff_sub = ((~mN_p)*B) | dv;
} else { H0 N_epsilon_v; N_epsilon_v &= N_p; stiff &= MATRIX("int, int", 12, 12); E = – ∫ [ N v ⊗ N p ]dΩ
C0 stiff_sub = MATRIX("int, int, C0&, int, int", 4, 4, stiff, 8, 8);
stiff_sub = (N_epsilon_v*(~N_p)) | dv; Ωe
}
}
Element_Formulation* Element_Formulation::type_list = 0;
static Element_Type_Register element_type_register_instance;
static Incompressible_3_field_ElasticQ84_Mixed_Formulation
incompressible_3_field_elasticq84_mixed_formulation_instance(element_type_register_instance);
static Matrix_Representation mr_u(u_gd);
static Matrix_Representation mr_epsilon_v(epsilon_v_gd);
static Matrix_Representation_Couple mrc_p_u(gdc_p_u, 0, 0, &(mr_u.rhs()), &mr_u);
static Matrix_Representation_Couple
mrc_epsilon_v_p(gdc_epsilon_v_p, 0, &(mr_epsilon_v.rhs()), &(mrc_p_u.rhs()) , &mrc_p_u);
int main() {
mr_u.assembly(); mr_epsilon_v.assembly();
mrc_p_u.assembly(); mrc_epsilon_v_p.assembly();
C0 A = ((C0)(mr_u.lhs())), H = ((C0)(mr_epsilon_v.lhs())),
C = ((C0)(mrc_p_u.lhs())), E = ((C0)(mrc_epsilon_v_p.lhs())), û = [ A + C T ( E T H –1 E ) –1 C ] –1
f_1 = ((C0)(mr_u.rhs())), f_2 = ((C0)(mrc_p_u.rhs())), f_3 = ((C0)(mr_epsilon_v.rhs()));
Cholesky dH(H); C0 Hinv = dH.inverse(), EtHinv = (~E)*Hinv,EtHinvE = EtHinv*E; [ f 1 + C T ( E T H – 1 E ) –1 ( E T H –1 f 3 – f2 ) ]
Cholesky dEtHinvE(EtHinvE);
C0 EtHinvE_inv = dEtHinvE.inverse(), ApCtEtHinvEC = A + (~C)*EtHinvE_inv*C;
Cholesky dApCtEtHinvEC(ApCtEtHinvEC);
C0 u = dApCtEtHinvEC * (f_1 + (~C)*(dEtHinvE*(EtHinv*f_3 + f_2))); p̂ = ( E T H –1 E ) – 1 ( Cû – E T H – 1 f 3 – f 2 )
Cholesky dE(E); C0 epsilon_v = dE*(C*u-f_2), p = dE*(H*epsilon_v-f_3);
u_h = u; u_h = u_gd.gh_on_gamma_h(); cout << "displacement(u-x, u-y):" << endl << u_h;
p_h = p; p_h = p_gd.gh_on_gamma_h(); cout << "pressure:" << endl << p_h; ε̂ v = E –1 ( Cû – f 2 )
epsilon_v_h = epsilon_v; epsilon_v_h = epsilon_v_gd.gh_on_gamma_h();
cout << "volumetric strain:" << endl << epsilon_v_h;
return 0;
}

Listing 5•8 Incompressible u-p-εv formulation for plane elasticity (project:


“incompressible_u_p_epsilon_v_formulation” in project workspace file “fe.dsw”).

Workbook of Applications in VectorSpace C++ Library 501


Chapter 5 Advanced Finite Element Methods
1 Cholesky dE(E);
2 C0 Einv = dE.inverse(),
// W ≡ –( E ) C
T –1
3 W = - Einv*C,
4 A_bar = A+(~W)*H*W; // A = A + W T HW
5 Cholesky dA_bar(A_bar); –1
6 C0 u = dA_bar * f_1, // û = ( A ) f 1
7 epsilon_v = W*u; // ε̂v = Wû
8 p = dE*(H*epsilon_v); // p̂ = –E –1 Hε̂
v

This computation can be invoked by setting macro definition “__TEST_A_BAR_FORMULATION” at the com-
pile time for the project “incompressible_u_p_epsilon_v_formulation” in project workspace file “fe.dsw”.

B Method
Mixed formulation leads to complicated matrix substructuring problem. With the support of “fe.lib” it does
not seems to be too difficult. However, most finite element programs do not have the capability to deal with the
matrix substructuring problem. Engineering simplification need to be made, particularly if such simplification
leads to highly efficient programs. For example, the A formulation in the above simplifies the program to be
compatible with the standard irreducible formulation, if only the displacement field is to be solved. Furthermore,
we may wish to re-define, in place of the strain-displacement matrix (B-matrix), the B-matrix such that A is
expressed in the form conformable to the standard irreducible formulation1

∫B
T
A≡ DB dΩ Eq. 5•116
Ωe

From Eq. 4•220 in Chapter 4, the standard form of D is

2 00
D ≡ µ  D 0 – --- m ⊗ m + K ( m ⊗ m ) where D 0 = 0 2 0
2
Eq. 5•117
3
0 01

This expression can be easily implemented with the aid of VectorSpace C++ Library. A more graphic expression
suitable for a lower-level implementation is

K +  2 – --- µ
2 2
K – --- µ 0
 3 3
D≡ Eq. 5•118
K +  2 – --- µ 0
2 2
K – --- µ
3  3
0 0 µ

1. p. 345 in Zienkiewicz, O.C. and R.L. Taylor, 1989, “The finite element method”, 4th ed., vol. 1. McGraw-Hill, Inc., UK.

502 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
By inspecting Eq. 5•116, the modification to the standard form of stiffness definition is restricted only to the B-
matrix and it can be defined as

B ≡ B dev + B vol Eq. 5•119

That is the deviatoric part of B is just the same as that of B, but its volumetric part, Bvol , needs to be consistent
with the u-p-εv mixed formulation. Bvol and Bdev are defined as

1 1 1
Bvol ≡ --- ( m ⊗ m )B = --- m ( m • B ) and B dev ≡ B – B vol = I – --- ( m ⊗ m ) B Eq. 5•120
3 3 3

Now, we make distinction of discrete approximation to the volumetric strain as ε vh ≡ ε v and the infinite dimen-
sional εv in continuum mechanics. The approximation in Eq. 5•97 can be more precisely written (with over-bar
indicates “average” for certain simplest approximation which will become evident later) as

ε v ≡ Ψ e ε̂ v = Ψ e Wû Eq. 5•121

and,

ε v = div ( û ) = m • B û Eq. 5•122

Comparing Eq. 5•121 and Eq. 5•122, we have

m • B = Ψe W Eq. 5•123

From the first part of Eq. 5•120, Bvol can be defined similarly to Bvol as

1 1
Bvol = --- m ( m • B ) = --- mΨ e W Eq. 5•124
3 3

Substituting into Eq. 5•119, we have

1 1
B ≡ Bdev + B vol = I – --- ( m ⊗ m ) B + --- mΨ e W Eq. 5•125
3 3

Eq. 5•125 can look quite formidable, a step-by-step algorithm for the B formula can be given as1

∂N ∂N
B1 = -------, and B2 = ------- Eq. 5•126
∂x ∂y
˜
and define B as

1. see p.233-236 in Hughes, T.J.R., 1987, “The finite element method: linear static and dynamic finite element analysis”,
Prentice-Hall, Inc., Englewood Cliffs, New Jersey.

Workbook of Applications in VectorSpace C++ Library 503


Chapter 5 Advanced Finite Element Methods
˜
B ≡ m • B = Ψe W Eq. 5•127

and

B 5 B6
B = B 4 B7 Eq. 5•128
B 2 B1

where

˜
B1 – B 1
B 4 ≡ ------------------ Eq. 5•129
3

B 5 ≡ B 1 + B4 Eq. 5•130

B2 – B 2
B 6 ≡ ------------------ Eq. 5•131
3

B 7 ≡ B 2 + B6 Eq. 5•132

It is easily verifiable that Eq. 5•126 to Eq. 5•132 is equivalent to Eq. 5•125. The algorithm given above, Eq.
5•128 to Eq. 5•132, involves only simple arithmatics. The Program Listing 5•9 implements the B formulation in
this section. Due to the importance of this formulation, the post-processing to compute reactions, stresses, strains
are also included. The details of the post-processing have been discussed in Chapter 4.
We note by passing that from Eq. 5•124 and for the case of bilinear element with piece-wise constant pres-
sure and volumetric strain; i.e., Ψe(x) = 1, we have

∫ ( mΨe ) ⊗ B dΩ ∫ Bvol dΩ
1 1 Ωe Ωe
B vol = --- mΨ e W = --- mΨ e ------------------------------------------- = ------------------------
- Eq. 5•133
3 3
∫ e e
[ Ψ ⊗ Ψ ]dΩ ∫ dΩ
Ωe Ωe

The last term shows that the Bvol is the “mean dilatation of B” over the element domain Ωe.1 This special case
adds another perspective to our understanding of Eq. 5•119 that the definition of B ≡ Bdev + B vol is through the
modification of its volumetric part Bvol, which is the mean of Bvol over the element domain, according to Eq.
5•133. By inspecting this equation, Bvol can also be interpreted as the least squares smoothing of Bvol over the
element domain. Therefore, B is considered an assumed-strain method as oppose to the assumed-displacement

1. see p. 235 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, and the reference therein by Nagtegaal, Parks and Rice.

504 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods

#include "include\fe.h"
static const int row_node_no = 5; static const int col_node_no = 3;
static const int row_segment_no = row_node_no-1;
static const double L_ = 10.0; static const double c_ = 1.0;
static const double h_e_ = L_/((double)row_segment_no);
static const double E_ = 1.e3; static const double v_ = 0.5-1.e-12;
static const double lambda_ = v_*E_/((1+v_)*(1-2*v_));
static const double mu_ = E_/(2*(1+v_)); static const double K_ = E_/3.0/(1-2.0*v_);
Omega_h::Omega_h() {
double v[2]; Node *node; define Ωu
v[0] = 0.0; v[1] = 0.0; node = new Node(0, 2, v); node_array().add(node);
1st row
v[0] = h_e_; node = new Node(1, 2, v); node_array().add(node);
v[0] = 2.0*h_e_; node = new Node(2, 2, v); node_array().add(node);
v[0] = 3.0*h_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);
2nd row
v[0] = 0.0; v[1] = 1.0*c_; node = new Node(5, 2, v); node_array().add(node);
v[0] = 2.0*h_e_; node = new Node(6, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(7, 2, v); node_array().add(node);
v[0] = 0.0; v[1] = 2.0*c_; node = new Node(8, 2, v); node_array().add(node);
3rd row
v[0] = h_e_; node = new Node(9, 2, v); node_array().add(node);
v[0] = 2.0*h_e_; node = new Node(10, 2, v); node_array().add(node);
v[0] = 3.0*h_e_; node = new Node(11, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(12, 2, v); node_array().add(node);
int ena[8]; Omega_eh *elem;
ena[0] = 0; ena[1] = 2; ena[2] = 10; ena[3] = 8; ena[4] = 1; ena[5] = 6; ena[6] = 9; ena[7] = 5; Serendipity 8-nodes element
elem = new Omega_eh(0, 0, 0, 8, ena); omega_eh_array().add(elem);
ena[0] = 2; ena[1] = 4; ena[2] = 12; ena[3] = 10; ena[4] = 3; ena[5] = 7; ena[6] = 11; ena[7] = 6;
elem = new Omega_eh(1, 0, 0, 8, ena); omega_eh_array().add(elem);
}
gh_on_Gamma_h::gh_on_Gamma_h( int df, Omega_h& omega_h) { boundary conditions
__initialization(df, omega_h);
the_gh_array[node_order(4)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(4)][0] = 0.0;
the_gh_array[node_order(7)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(7)][0] = 0.0;
the_gh_array[node_order(12)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(12)][0] = 0.0;
the_gh_array[node_order(7)](1) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(7)][1] = 0.0;
the_gh_array[node_order(8)](0) = gh_on_Gamma_h::Neumann;
the_gh_array[node_order(8)][0] = -5.0;
the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Neumann;
the_gh_array[node_order(0)][0] = 5.0;
}
static 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 Elastic_B_bar_Q84 : public Element_Formulation {
public:
Elastic_B_bar_Q84(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
Elastic_B_bar_Q84(int, Global_Discretization&);
};
Element_Formulation* Elastic_B_bar_Q84::make(int en, Global_Discretization& gd) {
return new Elastic_B_bar_Q84(en,gd);
}

Workbook of Applications in VectorSpace C++ Library 505


Chapter 5 Advanced Finite Element Methods
#if defined(__TEST_HUGHES)
#if defined(__TEST_PLANE_STRAIN_NEARLY_INCOMPRESSIBLE)
static const double a_ = E_*(1-v_)/(1+v_)/(1-2*v_);
static const double Dv[3][3] =
{ {a_, a_*v_/(1-v_), 0.0 }, {a_*v_/(1-v_), a_, 0.0}, {0.0, 0.0, a_*(1-2*v_)/2.0/(1-v_)} };
#else
static const double a_ = E_ / (1.0-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.0-v_)/2.0} };
#endif
C0 D = MATRIX("int, int, const double*", 3, 3, Dv[0]);
#else
static const double Dv[3][3] = { {mu_*(2.0-2.0/3.0)+K_,mu_*-2.0/3.0+K_, 0.0},
{mu_*-2.0/3.0+K_, mu_*(2.0-2.0/3.0)+K_,0.0}, {0.0, 0.0, mu_} };
C0 D = MATRIX("int, int, const double*", 3, 3, Dv[0]);
static const double i_dev[3][3]={{(1. - 1. / 3.),-1. / 3., .0},{-1. / 3.,(1. - 1. / 3.), .0}, { .0, .0, 1.}};
C0 I_dev = MATRIX("int, int, const double*", 3, 3, i_dev[0]);
#endif
Elastic_B_bar_Q84::Elastic_B_bar_Q84(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", 8, 2, qp),
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; B matrix:
N[4] = (1.0-Zai.pow(2))*(1.0-Eta)/2.0; N[5] = (1.0-Eta.pow(2))*(1.0+Zai)/2.0;
N[6] = (1.0-Zai.pow(2))*(1.0+Eta)/2.0; N[7] = (1.0-Eta.pow(2))*(1.0-Zai)/2.0;
N[0] = N[0] - (N[4]+N[7])/2.0; N[1] = N[1] - (N[4]+N[5])/2.0;
B 5 B6
N[2] = N[2] - (N[5]+N[6])/2.0; N[3] = N[3] - (N[6]+N[7])/2.0; B = B 4 B7
H1 X = N*xl; H0 Nx = d(N) * d(X).inverse(); J dv(d(X).det());
#if defined(__TEST_HUGHES) B 2 B1
H0 w_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, 2/*nsd*/, Nx), B1, B2, B;
B1 &= ~(w_x[0][0]); B2 &= ~(w_x[0][1]); C0 zero(0.0);
B &= ( B1 || zero) & (zero || B2 ) & ( B2 || B1 ); ˜
B ≡ Ψe W
double sqrt3 = sqrt(3.0);
H0 N_vp = INTEGRABLE_VECTOR("int, Quadrature", 4/*nen*/, qp), zai, eta;
∂N ∂N
zai &= ((H0)Z[0]); eta &= ((H0)Z[1]); B 1 = -------, and B 2 = -------
N_vp[0] = (1.0-sqrt3*zai)*(1.0-sqrt3*eta)/4.0; N_vp[1] = (1.0+sqrt3*zai)*(1.0-sqrt3*eta)/4.0; ∂x ∂y
N_vp[2] = (1.0+sqrt3*zai)*(1.0+sqrt3*eta)/4.0; N_vp[3] = (1.0-sqrt3*zai)*(1.0+sqrt3*eta)/4.0;
H0 mN_vp = (~N_vp) & (~N_vp) & (zero | zero | zero | zero); ˜
B1 – B1
C0 C = ((~mN_vp)*B) | dv, E = (N_vp*(~N_vp)) | dv, B 4 ≡ ------------------ , B 5 ≡ B1 + B 4
E_inv = Cholesky(E).inverse(), W = E_inv*C; 3
H0 B_mean = (~N_vp)*W;
H0 B_bar_i = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, 2, B_mean), B_bar1, B_bar2; ˜
B2 – B2
B_bar1 &= B_bar_i[0][0]; B_bar2 &= B_bar_i[0][1]; H0 B4, B5, B6, B7; B 6 ≡ ------------------ , B 7 ≡ B2 + B 6
B4 &= (B_bar1-B1)/3.0; B5 &= B1+B4; B6 &= (B_bar2-B2)/3.0; B7 &= B2+B6; 3
H0 B_bar = ( B5 || B6) & ( B4 || B7) & ( B2 || B1);
#else operator split:
H0 w_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, 2/*nsd*/, Nx), wx, wy, B, B_bar;
1
wx &= w_x[0][0]; wy &= w_x[0][1]; C0 zero(0.0); double sqrt3 = sqrt(3.0); B vol ≡ --- ( m ⊗ m )B
B &= (~wx || zero) & (zero || ~wy ) & (~wy || ~wx );
3
H0 N_vp = INTEGRABLE_VECTOR("int, Quadrature", 4/*nen*/, qp), zai, eta;
1
zai &= ((H0)Z[0]); eta &= ((H0)Z[1]); Bdev ≡ B – B vol = I – --- ( m ⊗ m ) B
N_vp[0] = (1.0-sqrt3*zai)*(1.0-sqrt3*eta)/4.0; N_vp[1] = (1.0+sqrt3*zai)*(1.0-sqrt3*eta)/4.0; 3
N_vp[2] = (1.0+sqrt3*zai)*(1.0+sqrt3*eta)/4.0; N_vp[3] = (1.0-sqrt3*zai)*(1.0+sqrt3*eta)/4.0; 1
H0 mN_vp = (~N_vp) & (~N_vp) & (zero | zero | zero | zero); Bvol = --- mΨ e W
3
C0 C = ((~mN_vp)*B) | dv, E = (N_vp*(~N_vp)) | dv,
E_inv = Cholesky(E).inverse(), W = E_inv*C;
B ≡ B dev + Bvol
B_bar &= I_dev * B + (1.0/3.0) * mN_vp * W;
#endif

506 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
if(Matrix_Representation::Assembly_Switch == Matrix_Representation::REACTION) { Post-processing
stiff &= ((~B_bar)*D*B_bar)|dv;
the_element_nodal_value &= stiff * (ul+gl);
reactions
} else if(Matrix_Representation::Assembly_Switch == Matrix_Representation::STRAIN) {
H0 epsilon_hat = INTEGRABLE_VECTOR("int, Quadrature", 3, qp); strains
epsilon_hat = 0.0;
for(int i = 0; i < nen; i++) epsilon_hat +=
B_bar(i*ndf)*(ul[i*ndf]+gl[i*ndf]) + B_bar(i*ndf+1)*(ul[i*ndf+1]+gl[i*ndf+1]);
int nqp = qp.no_of_quadrature_point();
cout.setf(ios::left,ios::adjustfield);
cout << setw(9) << " elem #, " << setw(14) << "x-coor.," << setw(14) << "y-coor.,"
<< setw(14) << "epsilon-x," << setw(14) << "epsilon-y," << setw(14) << "epsilon-xy"
<< endl;
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) << (epsilon_hat[0].quadrature_point_value(i))
<< setw(14) << (epsilon_hat[1].quadrature_point_value(i))
<< setw(14) << (epsilon_hat[2].quadrature_point_value(i)) << endl;
} nodal strains by projection
} else if(Matrix_Representation::Assembly_Switch ==
Matrix_Representation::NODAL_STRAIN) {
int strain_no = (ndf+1)*ndf/2; the_element_nodal_value &= C0(nen*strain_no, (double*)0);
C0 projected_nodal_strain = SUBVECTOR("int, C0&", strain_no, the_element_nodal_value);
H0 epsilon_hat = INTEGRABLE_VECTOR("int, Quadrature", 3, qp);
epsilon_hat = 0.0;
for(int i = 0; i < nen; i++) epsilon_hat +=
B_bar(i*ndf)*(ul[i*ndf]+gl[i*ndf]) + B_bar(i*ndf+1)*(ul[i*ndf+1]+gl[i*ndf+1]);
H0 unit(qp); unit = 1.0;
C0 area = unit | dv,
lumped_mass = VECTOR("int", nen),
sum_of_lumped_mass(0.0);
for(int i = 0; i < nen; i++) {
lumped_mass[i] = (((H0)N[i]).pow(2)) | dv; sum_of_lumped_mass += lumped_mass[i];
}
C0 normalized_factor = area/sum_of_lumped_mass;
for(int i = 0; i < nen; i++) {
lumped_mass[i] *= normalized_factor;
projected_nodal_strain(i) = ( (((H0)N[i])*epsilon_hat) | dv ) / lumped_mass[i];
}
} else if(Matrix_Representation::Assembly_Switch == Matrix_Representation::STRESS) {
H0 sigma_hat = INTEGRABLE_VECTOR("int, Quadrature", 3, qp);
stresses
sigma_hat = 0.0;
H0 DB_bar = D*B_bar;
for(int i = 0; i < nen; i++) sigma_hat +=
DB_bar(i*ndf)*(ul[i*ndf]+gl[i*ndf]) + DB_bar(i*ndf+1)*(ul[i*ndf+1]+gl[i*ndf+1]);
int nqp = qp.no_of_quadrature_point();
cout.setf(ios::left,ios::adjustfield);
cout << setw(9) << " elem #, " << setw(14) << "x-coor.," << setw(14) << "y-coor.,"
<< setw(14) << "sigma-x," << setw(14) << "sigma-y," << setw(14) << "sigma-xy"
<< endl;
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_hat[0].quadrature_point_value(i))
<< setw(14) << (sigma_hat[1].quadrature_point_value(i))
<< setw(14) << (sigma_hat[2].quadrature_point_value(i)) << endl;
}

Workbook of Applications in VectorSpace C++ Library 507


Chapter 5 Advanced Finite Element Methods
} else if(Matrix_Representation::Assembly_Switch ==
nodal stresses by projection
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_hat = INTEGRABLE_VECTOR("int, Quadrature", 3, qp);
sigma_hat = 0.0;
H0 DB_bar = D*B_bar;
for(int i = 0; i < nen; i++)
sigma_hat +=
DB_bar(i*ndf)*(ul[i*ndf]+gl[i*ndf]) + DB_bar(i*ndf+1)*(ul[i*ndf+1]+gl[i*ndf+1]);
H0 unit(qp); unit = 1.0;
C0 area = unit | dv,
lumped_mass = VECTOR("int", nen),
sum_of_lumped_mass(0.0);
for(int i = 0; i < nen; i++) {
lumped_mass[i] = (((H0)N[i]).pow(2)) | dv;
sum_of_lumped_mass += lumped_mass[i];
}
C0 normalized_factor = area/sum_of_lumped_mass;
for(int i = 0; i < nen; i++) {
lumped_mass[i] *= normalized_factor;
projected_nodal_stress(i) = ( (((H0)N[i])*sigma_hat) | dv ) / lumped_mass[i];
}

∫B
T
} else stiff &= ((~B_bar)*D*B_bar)|dv; K≡ DB dΩ
}
Element_Formulation* Element_Formulation::type_list = 0; Ωe
Element_Type_Register element_type_register_instance;
D ≡ µ  D 0 – --- m ⊗ m + K ( m ⊗ m )
2
static Elastic_B_bar_Q84
elastic_B_bar_Q84_instance(element_type_register_instance);
 3 
int main() {
where
Matrix_Representation mr(gd);
mr.assembly();
20 0
C0 u = ((C0)(mr.rhs())) / ((C0)(mr.lhs()));
gd.u_h() = u; D0 = 0 2 0
gd.u_h() = gd.gh_on_gamma_h(); 00 1
cout << gd.u_h() << endl;
#if defined(__TEST_POST_PROCESSING)
Matrix_Representation::Assembly_Switch = Matrix_Representation::REACTION; post-processing
mr.assembly(FALSE);
cout << "reaction:" << endl << (mr.global_nodal_value()) << endl;
Matrix_Representation::Assembly_Switch = Matrix_Representation::STRAIN;
mr.assembly(FALSE);
Matrix_Representation::Assembly_Switch = Matrix_Representation::NODAL_STRAIN;
mr.assembly(FALSE);
cout << "nodal strains:" << endl << (mr.global_nodal_value()) << endl;
Matrix_Representation::Assembly_Switch = Matrix_Representation::STRESS;
mr.assembly(FALSE);
Matrix_Representation::Assembly_Switch = Matrix_Representation::NODAL_STRESS;
mr.assembly(FALSE);
cout << "nodal stresses:" << endl << (mr.global_nodal_value()) << endl;
#endif
return 0;
}

Listing 5•9 B matrix formulation for plane elasticity (project: “b_bar_formulation” in project workspace
file “fe.dsw”).

508 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
method for the standard irreducible formulation. We will also encounter an assumed-stress method, the Pian-
Sumihara element, later in this chapter.

Non-conforming Element
In Chapter 4 Figure 4•42 eight bilinear 4-node elements and two Lagrangian 9-node elements are used to
compute beam bending problem. The tip-deflection solution (see TABLE 4•3. in page 405) with bilinear 4-node
elements (2 × 2 integration) is only 60 % of the exact solution, while it is 98% accurate for the Lagrangian 9-node
elements. The bilinear 4-node element exhibits shear locking and dilatation locking both due to the interpolation
failure to represent x2 and y2 (see aliasing analysis discussed in page 397 and page 406). Therefore these two
quadratic displacement modes are added back (1) to improve the bending behavior, and (2) to overcome the
incompressible limit of the 4-node element. This is the Wilson’s nonconforming element.

N 4 ≡ 1 – ξ 2, and N 5 ≡ 1 – η 2 Eq. 5•134


3 5
∑ Na ( ξ, η )ûea + ∑ Na ( ξ, η ) α̂e
a
u eh ( ξ, η ) ≡ Eq. 5•135
a=0 a=4

where node number “0, 1, 2 , 3” corresponding to four nodes of the element. α̂ e , with a = 4, 5, are known as
a

“nodeless variables” or “generalized displacements”, which is independent from the other element. Since α̂ e are
a

independent from the other elements, they can be eliminated at the element level. The element stiffness matrix is
of the form corresponding to variables a = [u0, u1, u2, u3, α4, α5]T as

T
k uu k αu û f
= Eq. 5•136
k αu k αα α̂ 0

From second equation

α̂ – 1 k û
= – k αα αu Eq. 5•137

Substituting into the first equation

kû ≡ [ k uu – k αu
T k – 1 k ]û = f
αα αu Eq. 5•138

However, it is found that Wilson’s non-conforming element only works for rectangular or parallelogram element.
The addition of two quadratic shape functions in Eq. 5•134 make the interpolation polynomial not complete up to
the second-order. Therefore, the spatial isotropy is lost; the element is not invariant with respect to an arbitrary
coordinate axes rotation. The integration of B-matrix components corresponding to the nodeless variables are

Workbook of Applications in VectorSpace C++ Library 509


Chapter 5 Advanced Finite Element Methods

∂y
– ξ ------ 0
1 1 N 4, x 0 1 1 ∂η
∂x
B4 dΩ = ∫∫ 0 N 4, y J dξ dη = 2 ∫ ∫ 0 ξ ------ dξd
∂η
Eq. 5•139
–1 –1 N 4, y N 4, x –1 –1
∂x ∂y
ξ ------ – ξ ------
∂η ∂η

∂y
η ------ 0
1 1 N 5, x 0 1 1 ∂ξ
∂x
∫ B 5 dΩ = ∫∫ 0 N 5, y J dξ dη = 2 ∫ ∫ 0 – η ------ dξ dη
∂ξ
Eq. 5•140
Ωe –1 – 1 N 5, y N 5, x –1 –1
∂x ∂y
– η ------ η ------
∂ξ ∂ξ

where we have use the relation N,x = N,ξ ξ,x, and the inversion of a 2 × 2 matrix

–1
∂ξ ∂ξ ∂x ∂x ∂y ∂x
------ ------ ------ ------ ------ ------
∂x ∂y ∂ξ ∂η 1 ∂η – ∂η
= = --
- Eq. 5•141
∂η ∂η ∂y ∂y J ∂y ∂x
------ ------ ------ ------ – ------ ------
∂x ∂y ∂ξ ∂η ∂ξ ∂ξ

For rectangular and parallelogram elements, the derivatives in Eq. 5•139 and Eq. 5•140 are constants through
out element domain. The integration of B4 and B5 over ξ and η = [-1, 1] are zero when the derivatives are con-
stant throught out the element domain, since B4 and B5, as defined in Eq. 5•139 and Eq. 5•140, become odd
functions of ξ and η. For element geometry other than rectangular or parallelogram, we can improve the behav-
ior of the non-conforming element. This can be achieved by evaluating the derivatives and the Jacobian for B4
and B5 only at the center [ξ0, η0] of the element. Such element has the improved behavior for element geometry
that are not rectangular or parallelogram (Taylor’s non-conforming element). Recall that the 8 bilinear 4-node
elements in the higher-order patch test (page 422 in Chapter 4) produces tip deflection of “-0.656467”, which is
significantly less than the exact solution of “-0.75”. Program Listing 5•10 implements the non-conforming ele-
ment discussed in this section. The Taylor’s non-conforming element can be invoked by setting macro definition
“__TEST_TAYLOR” at the compile time. The distortion of vertical element boundaries can be set by the macro
definition “__TEST_DISTORTION”. The results of eight non-conforming elements in the same problem in the
higher-order patch test are listed in the TABLE5• 1.

Distortion Wilson Taylor


d=0 -0.75 -0.75
d = 0.125 -0.591791 -0.680276
TABLE5• 1. Non-conforming element tip-deflections.

Without geometrical distortion both Wilson’s and Taylor’s non-conforming elements produce exact solution.
That is they both provide solution superior than the standard bilinear element. When the geometrical distortion

510 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods

#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_ = 1.e3; static const double v_ = 0.3;
#if defined(__TEST_DISTORTION)
static const double e_ = h_e_/2.0;
#else
static const double e_ = 0.0;
#endif
Omega_h::Omega_h() {
double v[2]; Node *node; define Ωu
v[0] = 0.0; v[1] = 0.0; node = new Node(0, 2, v); node_array().add(node);
1st row
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);
2nd row
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);
3rd row
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); 4-nodes element
int ena[4]; Omega_eh *elem;
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); boundary conditions
the_gh_array[node_order(4)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(4)][0] = 0.0;
the_gh_array[node_order(9)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(9)][0] = 0.0;
the_gh_array[node_order(14)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(14)][0] = 0.0;
the_gh_array[node_order(9)](1) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(9)][1] = 0.0;
the_gh_array[node_order(10)](0) = gh_on_Gamma_h::Neumann;
the_gh_array[node_order(10)][0] = -5.0;
the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Neumann;
the_gh_array[node_order(0)][0] = 5.0;
}

Workbook of Applications in VectorSpace C++ Library 511


Chapter 5 Advanced Finite Element Methods
static 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 Elastic_Nonconforming_Q4 : public Element_Formulation {
public:
Elastic_Nonconforming_Q4(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
Elastic_Nonconforming_Q4(int, Global_Discretization&);
};
Element_Formulation* Elastic_Nonconforming_Q4::make(int en, Global_Discretization& gd) {
return new Elastic_Nonconforming_Q4(en,gd);
}
static const double a_ = E_ / (1.0-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.0-v_)/2.0} };
C0 D = MATRIX("int, int, const double*", 3, 3, Dv[0]);
Elastic_Nonconforming_Q4::Elastic_Nonconforming_Q4(int en, Global_Discretization& gd) :
Element_Formulation(en, gd) {
#if defined(__TEST_TAYLOR)
Taylor’s non-conforming element
Quadrature qp(2, 4); 2 × 2 Gauss integration
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.0-Zai)*(1.0-Eta)/4.0; bilinear 4-nodes shape functions
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, B;
Wx &= W_x[0][0]; Wy &= W_x[0][1];
B &= (~Wx || C0(0.0)) & B = [B0, B1, B2, B3]
(C0(0.0) || ~Wy ) &
(~Wy || ~Wx );
C0 Kdd = ((~B) * (D * B)) | dv;
Quadrature qp1(2, 1); 1-point Gaussian integration
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 dx = d(x);
#define QPV quadrature_point_value
C0 x_zai = dx[0][0].QPV(0), y_zai = dx[1][0].QPV(0),
x_eta = dx[0][1].QPV(0), y_eta = dx[1][1].QPV(0);
H0 b = ((-((H0)Zai)*y_eta) | C0(0.0) | (((H0)Eta)*y_zai) | C0(0.0) )&
( C0(0.0) | (((H0)Zai)*x_eta) | C0(0.0) | (-((H0)Eta)*x_zai) ) &
b = [B4, B5]
((((H0)Zai)*x_eta) | (-((H0)Zai)*y_eta) | (-((H0)Eta)*x_zai) | (((H0)Eta)*y_zai) );
C0 Kaa = ((~b) * (D * b)) | dv,
Kau = ((~b) * (D * B)) | dv,
kû ≡ [ k uu – k αu
T k – 1 k ]û = f
αα αu
Kaa_inv = Kaa.inverse();
stiff &= Kuu- (~Kau)*Kaa_inv*Kau;

512 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
#else Wilson’s non-conforming elements is
int nonconforming_mode = 2;
Quadrature qp(2, 9); the default
int N_size = 4 + nonconforming_mode;
H1 Z(2, (double*)0, qp),
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE(
"int, int, Quadrature", N_size, 2, qp),
Zai, Eta;
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;
nodeless variable shape functions
H1 X;
N[4] = 1.0-Zai.pow(2); N 4 ≡ 1 – ξ 2, and N 5 ≡ 1 – η 2
N[5] = 1.0-Eta.pow(2);
C0 nodeless_x = MATRIX("int, int", 2, 2); nodeless_x = 0.0;
C0 x = xl & nodeless_x;
X &= N*x;
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, B;
wx &= w_x[0][0];
wy &= w_x[0][1];
B &= (~wx || C0(0.0)) &
(C0(0.0) || ~wy ) &
(~wy || ~wx );

C0 K = ((~B) * (D * B)) | dv,


Kdd = MATRIX("int, int, C0&, int, int", 8, 8, K, 0, 0),
Kaa = MATRIX("int, int, C0&, int, int", 4, 4, K, 8, 8),
Kad = MATRIX("int, int, C0&, int, int", 4, 8, K, 8, 0),
Kaa_inv = Kaa.inverse();
kû ≡ [ k uu – k αu
T k – 1 k ]û = f
αα αu
stiff &= Kdd - (~Kad)*Kaa_inv*Kad;
#endif
}

Element_Formulation* Element_Formulation::type_list = 0;
Element_Type_Register element_type_register_instance;
static Elastic_Nonconforming_Q4
elastic_nonconforming_q4_instance(element_type_register_instance);

int main() {
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;
}

Listing 5•10 Nonconforming element for plane elasticity (project: “nonconforming_element” in project
workspace file “fe.dsw”).

Workbook of Applications in VectorSpace C++ Library 513


Chapter 5 Advanced Finite Element Methods
sets in the Wilson’s non-conforming element deteriorates to “-0.591791”, while Taylor’s non-conforming ele-
ment provides improved solution of “-0.680276”, which is also better than the standard bilinear 4-node element.

514 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
Hourglass Element
The hourglass element for plane elasticity is similar to that for the 2-D heat condition. The modified element
stiffness matrix is still

ke = ke(1-point) + ke(hourglass) Eq. 5•142

The element stiffness matrix is of size 8 × 8 for bilinear 4-node element (i.e., {ndf × nen} × {ndf × nen}). There are
three independent relations provided by three equations of stress-strain relations. Therefore, the one point Gauss
integration produces ke(1-point) of rank 3, which is clearly rank deficient. The correct rank number for ke should
be 5, which is from the full-rank subtracts the three rigid-body-motions (8-3=5). Therefore, two trial hourglass
modes (expanded by Ψ to both u and v), corresponding to the x-hourglass and y-hourglass modes (see Figure
4•50d& e in Chapter 4), are used to define ke(hourglass) as

EJ ( b ⊗ b )
k e ( hourglass ) ≡ -------------------------- ⊗ ( Ψ ⊗ Ψ ) Eq. 5•143
12

where E is the Young’s modulus. We can view Eq. 5•36 for heat conduction as the 1-ndf degenerated version of
Eq. 5•143 (by using κ ( b • b ) , a scalar, in place of E ( b ⊗ b ), which is a 2 × 2 matrix).The hourglass element is
implemented in project “hourglass_element” in project workspace file “fe.dsw” and is shown in the Program
Listing 5•11.
We test the performance of the hourglass element by considering the same problem solved by project
“higher_order_patch_test”. The formulation of 1-point integration stiffness is the same as the stiffness matrix
computed with macro definition “__TEST_B_MATRIX_FORMULATION” set at compile time for project
“higher_order_patch_test”. The hourglass element takes only 0.5 second to assemble the global stiffness matrix
comparing to 4.5 seconds for the standard 2 × 2 integration with project “higher_order_patch_test” (on an obso-
lete 166 MHz PC). However, the tip deflection is -0.82 instead of -0.75 (exact), which is not very accurate.

Workbook of Applications in VectorSpace C++ Library 515


Chapter 5 Advanced Finite Element Methods

#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_ = 1.e3; static const double v_ = 0.3;
#if defined(__TEST_DISTORTION)
static const double e_ = h_e_/2.0;
#else
static const double e_ = 0.0;
#endif
Omega_h::Omega_h() {
double v[2]; Node *node; define Ωu
v[0] = 0.0; v[1] = 0.0; node = new Node(0, 2, v); node_array().add(node);
1st row
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);
2nd row
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);
3rd row
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); 4-nodes element
int ena[4]; Omega_eh *elem;
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); boundary conditions
the_gh_array[node_order(4)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(4)][0] = 0.0;
the_gh_array[node_order(9)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(9)][0] = 0.0;
the_gh_array[node_order(14)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(14)][0] = 0.0;
the_gh_array[node_order(9)](1) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(9)][1] = 0.0;
the_gh_array[node_order(10)](0) = gh_on_Gamma_h::Neumann;
the_gh_array[node_order(10)][0] = -5.0;
the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Neumann;
the_gh_array[node_order(0)][0] = 5.0;
}

516 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
static 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 Elastic_Hourglass_Q4 : public Element_Formulation { public:
Elastic_Hourglass_Q4(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
Elastic_Hourglass_Q4(int, Global_Discretization&);
};
Element_Formulation* Elastic_Hourglass_Q4::make(int en, Global_Discretization& gd) {
return new Elastic_Hourglass_Q4(en,gd); }
static const double a_ = E_ / (1.0-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.0-v_)/2.0} };
C0 D = MATRIX("int, int, const double*", 3, 3, Dv[0]);
Elastic_Hourglass_Q4::Elastic_Hourglass_Q4(int en, Global_Discretization& gd) :
Element_Formulation(en, gd) { Quadrature qp(2, 1);
H1 Z(2, (double*)0, qp), Zai, Eta, 1-point Gaussian quadrature
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 4, 2, qp);
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 unit(qp); unit = 1.0;
double vol = ((double)(unit | dv));
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 K_standard = ((~B) * (D * B)) | dv;
#if defined(__TEST_BELYTSCHKO)
double hv[4] = {1.0, -1.0, 1.0, -1.0}; Ψa = ha - (ha xa) bxa - (ha ya) bya
C0 h = VECTOR("int, const double*", 4, hv), phi = h - Nx.quadrature_point_value(0)*((~xl)*h);
#else
double l[4] = {0.0, 0.0, 0.0, 1.0};
C0 rhs = MATRIX("int, int", 4, 4), lhs = VECTOR("int, const double*", 4, l);
rhs[0] = 1.0; rhs[1] = xl(0); rhs[2] = xl(1); rhs[3] = lhs; C0 phi = lhs / rhs;
#endif
double factor = 2.0/norm(phi); phi *= factor; Ψa is Ψa normalized to ||Ψa|| =2
H0 s = INTEGRABLE_MATRIX("int, int, Quadrature", 2, 2, qp);
s[0][0] = Nx(0)*Nx(0); s[0][1] = s[1][0] = Nx(0)*Nx(1); s[1][1] = Nx(1)*Nx(1);
C0 pp = phi*(~phi), S = s|dv;
EJ ( b ⊗ b )
C0 K_hourglass = (E_*vol/48.0) *( k e ( hourglass ) ≡ -------------------------- ( Ψ ⊗ Ψ ) ,
( (S*pp[0][0]) | (S*pp[0][1]) | (S*pp[0][2]) | (S*pp[0][3]) ) & 12
( (S*pp[1][0]) | (S*pp[1][1]) | (S*pp[1][2]) | (S*pp[1][3]) ) &
( (S*pp[2][0]) | (S*pp[2][1]) | (S*pp[2][2]) | (S*pp[2][3]) ) &
( (S*pp[3][0]) | (S*pp[3][1]) | (S*pp[3][2]) | (S*pp[3][3]) ) ke(1-point) for irreducible formulation
); ke = ke(1-point) + ke(hourglass)
stiff &= K_standard + K_hourglass;
}
Element_Formulation* Element_Formulation::type_list = 0;
Element_Type_Register element_type_register_instance;
static Elastic_Hourglass_Q4 elastic_hourglass_q4_instance(element_type_register_instance);
int main() {
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;
}

Listing 5•11 Hourglass element for plane elasticity (project: “hourglass_element” in project workspace file
“fe.dsw”).

Workbook of Applications in VectorSpace C++ Library 517


Chapter 5 Advanced Finite Element Methods
5.1.3 Hybrid Formulation for Plane Elasticity
Hybrid formulation has field that is approximated at the interface of an element, also known as incomplete
field, not covering the entire element domain as they were always implicitly assumed in all of previous formula-
tions.
We consider basic physics of a body with internal discontinuities (see Figure 5•4).1 ΓI is the discontinuous
interface in between subdomain Ω0, and Ω1. The traction t0 at a point x0 on subdomain Ω0 is acting on a point x1
on subdomain Ω1. The traction t1 at the point x1 on subdomain Ω1 is acting on the point x0 on subdomain Ω0.
Under equilibrium, Newton’s third law of motion requires that tractions t0 and t1 be equal in magnitudes but
opposite in signs. That is

t0 = - t1 = λ Eq. 5•144

Irreducible Subdomains
The Euler-Lagrange equations applied on each of the two subdomains are

∫ δu0T L T σ0 dΩ – ∫ δu0T b dΩ – ∫ δu0T h 0 dΓ – ∫ δu0T t0 dΓ = 0


Ω0 Ω0 Γ0 ΓI

∫ δu1T L T σ1 dΩ – ∫ δu1T b dΩ – ∫ δu1T h 1 dΓ – ∫ δu1T t1 dΓ = 0 Eq. 5•145


Ω1 Ω1 Γ1 ΓI

The displacement continuity on ΓI is enforced by a constraint equation that

Γ0
Ω0
σ0 , u0 t = nσ (Cauchy’s formula)
n
t1
x0
ΓI tn = t • n
t0

Ω1 tt = t-tn
σ1 , u1
Γ1

Figure 5•4 Traction contact condition in the internal discontinuous surface ΓI. tn
is the normal component of t, and tt is its tangential component.

1. see p. 242 in Malvern, L.E., 1969, “Introduction to the mechanics of a continuous medium”, Prentice-Hall, Inc., Engle-
wood Cliffs, New Jersey.

518 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods

∫ δ λ T ( u1 – u0 ) dΓ Eq. 5•146
ΓI

The matrix form of Eq. 5•145 and Eq. 5•146 after finite element approximation is1

K 0 Q 0T 0 û 0 f0
Q 0 0 Q1 λ̂ = fI Eq. 5•147
0 Q 1T K 1 û 1 f1

where for i = 0, 1

Ki = ∫ BiT Di Bi dΩ Eq. 5•148


Ωi

Q i = ( – 1 ) i ∫ N λT N u dΓ Eq. 5•149
i
ΓI

fi = ∫ NuT b dΩ + ∫ NuT h i dΓ
i i
Eq. 5•150
Ωi Γi

In the spirit of the B-method, all internal fields are eliminated at the element level through static condensation
to make a displacement-only formulation that resembles the standard irreducible formulation. In the present case,
the displacement û in Eq. 5•147 can be eliminated, leave only with boundary forces λ̂, provided that the stiff-
ness matrices Ki are all invertible. However, that would require each subdomain be specified so that the rigid-
body-motions are precluded. Therefore, the singularity of the stiffness matrices is avoided. The difficulty in
removing rigid-body-motions for each subdomain limits the practical use of the hybrid method in the present
form.
The Program Listing 5•12 implements the hybrid irreducible domains formulation. The test problem for the
higher-order patch test is now illustrated in Figure 5•5. As stated earlier special difficulty arises that the subdo-
main “0” is not fully constrained that the rigid-body modes can be prevented. The solution procedure is pro-
ceeded as the followings. First the static condensation can still be applied to the subdomain “1”, since its is
constrained sufficiently to suppress the rigid body motions. From third equation of Eq. 5•147, since K1 is not sin-
gular we can have

û 1 = K 1–1 [ f 1 – Q 1T λ̂ ] Eq. 5•151

1. see p.375 in Zienkiewicz, O.C. and R.L. Taylor, 1989, “The finite element method”, 4th ed., vol. 1. McGraw-Hill, Inc.,
UK.

Workbook of Applications in VectorSpace C++ Library 519


Chapter 5 Advanced Finite Element Methods
Substituting Eq. 5•151 into second equation of Eq. 5•147, we have

 
Q 0 û 0 + Q 1  K 1– 1 [ f 1 – Q 1T λ̂ ]  = fI Eq. 5•152
 

Therefore,

λ̂ = [ Q 1 K 1–1 Q 1T ] –1 [ Q 0 û 0 + Q 1 K 1–1 f 1 – f I ] Eq. 5•153

Substituting into the first equation of Eq. 5•147,

K 0 û 0 + Q 0T { [ Q 1 K 1–1 Q 1T ] – 1 [ Q 0 û 0 + Q 1 K 1–1 f 1 – fI ] } = f0 Eq. 5•154

We can solve the displacement on the first subdomain by

û 0 = { K 0 + Q 0T [ Q 1 K 1–1 Q 1T ] –1 Q 0 } – 1 { f 0 + Q 0T [ Q 1 K 1–1 Q 1T ] –1 f I – Q 0T [ Q 1 K 1–1 Q 1T ] –1 Q 1 K 1– 1 f 1 } Eq. 5•155

After û 0 is obtained, λ̂ is computed from Eq. 5•153, then û 1 is computed from Eq. 5•151. The solution of this
computation shows that the tip-deflection is 0.75 (exact), and the horizontal traction λx on top and bottom of the
interface element has the magnitude of “15”, which is also exact.

Γ0 Γ1

15

Ωh0 Ωh 1
-15

ΓI
E =103, ν = 0.3

Figure 5•5 Bean bending problem for the hybrid irreducible domains formulation.

520 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods

#include "include\fe.h"
#include "include\omega_h_n.h"
Matrix_Representation_Couple::assembly_switch
Matrix_Representation_Couple::Assembly_Switch = Matrix_Representation_Couple::ALL;
#include "include\global_discretization_gamma_h_n.h"
static const double L_ = 10.0;
static const double c_ = 1.0;
static const double h_e_ = L_/((double)4.0);
static const double E_ = 1.e3;
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_);
static const double K_ = lambda_bar+2.0/3.0*mu_;
Omega_h_i::Omega_h_i(int i) : Omega_h(0){
if(i == 0) {
double v[2]; define Ωh0
Node *node;
v[0] = 0.0; v[1] = 0.0; node = new Node(0, 2, v); node_array().add(node);
1st row
v[0] = h_e_; node = new Node(1, 2, v); node_array().add(node);
v[0] = 2.0*h_e_; node = new Node(2, 2, v); node_array().add(node); 2nd row
v[0] = 0.0; v[1] = 1.0*c_; node = new Node(3, 2, v); node_array().add(node);
v[0] = 2.0*h_e_; node = new Node(4, 2, v); node_array().add(node);
v[0] = 0.0; v[1] = 2.0*c_; node = new Node(5, 2, v); node_array().add(node);
v[0] = h_e_; node = new Node(6, 2, v); node_array().add(node); 3rd row
v[0] = 2.0*h_e_; node = new Node(7, 2, v); node_array().add(node);
int ena[8]; Omega_eh *elem;
ena[0] = 0; ena[1] = 2; ena[2] = 7; ena[3] = 5; 4-nodes element
ena[4] = 1; ena[5] = 4; ena[6] = 6; ena[7] = 3;
elem = new Omega_eh(0, 0, 0, 8, ena);
omega_eh_array().add(elem);
} else if(i == 1) { define Ωh1
double v[2];
Node *node;
v[0] = 2.0*h_e_; v[1] = 0.0; node = new Node(0, 2, v); node_array().add(node);
v[0] = 3.0*h_e_; node = new Node(1, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(2, 2, v); node_array().add(node);
v[0] = 2.0*h_e_; v[1] = 1.0*c_; 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] = 2.0*h_e_; v[1] = 2.0*c_; node = new Node(5, 2, v); node_array().add(node);
v[0] = 3.0*h_e_; node = new Node(6, 2, v); node_array().add(node);
v[0] = 4.0*h_e_; node = new Node(7, 2, v); node_array().add(node);
int ena[8]; Omega_eh *elem;
ena[0] = 0; ena[1] = 2; ena[2] = 7; ena[3] = 5;
ena[4] = 1; ena[5] = 4; ena[6] = 6; ena[7] = 3;
elem = new Omega_eh(0, 0, 0, 8, ena);
omega_eh_array().add(elem);
} else if(i == 2) { define ΓI; interface line elements
double v[2];
Node *node;
v[0] = 2.0*h_e_; v[1] = 0.0; node = new Node(0, 2, v); node_array().add(node);
v[1] = 1.0*c_; node = new Node(1, 2, v); node_array().add(node);
v[1] = 2.0*c_; node = new Node(2, 2, v); node_array().add(node);
int ena[3]; Omega_eh *elem; ena[0] = 0; ena[1] = 1; ena[2] = 2;
elem = new Omega_eh(0, 0, 0, 3, ena);
omega_eh_array().add(elem);
}
}

Workbook of Applications in VectorSpace C++ Library 521


Chapter 5 Advanced Finite Element Methods
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(5)](0)= Nuemann B.C. bending moment on
the_gh_array[node_order(0)](0)=gh_on_Gamma_h::Neumann; left end
the_gh_array[node_order(5)][0] = -5.0; the_gh_array[node_order(0)][0] = 5.0;
} else if(i == 1) {
the_gh_array[node_order(2)](0) = the_gh_array[node_order(4)](0) = Dirichlet B.C. on right end
the_gh_array[node_order(7)](0) = the_gh_array[node_order(4)](1) =
gh_on_Gamma_h::Dirichlet;
}
}
Gamma_h_i::Gamma_h_i(int i, Omega_h &oh) : Omega_h(oh), the_index(i) { boundaries of Ωh0 & Ωh1 on ΓI
int ena[3]; Omega_eh *elem;
if(i == 0) { ena[0] = 2; ena[1] = 4; ena[2] = 7;
elem = new Omega_eh(0, 0, 0, 3, ena); the_omega_eh_array.add(elem); Γ0
} else if(i == 1) {
ena[0] = 0; ena[1] = 3; ena[2] = 5;
elem = new Omega_eh(0, 0, 0, 3, ena); the_omega_eh_array.add(elem);
Γ1
}
} First domain:
static int ndf = 2; static Omega_h_i oh_0(0);
static gh_on_Gamma_h_i gh_0(0, ndf, oh_0);
Ωh0 and B.C.
static U_h u_0(ndf, oh_0); u0
static Global_Discretization gd_0(oh_0, gh_0, u_0); Γ0
static Gamma_h_i gamma_h_0(0, oh_0);
static Global_Discretization_Gamma_h_i gd_gamma_0(
global discretization of Γ0
0, gd_0, (Global_Discretization*)0, gamma_h_0); Second domain:
static Omega_h_i oh_1(1); Ωh1 and B.C
static gh_on_Gamma_h_i gh_1(1, ndf, oh_1);
static U_h u_1(ndf, oh_1);
u1
static Global_Discretization gd_1(oh_1, gh_1, u_1); Γ1
static Gamma_h_i gamma_h_1(1, oh_1); global discretization of Γ1
static Global_Discretization_Gamma_h_i gd_gamma_1(
1, gd_1, (Global_Discretization*)0, gamma_h_1);
static Omega_h_i interface(2); Interface ΓI
static gh_on_Gamma_h_i interface_condition(2, ndf, interface); ΓI B.C. (do nothing)
static U_h lambda(ndf, interface);
static Global_Discretization gd_interface(interface, interface_condition, lambda);
λ and global discretization of ΓI
static Global_Discretization_Couple *interface_gamma_0_type = new {Ωh0, Γ0} global discretization couple
Global_Discretization_Couple(); { ΓI, Γ0} global discretization couple
static Global_Discretization_Couple gdc_interface_0(
gd_interface, gd_gamma_0, interface_gamma_0_type);
static Global_Discretization_Couple *interface_gamma_1_type = {Ωh1, Γ1} global discretization couple
new Global_Discretization_Couple(); { ΓI, Γ1} global discretization couple
static Global_Discretization_Couple gdc_interface_1(
gd_interface, gd_gamma_1, interface_gamma_1_type);
class Elastic_Hybrid_Irreducible_Q38 : public Element_Formulation_Couple { hybrid Q 3/8 element
public: 3-nodes line element on interface
Elastic_Hybrid_Irreducible_Q38(Element_Type_Register a) :
Element_Formulation_Couple(a) {}
8-node serendipity element on
Element_Formulation *make(int, Global_Discretization&); irreducible subdomains
Elastic_Hybrid_Irreducible_Q38(int, Global_Discretization&);
Element_Formulation_Couple *make(int, Global_Discretization_Couple&);
Elastic_Hybrid_Irreducible_Q38(int, Global_Discretization_Couple&);
};
Element_Formulation* Elastic_Hybrid_Irreducible_Q38::make(
int en, Global_Discretization& gd) {
return new Elastic_Hybrid_Irreducible_Q38(en,gd);
}

522 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
static const double a_ = E_ / (1.0-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.0-v_)/2.0}
};
C0 D = MATRIX("int, int, const double*", 3, 3, Dv[0]);
selective reduced integration with
Elastic_Hybrid_Irreducible_Q38::Elastic_Hybrid_Irreducible_Q38( λ−µ formulation for
int en, Global_Discretization& gd) : Element_Formulation_Couple(en, gd) { irreducible subdomains
Quadrature qp(2, 9);
H1 Z(2, (double*)0, qp),
3 × 3 Gauss quadrature
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 8, 2, qp),
Zai, Eta;
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;
serendipity shape function
N[2] = (1.0+Zai)*(1.0+Eta)/4.0; N[3] = (1.0-Zai)*(1.0+Eta)/4.0;
N[4] = (1.0-Zai.pow(2))*(1.0-Eta)/2.0; N[5] = (1.0-Eta.pow(2))*(1.0+Zai)/2.0;
N[6] = (1.0-Zai.pow(2))*(1.0+Eta)/2.0; N[7] = (1.0-Eta.pow(2))*(1.0-Zai)/2.0;
N[0] = N[0] - (N[4]+N[7])/2.0; N[1] = N[1] - (N[4]+N[5])/2.0;
N[2] = N[2] - (N[5]+N[6])/2.0; N[3] = N[3] - (N[6]+N[7])/2.0;
H1 X = N*xl;
H0 Nx = d(N) * d(X).inverse();
J dv(d(X).det());
C0 e = BASIS("int", ndf),
E = BASIS("int", nen),
u = e*E,
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_dev = mu_* (
Kdev
+( ((2.0*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.0*Wy*~Wy) +(Wx*~Wx))*((e[1]%e[1])*(E%E)) )
| dv);
Quadrature qp2(2, 4); 2 × 2 Gauss quadrature
H1 z(2, (double*)0, qp2),
n = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 8, 2, qp2),
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;
n[4] = (1-zai.pow(2))*(1-eta)/2; n[5] = (1-eta.pow(2))*(1+zai)/2;
n[6] = (1-zai.pow(2))*(1+eta)/2; n[7] = (1-eta.pow(2))*(1-zai)/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 dv2(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]; Kvol
C0 stiff_vol = lambda_bar* (+( wx*~wx*U[0][0]+
wx*~wy*U[0][1]+
wy*~wx*U[1][0]+

∫ BiT Di Bi dΩ
wy*~wy*U[1][1] )
| dv2 ); Ki =
stiff &= stiff_vol + stiff_dev; Ωi
}

Workbook of Applications in VectorSpace C++ Library 523


Chapter 5 Advanced Finite Element Methods
Element_Formulation_Couple* Elastic_Hybrid_Irreducible_Q38::make(
formulation for interface
int en, Global_Discretization_Couple& gdc) {
return new Elastic_Hybrid_Irreducible_Q38(en,gdc); }
Elastic_Hybrid_Irreducible_Q38::Elastic_Hybrid_Irreducible_Q38(
int en, Global_Discretization_Couple& gdc) : Element_Formulation_Couple(en, gdc) { 1-D (line integral)
Quadrature qp3(1, 3);
H1 Z(qp3), 3-points Gauss quadrature
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 3, 1, qp3);
N[0] = (Z-1.0)*Z/2.0; N[1] = (1.0+Z)*(1.0-Z); N[2] = Z*(1.0+Z)/2.0;
H1 X = N*xl;
J d_l(norm(d(X)(0)));
H0 N_u = ((~(H0)N) || C0(0.0) ) & (C0(0.0) || (~(H0)N)),
N_lambda;
N_lambda &= N_u;
if(gdc.type() == interface_gamma_0_type) stiff &= -(((~N_u)*N_lambda)|d_l);
else stiff &= (((~N_u)*N_lambda)|d_l); Q i = ( – 1 ) i ∫ N λT N u dΓ
i
}
ΓI
Element_Formulation* Element_Formulation::type_list = 0;
Element_Type_Register element_type_register_instance;
static Elastic_Hybrid_Irreducible_Q38
elastic_hybrid_irreducible_Q38_instance(element_type_register_instance);
static Matrix_Representation mr_K_0(gd_0);
static Matrix_Representation mr_K_1(gd_1);
static Matrix_Representation_Couple mrc_Q_0(gdc_interface_0, 0, 0, &(mr_K_0.rhs()) );
static Matrix_Representation_Couple
mrc_Q_1(gdc_interface_1, 0, &(mrc_Q_0.rhs()), &(mr_K_1.rhs()) );
int main() {
mr_K_0.assembly(); mr_K_1.assembly();
mrc_Q_0.assembly(); mrc_Q_1.assembly();
Ki
C0 K_0 = (C0)(mr_K_0.lhs()), K_1 = (C0)(mr_K_1.lhs()),
Q_0 = (C0)(mrc_Q_0.lhs()), Q_1 = (C0)(mrc_Q_1.lhs()), Qi
f_0 = (C0)(mr_K_0.rhs()), f_1 = (C0)(mr_K_1.rhs()), f_i = (C0)(mrc_Q_0.rhs()); f0, f1 , fI
Cholesky dK1(K_1);
C0 K1_inv = dK1.inverse(),
QK_inv_1 = Q_1*K1_inv,
QKQ1 = QK_inv_1*(~Q_1); û 0 = { K 0 + Q 0T [ Q 1 K 1–1 Q 1T ] –1 Q 0 } – 1
Cholesky dQKQ1(QKQ1);
C0 QKQ1_inv = dQKQ1.inverse(),
Q0t_QKQ1_inv = (~Q_0) * QKQ1_inv, { f 0 + Q 0T [ Q 1 K 1–1 Q 1T ] –1 f I
Q0t_QKQ1_inv_Q0 = Q0t_QKQ1_inv * Q_0;
Cholesky dlhs(K_0 + Q0t_QKQ1_inv_Q0);
– Q 0T [ Q 1 K 1–1 Q 1T ] – 1 Q 1 K 1–1 f1 }
C0 u_0 = dlhs*(f_0+Q0t_QKQ1_inv*f_i-Q0t_QKQ1_inv*Q_1*(dK1*f_1)),
lambda = dQKQ1 * (Q_0*u_0+Q_1*(dK1*f_1)-f_i),
u_1 = dK1*(f_1-(~Q_1)*lambda); λ̂ =[ Q1 K1–1 Q1T ] – 1 [ Q0 û0 + Q1 K1–1 f1 – f I ]
gd_interface.u_h() = lambda;

û 1 = K 1–1 [ f 1 – Q 1T λ̂ ]
gd_interface.u_h() = gd_interface.gh_on_gamma_h();
cout << "inteface traction:" << endl << gd_interface.u_h();
gd_0.u_h() = u_0;
gd_0.u_h() = gd_0.gh_on_gamma_h();
cout << "first domain displacement:" << endl << gd_0.u_h();
gd_1.u_h() = u_1;
gd_1.u_h() = gd_1.gh_on_gamma_h();
cout << "second domain displacement:" << endl << gd_1.u_h();
return 0;
}

Listing 5•12 Hybrid irreducible subdomains for plane elasticity (project: “hybrid_irreducible_subdomain”
in project workspace file “fe.dsw”).

524 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
Pian-Sumihara Element
In the previous section, the displacement continuity on the interface ΓI is constrained by Eq. 5•146. We can
use Eq. 5•146 with, instead of the variational principle for the irreducible formulation, the Hellinger-Reissner
variational principle for the mixed formulation (see page 468). Therefore, mixed form subdomains are linking by
the constraint equations with the Lagrangian functional

( σ i, u i, λ ) ≡ --- ∫ σ iT D –1 σ i dΩ + ∫ uiT ( LT ui + b ) dΩ – ∫ uiT ( n σi – h i ) dΓ + ( –1 ) i ∫ uiT λ dΓ


1
Eq. 5•156
2
Ωi Ωi Γh ΓI
i

where i = 0, 1. An alternative form of the Hellinger-Reissner variational principle can be used to develop highly
efficient and accurate element. The Pian-Sumihara element can be easily implemented with the assumed-stress
field as1

a 22 ( ξ – ξ 0 ) a 02 ( η – η 0 )
Nσ ≡ b 22 ( ξ – ξ 0 ) b 02 ( η – η 0 ) Eq. 5•157
a2 b2 ( ξ – ξ0 ) a0 b0 ( η – η0 )

where

3 3 3 3 3 3
a0 ≡ ∑ xa ξa, a1 ≡ ∑ x a ξa ηa, a 2 ≡ ∑ xa ηa, b0 ≡ ∑ y a ξa, b1 ≡ ∑ ya ξa ηa, b2 ≡ ∑ ya ηa Eq. 5•158
a=0 a=0 a=0 a=0 a=0 a=0

{xa, ya}T(a = 0, ..., 3) are nodal coordinate, {ξa, ηa}T = {{-1,-1}, {1,-1},{1,1},{-1,1}}, and

J1 J2 a 0 b 2 – a2 b 0 a0 b1 – a1 b0 a1 b2 – a2 b1
ξ 0 ≡ --------, η 0 ≡ --------, with J 0 ≡ ----------------------------, J 1 ≡ ---------------------------- , J 2 ≡ ---------------------------- Eq. 5•159
3J 0 3J 0 16 16 16

The element stiffness matrix is defined as

ke = ke(1-point) + ke(stabilizer) Eq. 5•160

where ke(1-point) is again the 1-point Gaussian integration on stiffness matrix from standard irreducible formula-
tion, and ke(stabilizer) is defined as

ke(stabilizer) = CTA-1C Eq. 5•161

1. see p.282-285 in Zienkiewicz, O.C. and R.L. Taylor, 1989, “The finite element method”, 4th ed., vol. 1. McGraw-Hill,
Inc., UK.

Workbook of Applications in VectorSpace C++ Library 525


Chapter 5 Advanced Finite Element Methods
with

∫ Nσ D –1 Nσ dΩ, ∫ N σ B dΩ
T T
A= and C = Eq. 5•162
Ωe Ωe

The Program Listing 5•12 implements the Pian-Sumihara element. For the test case in “higher-order patch test”
in nearly incompressible plane strain condition (with ν = 0.5 - 10-12), the tip-deflection of the Pian-Sumihara
element is “-0.566027”, which is comparable to the tip-deflection “-0.5625” in project
“incompressible_u_p_formulation” (with ν = 0.5 & plane strain). For element distortion test1, as shown in Fig-
ure 5•6, the Pian-Sumihara element produces tip-deflection that is 80% of that of the element without distortion,
which is far better than bilinear 4-node element. The Pian-Sumihara element is praised as the most efficient and
accurate four-noded element to date.

E = 75, ν = 0.5 - 10-12


-0.5 1

0.5
10

Figure 5•6 Element distortion test for the Pian-Sumihara element.

1. see p.387 in Zienkiewicz, O.C. and R.L. Taylor, 1989, “The finite element method”, 4th ed., vol. 1. McGraw-Hill, Inc.,
UK.

526 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods

#include "include\fe.h"
static const double L_ = 10.0; static const double c_ = 2.0; static const double h_e_ = L_/2.0;
#if defined(__TEST_HIGHER_ORDER_PATCH_TEST)
static const double E_ = 1000.0;
static const double v_ = 0.5-1.e-12;
#else
static const double E_ = 1500.0; static const double v_ = 0.25;
#endif
#if defined(__TEST_DISTORTION)
static const double e_ = h_e_/10.0;
#else
static const double e_ = 0.0;
#endif
EP::element_pattern EP::ep = EP::QUADRILATERALS_4_NODES;
Omega_h::Omega_h() {
#if defined(__TEST_HIGHER_ORDER_PATCH_TEST) eight 4-nodes quadrilaterals
double x[4][2] = {{0.0, 0.0}, {10.0, 0.0}, {10.0, 2.0}, {0.0, 2.0}};
int control_node_flag[4] = {1, 1, 1, 1}, col_node_no = 5, row_node_no = 3;
block(this, row_node_no, col_node_no, 4, control_node_flag, x[0]);
#else
double v[2]; Node *node; two 4-nodes quadrilaterals
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_; node = new Node(2, 2, v); node_array().add(node);
v[0] = 0.0; v[1] = c_; node = new Node(3, 2, v); node_array().add(node);
v[0] = h_e_+e_; node = new Node(4, 2, v); node_array().add(node);
v[0] = 2.0*h_e_; node = new Node(5, 2, v); node_array().add(node);
int ena[4]; Omega_eh *elem; ena[0] = 0; ena[1] = 1; ena[2] = 4; ena[3] = 3;
elem = new Omega_eh(0, 0, 0, 4, ena); 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); omega_eh_array().add(elem);
#endif
}
gh_on_Gamma_h::gh_on_Gamma_h(int df, Omega_h& omega_h) {
__initialization(df, omega_h);
#if defined(__TEST_HIGHER_ORDER_PATCH_TEST) test case from p. 301 “Load 2”
int col_node_no = 5, row_node_no = 3; Zienkiewicz & Taylor vol.1
for(int i = 0; i < row_node_no; i++) {
the_gh_array[node_order((i+1)*col_node_no-1)](0) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order((i+1)*col_node_no-1)][0] = 0.0;
}
the_gh_array[node_order(col_node_no*((row_node_no+1)/2)-1)](1) =
gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(col_node_no*((row_node_no+1)/2)-1)][1] = 0.0;
double h_ = 1.0, f_ = 15.0;
the_gh_array[node_order(2*col_node_no)](0) = gh_on_Gamma_h::Neumann;
the_gh_array[node_order(2*col_node_no)][0] = -f_*(1.0/3.0)*h_;
the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Neumann;
the_gh_array[node_order(0)][0] = f_*(1.0/3.0)*h_;
#else
the_gh_array[node_order(2)](0) = the_gh_array[node_order(5)](0) =
test case B.C. #3 from p. 386 , distorted
the_gh_array[node_order(2)](1) = gh_on_Gamma_h::Dirichlet; element configuration in p. 387 of
the_gh_array[node_order(3)](0) = gh_on_Gamma_h::Neumann; Zienkiewicz & Taylor vol.1
the_gh_array[node_order(3)][0] = 1000.0;
the_gh_array[node_order(0)](0) = gh_on_Gamma_h::Neumann;
the_gh_array[node_order(0)][0] = -1000.0;
#endif
}

Workbook of Applications in VectorSpace C++ Library 527


Chapter 5 Advanced Finite Element Methods
class Elastic_Pian_Sumihara_Q4 : public Element_Formulation {
public:
Elastic_Pian_Sumihara_Q4(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
Elastic_Pian_Sumihara_Q4(int, Global_Discretization&);
};
Element_Formulation* Elastic_Pian_Sumihara_Q4::make(int en, Global_Discretization& gd) {
return new Elastic_Pian_Sumihara_Q4(en,gd);
}
#if defined(__TEST_PLANE_STRAIN_NEARLY_INCOMPRESSIBLE)
static const double a_ = E_*(1-v_)/(1+v_)/(1-2*v_);
static const double Dv[3][3] = {
{a_, a_*v_/(1-v_), 0 .0},
{a_*v_/(1-v_), a_, 0.0},
{0.0, 0.0, a_*(1-2*v_)/2.0/(1-v_)}};
#else
static const double a_ = E_ / (1.0-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.0-v_)/2.0}};
#endif
C0 D = MATRIX("int, int, const double*", 3, 3, Dv[0]);
Elastic_Pian_Sumihara_Q4::Elastic_Pian_Sumihara_Q4(int en, Global_Discretization& gd) :
Element_Formulation(en, gd) {
Quadrature qp(2, 1);
H1 Z(2, (double*)0, qp),
2-D 1-point Gauss integration
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 4, 2, qp),
Zai, Eta;
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, B;
Wx &= W_x[0][0];
Wy &= W_x[0][1];
B &= (~Wx || C0(0.0)) &
(C0(0.0) || ~Wy ) & 4-node nodal natural coordinates
(~Wy || ~Wx );
C0 K_0 = ((~B) * (D * B)) | dv; 3 3 3
double a[3], b[3], j[3], a 0≡∑ x a ξ a, a 1 ≡∑ x a ξa η a , a 2≡ ∑ x a η a
zai[4] = {-1.0, 1.0, 1.0, -1.0}, a=0 a=0 a=0
eta[4] = {-1.0, -1.0, 1.0, 1.0};
a[0] = xl[0][0]*zai[0]+xl[1][0]*zai[1]+xl[2][0]*zai[2]+xl[3][0]*zai[3]; 3 3 3
a[1] = xl[0][0]*zai[0]*eta[0]+xl[1][0]*zai[1]*eta[1]+ b 0≡ ∑ y a ξ a , b 1 ≡ ∑ y a ξ a η a, b 2≡ ∑ y a η a
xl[2][0]*zai[2]*eta[2]+xl[3][0]*zai[3]*eta[3]; a=0 a=0 a=0
a[2] = xl[0][0]*eta[0]+xl[1][0]*eta[1]+xl[2][0]*eta[2]+xl[3][0]*eta[3];
b[0] = xl[0][1]*zai[0]+xl[1][1]*zai[1]+xl[2][1]*zai[2]+xl[3][1]*zai[3]; a 0 b 2 – a2 b 0 a0 b1 – a1 b0
b[1] =xl[0][1]*zai[0]*eta[0]+xl[1][1]*zai[1]*eta[1]+ J 0 ≡ ----------------------------, J 1 ≡ ----------------------------
16 16
xl[2][1]*zai[2]*eta[2]+xl[3][1]*zai[3]*eta[3];
b[2] = xl[0][1]*eta[0]+xl[1][1]*eta[1]+xl[2][1]*eta[2]+xl[3][1]*eta[3]; a1 b2 – a2 b1
j[0] = a[0]*b[2]-a[2]*b[0]; J 2 ≡----------------------------
j[1] = a[0]*b[1]-a[1]*b[0]; 16
j[2] = a[1]*b[2]-a[2]*b[1];
double zai_0 = j[1]/(j[0]*3.0),
J1 J2
ξ 0 ≡ --------, η 0 ≡ --------
eta_0 = j[2]/(j[0]*3.0); 3J 0 3J 0

528 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
Quadrature qp2(2, 4); 2-D 2 × 2 Gauss quadrature
H1 z(2, (double*)0, qp2),
n = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 4, 2, qp2);
n[0] = (1.0-z[0])*(1.0-z[1])/4.0;
n[1] = (1.0+z[0])*(1.0-z[1])/4.0;
n[2] = (1.0+z[0])*(1.0+z[1])/4.0;
n[3] = (1.0-z[0])*(1.0+z[1])/4.0;

H1 x = n*xl;
H0 nx = d(n) * d(x).inverse();
J dv2(d(x).det());

H0 w_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, nsd, nx),


wx, wy, B2, N_sigma_i, N_sigma;
wx &= w_x[0][0];
wy &= w_x[0][1];
B2 &= (~wx || C0(0.0)) &
(C0(0.0) || ~wy ) & a 22 ( ξ – ξ 0 ) a 02 ( η – η 0 )
(~wy || ~wx );
Nσ ≡ b 22 ( ξ – ξ 0 ) b 02 ( η – η 0 )
N_sigma_i &= ( (pow(a[2],2)*(((H0)z[0])-zai_0)) | (pow(a[0],2)*(((H0)z[1])-eta_0)) ) &
( (pow(b[2],2)*(((H0)z[0])-zai_0)) | (pow(b[0],2)*(((H0)z[1])-eta_0)) ) & a2 b2 ( ξ – ξ0 ) a0 b 0 ( η – η 0 )
( (a[2]*b[2]*(((H0)z[0])-zai_0)) | (a[0]*b[0]*(((H0)z[1])-eta_0)) );
N_sigma &= (N_sigma_i | N_sigma_i | N_sigma_i | N_sigma_i);
C0 C = ((~N_sigma)*B2) | dv2,
D_inv = D.inverse(),
∫ Nσ D–1 Nσ dΩ, ∫ N σ B dΩ
T T
A = ((~N_sigma)*D_inv*N_sigma) | dv2, A= C =
A_inv = A.inverse(), Ωe Ωe
K_stabilizer = (~C)*A_inv*C;

stiff &= K_0 + K_stabilizer; ke(stabilizer) = CTA-1C


} ke = ke(1-point) + ke(stabilizer)
Element_Formulation* Element_Formulation::type_list = 0;
Element_Type_Register element_type_register_instance;
static Elastic_Pian_Sumihara_Q4
elastic_pian_sumihara_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();
return 0;
}

Listing 5•13 Hybrid Pian-Sumihara element for plane elasticity (project: “hybrid_pian_sumihara” in
project workspace file “fe.dsw”).

Workbook of Applications in VectorSpace C++ Library 529


Chapter 5 Advanced Finite Element Methods
5.1.4 Reissner-Mindlin (Thick-) Plate Formulations

θ-w Irreducible Formulation


FromEq. 4•250 in Chapter 4, the transverse shear strain γ is defined as

∂w
γx θx -------
∂x
γ = = – + = ( – θ ) + ∇w Eq. 5•163
γy θy ∂w
-------
∂y

In Reissner-Mindlin plate theory, the “fiber” is assumed to remain in plane, but it is not assumed to keep perpen-
dicular to the mid-surface as in thin plate theory. That is the transverse shear, γ, is not assumed to be zero. The
bending moment constitutive equations from Eq. 4•251 in Chapter 4 is

M = DL θ Eq. 5•164

The shear force relations to the bending moments and vertical loads from Eq. 4•254 in Chapter 4 are

L T M + S = 0, and ∇ T S + q = 0 Eq. 5•165

First, M can be eliminated by substituting Eq. 5•164 into first part of Eq. 5•165 as

L T DL θ + S = 0 Eq. 5•166

Then, the constitutive equation for shear force and transverse shear strain is S = αγ (Eq. 4•253 in Chapter 4)
where α is the shear rigidity. Therefore,

--- + θ – ∇w = 0
S
Eq. 5•167
α

from the definition of the transverse shear strain γ ≡ ( – θ ) + ∇w in Eq. 5•163. Eq. 5•167 can be re-arranged as S
= α ( – θ + ∇w ) . Then, this is used to eliminate the shear force S in Eq. 5•166 as

L T DL θ – α ( θ – ∇w ) = 0 Eq. 5•168

and from the second part of Eq. 5•165

∇ T [ α ( θ – ∇w ) ] = q Eq. 5•169

Galerkin approximations to “θ” and “w” are made separately

θ = N θ θ̂ , and w = N w ŵ Eq. 5•170

530 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
Weak forms to Eq. 5•168 and Eq. 5•169 can be obtained by multiplying Nθ to Eq. 5•168 and Nw to Eq. 5•169 as

∫ ( NθT ( L T DLNθ ) – NθT αNθ ) dΩ θ̂ + ∫ NθT α∇Nw dΩŵ = 0


Eq. 5•171
Ω Ω

and

∫ NwT ∇ T αNθ dΩ θ̂ – ∫ NwT ∇ T α∇Nw dΩŵ = ∫ NwT q dΩ Eq. 5•172


Ω Ω Ω

Integrating by parts to first term of Eq. 5•171 and to the left-hand-side terms of Eq. 5•172 and apply Green’s the-
orem

∫ ( ( LNθ ) T DLNθ + NθT αNθ ) dΩ θ̂ – ∫ NθT α∇Nw dΩŵ = ∫ NθT MΓ dΓ Eq. 5•173
Ω Ω Γ

and

– ∫ ( ∇N w ) T αN θ dΩ θ̂ + ∫ ( ∇Nw ) T α∇N w dΩŵ = ∫ NwT q dΩ – ∫ NwT SΓ dΓ Eq. 5•174


Ω Ω Ω Γ

or in matrix form

T ŵ
K s K bs fw
= Eq. 5•175
K bs K b θ̂ fθ

where

Ks = ∫ ( ∇Nw ) T α∇Nw dΩ Eq. 5•176


K bs = – ∫ N θT α∇N w dΩ Eq. 5•177


Kb = ∫ ( ( LNθ ) T DLNθ + NθT αNθ ) dΩ Eq. 5•178


fw = ∫ NwT q dΩ – ∫ NwT SΓ dΓ Eq. 5•179


Ω Γ

fθ = ∫ NθT MΓ dΓ Eq. 5•180


Γ

Workbook of Applications in VectorSpace C++ Library 531


Chapter 5 Advanced Finite Element Methods
In Eq. 5•175 the submatrices involve shear constraints can be separated from the one that only involve bending
energy as

T
K s K bs Ks T
K bs 0 0
= + ≡ KS + KB Eq. 5•181
K bs K b K bs K b ( α ) 0 Kb ( D )

where the submatrices

Kb(α) = ∫ NθT αNθ dΩ, and K b ( D ) = ∫ ( LNθ ) T DLNθ dΩ Eq. 5•182


Ω Ω

The matrix KS involves components that enforce the shear constraints, and KB is the part that has only to do
with the bending energy. This stiffness splitting is useful for selective reduced integration, in which the reduced
integration is applied to all the submatrices in the shear constraint part, KS, to avoid shear locking.
The Program Listing 5•14 implements the “heterosis element” illustrated in Figure 5•7. The deflection
degree of freedom, w, uses eight-node serendipity shape function, and the rotation degrees of freedom, θ = [θx ,
θy]T, use Lagrangian 9-node shape function. All terms except the bending stiffness submatrix Kb(D) (or KB stiff-
ness matrix) use the reduced (2 × 2) integration. We solve the same test problem in the thin plate section. The
maximum deflection is 222358 at center, which is comparing to the exact solution of the thin-plate theory of
226800.

:w

: 2x2 reduced integration on


shear constrinats related terms

Figure 5•7 Heterosis element for irreducible thick plate formulation with Serendipity shape
function for deflection (w) and Lagrangian shape function for rotation (θ). The bending stiffness
submatrix Kb(D) is fully integrated with 3 × 3 integration. All other terms, that involve shear
constraints, are selective reduced integrated (2 × 2).

532 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods

#include "include\fe.h"
#include "include\omega_h_n.h"
Matrix_Representation_Couple::assembly_switch
Matrix_Representation_Couple::Assembly_Switch = Matrix_Representation_Couple::ALL;
Omega_h_i::Omega_h_i(int i) : Omega_h(0) { Heterosis element
if(i == 0) { int row_segment_no = 2, count=0;
Node *node; double v[2], h = 1.0/((double)(row_segment_no)); Ωw; 8-node serendipity element
for(int j = 0; j < row_segment_no; j++) {
v[1] = ((double)j)*h;
for(int k = 0; k < (2*row_segment_no+1); k++) {
v[0] = ((double)k)*h/2; node = new Node(count++, 2, v); the_node_array.add(node);
}
v[1] += h/2.0;
for(int k = 0; k < row_segment_no+1; k++) {
v[0] = ((double)k)*h; node = new Node(count++, 2, v); the_node_array.add(node);
}
}
v[1] = 1.0;
for(int j = 0; j < (2*row_segment_no+1); j++) {
v[0] = ((double)j)*h/2.0; node = new Node(count++, 2, v); the_node_array.add(node);
}
int ena[8]; Omega_eh *elem; count = 0;
for(int j = 0; j < row_segment_no; j++)
for(int k = 0; k < row_segment_no; k++) {
int first_node = j*(3*row_segment_no+2)+k*2;
ena[0] = first_node; ena[1] = ena[0]+2; ena[2] = ena[1]+(3*row_segment_no+2);
ena[3] = ena[2]-2; ena[4] = ena[0]+1;
ena[5] = (j+1)*(2*row_segment_no+1)+j*(row_segment_no+1)+k+1;
ena[6] = ena[3]+1; ena[7] = ena[5]-1;
elem = new Omega_eh(count++, 0, 0, 8, ena); the_omega_eh_array.add(elem);
}
} else if(i == 1) { Ωθ; Lagrangian 9-node element
int row_segment_no = 2;
int count = 0; Node *node; double v[2], h = 1.0/((double)(row_segment_no));
for(int j = 0; j < row_segment_no; j++) {
v[1] = ((double)j)*h;
for(int k = 0; k < (2*row_segment_no+1); k++) {
v[0] = ((double)k)*h/2; node = new Node(count++, 2, v); the_node_array.add(node);
}
v[1] += h/2.0;
for(int k = 0; k < (2*row_segment_no+1); k++) {
v[0] = ((double)k)*h/2; node = new Node(count++, 2, v); the_node_array.add(node);
}
}
v[1] = 1.0;
for(int j = 0; j < (2*row_segment_no+1); j++) {
v[0] = ((double)j)*h/2.0; node = new Node(count++, 2, v); the_node_array.add(node);
}
int ena[9]; Omega_eh *elem; count = 0;
for(int j = 0; j < row_segment_no; j++) for(int k = 0; k < row_segment_no; k++) {
int row_node_no = 2*row_segment_no+1, first_node = j*2*row_node_no+k*2;
ena[0] = first_node; ena[1] = ena[0]+2; ena[2] = ena[1]+2*row_node_no;
ena[3] = ena[2]-2; ena[4] = ena[0]+1; ena[5] = ena[1]+row_node_no;
ena[6] = ena[2]-1; ena[7] = ena[0]+row_node_no; ena[8] = ena[7] +1;
elem = new Omega_eh(count++, 0, 0, 9, ena); the_omega_eh_array.add(elem);
}
}
}

Workbook of Applications in VectorSpace C++ Library 533


Chapter 5 Advanced Finite Element Methods
gh_on_Gamma_h_i::gh_on_Gamma_h_i(int i, int df, Omega_h& omega_h) : gh_on_Gamma_h() { boundary conditions
gh_on_Gamma_h::__initialization(df, omega_h);
if(i == 0) {
the_gh_array[node_order(4)](0) = the_gh_array[node_order(7)](0) =
w=0
the_gh_array[node_order(12)](0) = the_gh_array[node_order(15)](0)= right
the_gh_array[node_order(16)](0) = the_gh_array[node_order(17)](0)=
the_gh_array[node_order(18)](0) = the_gh_array[node_order(19)](0)=
the_gh_array[node_order(20)](0) = gh_on_Gamma_h::Dirichlet;
top
} else if(i == 1) { θ=0
the_gh_array[node_order(4)](0) = the_gh_array[node_order(4)](1) = right
the_gh_array[node_order(9)](0) = the_gh_array[node_order(9)](1) =
the_gh_array[node_order(14)](0) = the_gh_array[node_order(14)](1) =
the_gh_array[node_order(19)](0) = the_gh_array[node_order(19)](1) =
the_gh_array[node_order(20)](0) = the_gh_array[node_order(20)](1) = top
the_gh_array[node_order(21)](0) = the_gh_array[node_order(21)](1) =
the_gh_array[node_order(22)](0) = the_gh_array[node_order(22)](1) =
the_gh_array[node_order(23)](0) = the_gh_array[node_order(23)](1) =
the_gh_array[node_order(24)](0) = the_gh_array[node_order(24)](1) = bottom
the_gh_array[node_order(0)](1) = the_gh_array[node_order(1)](1) =
the_gh_array[node_order(2)](1) = the_gh_array[node_order(3)](1) =
the_gh_array[node_order(0)](0) = the_gh_array[node_order(5)](0) = left
the_gh_array[node_order(10)](0) = the_gh_array[node_order(15)](0) =
gh_on_Gamma_h::Dirichlet;
}
}
static Global_Discretization *w_type = new Global_Discretization;
static Global_Discretization *theta_type = new Global_Discretization;
class Plate_Heterosis : public Element_Formulation_Couple {
public:
Plate_Heterosis(Element_Type_Register a) : Element_Formulation_Couple(a) {} diagonal formulation
Element_Formulation *make(int, Global_Discretization&);
Plate_Heterosis(int, Global_Discretization&);
Element_Formulation_Couple *make(int, Global_Discretization_Couple&); off-diagonal formulation
Plate_Heterosis(int, Global_Discretization_Couple&);
};
Element_Formulation* Plate_Heterosis::make(int en, Global_Discretization& gd) {
return new Plate_Heterosis(en,gd);
} 1 ν 0
static const double E_ = 1.0; static const double v_ = 0.25; static const double t_ = 0.01; Et 3 ν 1 0
D = -------------------------
static const double D_ = E_ * pow(t_,3) / (12.0*(1-pow(v_,2))); 12 ( 1 – ν 2 ) 1–ν
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} }; 0 0 ------------
C0 D = MATRIX("int, int, const double*", 3, 3, Dv[0]); 2
static const double mu_ = E_/(2*(1+v_));
static const double alpha_ = (5.0/6.0)*mu_*t_;
Plate_Heterosis::Plate_Heterosis(int en, Global_Discretization& gd) :
Element_Formulation_Couple(en, gd) {
if(gd.type() == w_type) {
Quadrature qp(2, 4);
Ks formulation
H1 Z(2, (double*)0, qp), Zai, Eta, 2 × 2 reduced Gauss integration for
N=INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 8, 2, qp) ; shear constraints
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; serendipity shape functions for w dof
N[4] = (1-Zai.pow(2))*(1-Eta)/2.0; N[5] = (1-Eta.pow(2))*(1+Zai)/2.0;
N[6] = (1-Zai.pow(2))*(1+Eta)/2.0; N[7] = (1-Eta.pow(2))*(1-Zai)/2.0;
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;
J dv(d(X).det()); H0 Nx = d(N) * d(X).inverse();

534 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
H0 W_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, 2/*nsd*/, Nx), Wx, Wy, grad_W;
Wx &= W_x[0][0]; Wy &= W_x[0][1]; Ks = ∫ ( ∇Nw )T α∇Nw dΩ
grad_W &= (~Wx) & (~Wy); Ω
stiff &= ((~grad_W)* (alpha_ * grad_W)) | dv;
double f_0 = 1.0;
force &= (((H0)N)*f_0) | dv;
fw = ∫ NwT q dΩ

} else if (gd.type() == theta_type) {
Quadrature qp(2, 9); Kb formulation
H1 Z(2, (double*)0, qp), Zai, Eta, 3 × 3 Gauss integration for bending
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 9, 2, qp);
Zai &= Z[0]; Eta &= Z[1];
Lagrangian shape functions for θ dof
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());
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 );
Quadrature qpr(2, 4);
2 × 2 reduced Gauss integration for
H1 z(2, (double*)0, qpr),zai, eta, shear constraints
n =INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 9, 2, qpr); Lagrangian shape functions for θ dof
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; J dv(d(x).det()); C0 zero(0.0);
H0 n0 = (H0)n[0], n1 = (H0)n[1], n2 = (H0)n[2], n3 = (H0)n[3], n4 = (H0)n[4],
n5 = (H0)n[5], n6 = (H0)n[6], n7 = (H0)n[7], n8 = (H0)n[8];
H0 n_theta = (n0|zero|n1| zero|n2 |zero|n3|zero|n4|zero|n5|zero|n6|zero|n7|zero|n8|zero) & Nθ
(zero|n0|zero|n1|zero|n2|zero|n3|zero|n4|zero|n5|zero|n6|zero|n7|zero|n8);
stiff &= ((~B)*(D*B) | dV) + ((~n_theta)*(alpha_*n_theta) | dv);
} Kb = ∫ ( ( LNθ ) T DLNθ + NθT αNθ ) dΩ
} Ω
Element_Formulation_Couple* Plate_Heterosis::make(
int en, Global_Discretization_Couple& gdc) {
return new Plate_Heterosis(en,gdc);
} Kbs formulation
Plate_Heterosis::Plate_Heterosis(int en, Global_Discretization_Couple& gdc) :
Element_Formulation_Couple(en, gdc) {
2 × 2 reduced Gauss integration
Quadrature qp(2, 4);
H1 Z(2, (double*)0, qp), Zai, Eta, Lagrangian shape functions for θ dof
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;
H1 X = N*xl; J dv(d(X).det());

Workbook of Applications in VectorSpace C++ Library 535


Chapter 5 Advanced Finite Element Methods
H0 N0 = N[0], N1 = N[1], N2 = N[2], N3 = N[3],
N4 = N[4], N5 = N[5], N6 = N[6], N7 = N[7], N8 = N[8];
H0 zero(qp); zero = 0.0;
H0 N_theta = (N0|zero|N1|zero|N2|zero|N3|zero|N4|zero|N5|zero|N6|zero|N7|zero|N8|zero) & Nθ
(zero|N0|zero|N1|zero|N2|zero|N3|zero|N4|zero|N5|zero|N6|zero|N7|zero|N8);
serendipity shape functions for w dof
H1 n = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 8, 2, qp);
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[4] = (1-Zai.pow(2))*(1-Eta)/2; n[5] = (1-Eta.pow(2))*(1+Zai)/2;
n[6] = (1-Zai.pow(2))*(1+Eta)/2; n[7] = (1-Eta.pow(2))*(1-Zai)/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;
H0 nx = d(n) * d(X).inverse();
H0 w_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, 2/*nsd*/, nx), wx, wy;
∇w
wx &= w_x[0][0]; wy &= w_x[0][1];
H0 grad_w = (~wx) & (~wy);
stiff &= -alpha_*( (~N_theta)*(grad_w) | dv ); K bs = – ∫ N θT α∇N w dΩ
}

Element_Formulation* Element_Formulation::type_list = 0;
static Element_Type_Register element_type_register_instance;
static Plate_Heterosis plate_heterosis_instance(element_type_register_instance);
int main() {
const int w_ndf = 1;
Omega_h_i oh_w(0); w
gh_on_Gamma_h_i w_gh(0, w_ndf, oh_w);
U_h w_h(w_ndf, oh_w);

θ
Global_Discretization w_gd(oh_w, w_gh, w_h, w_type);
const int theta_ndf = 2;
Omega_h_i oh_theta(1);
gh_on_Gamma_h_i theta_gh(1, theta_ndf, oh_theta);
U_h theta_h(theta_ndf, oh_theta);
Global_Discretization theta_gd(oh_theta, theta_gh, theta_h, theta_type);
Global_Discretization_Couple gdc(theta_gd, w_gd); θ-w
Matrix_Representation mr_w(w_gd);
Matrix_Representation mr_theta(theta_gd);
Matrix_Representation_Couple mrc(gdc, 0, &(mr_theta.rhs()), &(mr_w.rhs()), &mr_w);
mr_w.assembly();
mr_theta.assembly();
mrc.assembly();
C0 Ks = ((C0)(mr_w.lhs())), f_w = ((C0)(mr_w.rhs())), Ks , and fw
Kbs = ((C0)(mrc.lhs())), Kbs
Kb = ((C0)(mr_theta.lhs())), f_theta = ((C0)(mr_theta.rhs()));
Kb, and fθ
Cholesky dKs(Ks);
C0 Ks_inv = dKs.inverse(),
KbsKs_invKbst = Kbs*Ks_inv*(~Kbs),
K = Kb-KbsKs_invKbst,
f = f_theta-Kbs*(dKs*f_w);
θ̂=(Kb-Kbs(Ks)-1KbsT)-1
LU dK(K); (fθ-Kbs(Ks)-1fw)
C0 theta = dK*f,
w = dKs*(f_w-(~Kbs)*theta);
ŵ = (Ks)-1(fw-KbsTθ̂ )
w_h = w; w_h = w_gd.gh_on_gamma_h();
cout << "deflection w:" << endl << w_h << endl;
theta_h = theta; theta_h = theta_gd.gh_on_gamma_h();
cout << "rotation (theta_x, theta_y):" << endl;
for(int i = 0; i < theta_h.total_node_no(); i++) cout << theta_h[i] << endl;
return 0;
}

Listing 5•14 Heterosis element for θ-w irreducible thick plate formulation (project:
“irreducible_thick_plate” in project workspace file “fe.dsw”).

536 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
θ-S-w Mixed Formulation
Eq. 5•166, Eq. 5•167 and second part of Eq. 5•165 give

L T DL θ + S = 0 Eq. 5•183

--- + θ – ∇w = 0
S
Eq. 5•184
α

∇TS + q = 0 Eq. 5•185

Three variables in Eq. 5•183 to Eq. 5•185 [θ, S, w]T are approximated as

θ = N θ θ̂ , S = N S S and w = Nw ŵ
ˆ
Eq. 5•186

The weighted residual statement of Eq. 5•183 to Eq. 5•185 are

∫ NθT LT DLNθ dΩ θ̂ + ∫ NθT NS dΩŜ = 0 Eq. 5•187


Ω Ω

∫ NST Nθ dΩ θ̂ + ∫ NST  --α- NS dΩŜ – ∫ NST ∇Nw dΩŵ


1
= 0 Eq. 5•188
Ω Ω Ω

∫ NwT ∇ T NS dΩŜ + ∫ NwT q dΩ = 0 Eq. 5•189


Ω Ω

Integrating by parts on the first term of Eq. 5•187 and the first term of Eq. 5•189, then apply Green’s theorem,
and simply change sign of Eq. 5•188 yield

∫ ( LNθ )T DLNθ dΩ θ̂ – ∫ NθT NS dΩŜ = ∫ NθT MΓ dΓ Eq. 5•190


Ω Ω Γ

– ∫ N ST N θ dΩ θ̂ – ∫ NST  --- N S dΩŜ + ∫ N ST ∇N w dΩŵ = 0


1
α 
Eq. 5•191
Ω Ω Ω

∫ ( ∇Nw )T NS dΩŜ = – ∫ Nw ∫ w Γ
T q dΩ + N T S dΓ Eq. 5•192
Ω Ω Γ

Eq. 5•190 to Eq. 5•192 can be re-written in matrix form as

Kb C T 0 θ̂ fθ
C H E T Sˆ = 0 Eq. 5•193
0 E 0 ŵ fw

Workbook of Applications in VectorSpace C++ Library 537


Chapter 5 Advanced Finite Element Methods
where

Kb = ∫ ( LNθ ) T DLNθ dΩ Eq. 5•194


C = – ∫ NST N θ dΩ Eq. 5•195


H = – ∫ N ST  --- N S dΩ
1
α 
Eq. 5•196

E = ∫ ( ∇Nw )T NS dΩ Eq. 5•197


fθ = ∫ NθT MΓ dΓ Eq. 5•198


Γ

fw = ∫ NwT q dΩ + ∫ NwT SΓ dΓ Eq. 5•199


Ω Γ

The condition for non-singular matrices parallel to that for the three-fields Hu-Washizu variational principle in
Eq. 5•63 is

n θ + n w ≥ n S , and n S ≥ n w Eq. 5•200

By inspecting Eq. 5•194 to Eq. 5•199, the first derivatives of θ and w exist. Therefore, C0-continuity need to be
satisfied for these two fields. S-field is taken as discontinuous, which has the potential advantage of being elim-
inated at the element level.1 The Program Listing 5•15 implements Heterosis element with θ-S-w mixed formu-
lation. The S-field is 4-node element by taking the four nodes at the 2 × 2 integration points. The results of the
mixed formulation is the same as the one with selective reduced integration on the shear constraint terms. In
plane elasticity, the equivalence theorem, in the previous chapter, was applied to (1) the selective reduced inte-
gration on the pressure constraint for the irreducible formulation, and (2) the pressure node taking on the Gauss
integration points for the mixed u-p formulation. Now, the equivalence theorem is applied to, the thick plate the-
ory, for (1) the θ-w irreducible formulation with the selective reduced integration on the shear constraint terms,
and (2) the θ-S-w mixed formulation where S-field is 4-node element with four nodes at the 2 × 2 integration
points. This equivalence theorem is illustrated in Figure 5•8.
The Program Listing 5•15 implement the mixed thick plate formulation (θ-S-w) for heterosis element. The
deflection is exactly the same as those obtained from the irreducible formulation.

1. p. 75 in Zienkiewicz, O.C. and R.L. Taylor, 1991, “The finite element method”, 4th ed., vol. 2. McGraw-Hill, Inc., UK.

538 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods

θ-w irrducible formulation θ-S-w mixed formulation

:w :θ : 2x2 reduced integration on :S


shear constrinats related terms

Figure 5•8 The equivalence of selective reduced integration of the shear constraint
terms of the irreducible formulation and mixed formulation with shear force nodes at
Gauss integration points. Both formulation use the heterosis element which uses
serendipity element for w and Lagrangian element for θ.

Workbook of Applications in VectorSpace C++ Library 539


Chapter 5 Advanced Finite Element Methods

#include "include\fe.h"
#include "include\omega_h_n.h"
Matrix_Representation_Couple::assembly_switch
Matrix_Representation_Couple::Assembly_Switch = Matrix_Representation_Couple::ALL;
Omega_h_i::Omega_h_i(int i) : Omega_h(0) { Heterosis element; see Hughes[1987]
Ωθ; 9-node Lagrangian element
if(i == 0) {
int row_segment_no = 2;
int count = 0; Node *node; double v[2], h = 1.0/((double)(row_segment_no));
for(int j = 0; j < row_segment_no; j++) {
v[1] = ((double)j)*h;
for(int k = 0; k < (2*row_segment_no+1); k++) {
v[0] = ((double)k)*h/2; node = new Node(count++, 2, v); the_node_array.add(node);
}
v[1] += h/2.0;
for(int k = 0; k < (2*row_segment_no+1); k++) {
v[0] = ((double)k)*h/2; node = new Node(count++, 2, v); the_node_array.add(node);
}
}
v[1] = 1.0;
for(int j = 0; j < (2*row_segment_no+1); j++) {
v[0] = ((double)j)*h/2.0; node = new Node(count++, 2, v); the_node_array.add(node);
}
int ena[9]; Omega_eh *elem; count = 0;
for(int j = 0; j < row_segment_no; j++) for(int k = 0; k < row_segment_no; k++) {
int row_node_no = 2*row_segment_no+1, first_node = j*2*row_node_no+k*2;
ena[0] = first_node; ena[1] = ena[0]+2; ena[2] = ena[1]+2*row_node_no;
ena[3] = ena[2]-2; ena[4]=ena[0]+1;ena[5]=ena[1]+row_node_no;
ena[6]=ena[2]-1;ena[7]=ena[0]+row_node_no; ena[8] = ena[7] +1;
elem = new Omega_eh(count++, 0, 0, 9, ena); the_omega_eh_array.add(elem);

ΩS; 2 × 2-Gauss-point-node element


}
} else if(i == 1) {
int row_segment_no = 2; Node *node;
double inv_sqrt3 = 1.0/sqrt(3.0), v[2], xl[4][2],
h = 1.0/((double)(row_segment_no)), zai, eta;
xl[0][0] = 0.0; xl[0][1] = 0.0; xl[1][0] = h; xl[1][1] = 0.0;
four nodal coordinates for element # 0
xl[2][0] = h; xl[2][1] = h; xl[3][0] = 0.0; xl[3][1] = h; node # 0 natural coordinates
zai = - inv_sqrt3; eta = - inv_sqrt3; node # 0 physcal coordinates
for(int j = 0; j < 2; j++)
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j]; make node and added to the database
node = new Node(0, 2, v); node_array().add(node); node # 1
zai = inv_sqrt3; eta = - inv_sqrt3;
for(int j = 0; j < 2; j++)
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(1, 2, v); node_array().add(node);
zai = inv_sqrt3; eta = inv_sqrt3;
node # 2
for(int j = 0; j < 2; j++)
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(2, 2, v); node_array().add(node);
zai = - inv_sqrt3; eta = inv_sqrt3; node # 3
for(int j = 0; j < 2; j++)
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(3, 2, v); node_array().add(node);
xl[0][0] = h; xl[0][1] = 0.0; xl[1][0] = 2.0*h; xl[1][1] = 0.0; element # 1
xl[2][0] = 2.0*h; xl[2][1] = h; xl[3][0] = h; xl[3][1] = h;

540 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
zai = - inv_sqrt3; eta = - inv_sqrt3; node # 4
for(int j = 0; j < 2; j++)
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(4, 2, v); node_array().add(node);
zai = inv_sqrt3; eta = - inv_sqrt3;
for(int j = 0; j < 2; j++)
node # 5
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(5, 2, v); node_array().add(node);
zai = inv_sqrt3; eta = inv_sqrt3;
node # 6
for(int j = 0; j < 2; j++)
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(6, 2, v); node_array().add(node);
zai = - inv_sqrt3; eta = inv_sqrt3; node # 7
for(int j = 0; j < 2; j++)
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(7, 2, v); node_array().add(node);
xl[0][0] = 0.0; xl[0][1] = h; xl[1][0] = h; xl[1][1] = h; element # 2
xl[2][0] = h; xl[2][1] = 2.0*h; xl[3][0] = 0.0; xl[3][1] = 2.0*h;
zai = - inv_sqrt3; eta = - inv_sqrt3;
node # 8
for(int j = 0; j < 2; j++)
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(8, 2, v); node_array().add(node);
zai = inv_sqrt3; eta = - inv_sqrt3; node # 9
for(int j = 0; j < 2; j++)
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(9, 2, v); node_array().add(node);
zai = inv_sqrt3; eta = inv_sqrt3; node # 10
for(int j = 0; j < 2; j++)
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(10, 2, v); node_array().add(node); node # 11
zai = - inv_sqrt3; eta = inv_sqrt3;
for(int j = 0; j < 2; j++)
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(11, 2, v); node_array().add(node);
xl[0][0] = h; xl[0][1] = h; xl[1][0] = 2.0*h; xl[1][1] = h;
element # 3
xl[2][0] = 2.0*h; xl[2][1] = 2.0*h; xl[3][0] = h; xl[3][1] = 2.0*h;
zai = - inv_sqrt3; eta = - inv_sqrt3; node # 12
for(int j = 0; j < 2; j++)
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(12, 2, v); node_array().add(node);
zai = inv_sqrt3; eta = - inv_sqrt3;
for(int j = 0; j < 2; j++)
node # 13
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(13, 2, v); node_array().add(node);
zai = inv_sqrt3; eta = inv_sqrt3;
node # 14
for(int j = 0; j < 2; j++)
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(14, 2, v); node_array().add(node);

Workbook of Applications in VectorSpace C++ Library 541


Chapter 5 Advanced Finite Element Methods
zai = - inv_sqrt3; eta = inv_sqrt3; node # 15
for(int j = 0; j < 2; j++)
v[j] = (1.0-zai)*(1.0-eta)/4.0*xl[0][j]+ (1.0+zai)*(1.0-eta)/4.0*xl[1][j]+
(1.0+zai)*(1.0+eta)/4.0*xl[2][j]+ (1.0-zai)*(1.0+eta)/4.0*xl[3][j];
node = new Node(15, 2, v); node_array().add(node);
for(int j = 0; j < 3; j++) { geometrical nodes; provide four corners
v[1] = h*(double)j;
for(int k = 0; k < 3; k++) { coordinates
int node_no = 16+j*3+k;
v[0] = h*(double)k;
node = new Node(node_no, 2, v);
node_array().add(node);
}
}
int ena[8]; Omega_eh *elem;
ena[0] = 0; ena[1] = 1; ena[2] = 2; ena[3] = 3;
ena[4] = 16; ena[5] = 17; ena[6] = 20; ena[7] = 19;
elem = new Omega_eh(0, 0, 0, 8, ena); the_omega_eh_array.add(elem);
ena[0] = 4; ena[1] = 5; ena[2] = 6; ena[3] = 7;
ena[4] = 17; ena[5] = 18; ena[6] = 21; ena[7] = 20;
elem = new Omega_eh(1, 0, 0, 8, ena); the_omega_eh_array.add(elem);
ena[0]=8;ena[1]=9;ena[2] = 10; ena[3] = 11;
ena[4] = 19; ena[5] = 20; ena[6] = 23; ena[7] = 22;
elem = new Omega_eh(2, 0, 0, 8, ena); the_omega_eh_array.add(elem);
ena[0]=12;ena[1]=13;ena[2]=14;ena[3]=15;
ena[4] = 20; ena[5] = 21; ena[6] = 24; ena[7] = 23;
elem = new Omega_eh(3, 0, 0, 8, ena); the_omega_eh_array.add(elem);
} else if(i == 2) { Ωw; 8-node serendipity element
int row_segment_no = 2, count = 0; Node *node;
double v[2], h = 1.0/((double)(row_segment_no));
for(int j = 0; j < row_segment_no; j++) {
v[1] = ((double)j)*h;
for(int k = 0; k < (2*row_segment_no+1); k++) {
v[0] = ((double)k)*h/2;
node = new Node(count++, 2, v); the_node_array.add(node);
}
v[1] += h/2.0;
for(int k = 0; k < row_segment_no+1; k++) {
v[0] = ((double)k)*h;
node = new Node(count++, 2, v); the_node_array.add(node);
}
}
v[1] = 1.0;
for(int j = 0; j < (2*row_segment_no+1); j++) {
v[0] = ((double)j)*h/2.0; node = new Node(count++, 2, v); the_node_array.add(node);
}
int ena[8]; Omega_eh *elem; count = 0;
for(int j = 0; j < row_segment_no; j++)
for(int k = 0; k < row_segment_no; k++) {
int first_node = j*(3*row_segment_no+2)+k*2;
ena[0]=first_node;ena[1]=ena[0]+2;
ena[2]=ena[1]+(3*row_segment_no+2); ena[3] = ena[2]-2;
ena[4] = ena[0]+1; ena[5] = (j+1)*(2*row_segment_no+1)+j*(row_segment_no+1)+k+1;
ena[6] = ena[3]+1; ena[7] = ena[5]-1;
elem = new Omega_eh(count++, 0, 0, 8, ena); the_omega_eh_array.add(elem);
}
}
}

542 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
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) {
θ=0
the_gh_array[node_order(4)](0) = the_gh_array[node_order(4)](1) = right
the_gh_array[node_order(9)](0) = the_gh_array[node_order(9)](1) =
the_gh_array[node_order(14)](0) = the_gh_array[node_order(14)](1) =
the_gh_array[node_order(19)](0) = the_gh_array[node_order(19)](1) =
the_gh_array[node_order(20)](0) = the_gh_array[node_order(20)](1) = top
the_gh_array[node_order(21)](0) = the_gh_array[node_order(21)](1) =
the_gh_array[node_order(22)](0) = the_gh_array[node_order(22)](1) =
the_gh_array[node_order(23)](0) = the_gh_array[node_order(23)](1) =
the_gh_array[node_order(24)](0) = the_gh_array[node_order(24)](1) =
the_gh_array[node_order(0)](1) = the_gh_array[node_order(1)](1) = bottom
the_gh_array[node_order(2)](1) = the_gh_array[node_order(3)](1) =
the_gh_array[node_order(0)](0) = the_gh_array[node_order(5)](0) =
the_gh_array[node_order(10)](0) = the_gh_array[node_order(15)](0) = left
gh_on_Gamma_h::Dirichlet;
} else if(i == 1) {
S-field disable all geometrical nodes
for(int j = 16; j <= 24; j++)
for(int k = 0; k < df; k++) {
the_gh_array[node_order(j)](k) = gh_on_Gamma_h::Dirichlet;
}
} else if(i == 2) {
the_gh_array[node_order(4)](0) = w=0
the_gh_array[node_order(7)](0) = right
the_gh_array[node_order(12)](0) =
the_gh_array[node_order(15)](0)=
the_gh_array[node_order(16)](0) =
the_gh_array[node_order(17)](0)= top
the_gh_array[node_order(18)](0) =
the_gh_array[node_order(19)](0)=
the_gh_array[node_order(20)](0) = gh_on_Gamma_h::Dirichlet;
}
}
static Global_Discretization *theta_type = new Global_Discretization;
static Global_Discretization *S_type = new Global_Discretization;
static Global_Discretization_Couple *S_theta_type = new Global_Discretization_Couple;
static Global_Discretization_Couple *w_S_type = new Global_Discretization_Couple;
class Plate_Heterosis : public Element_Formulation_Couple {
public:
Plate_Heterosis(Element_Type_Register a) : Element_Formulation_Couple(a) {}
Element_Formulation *make(int, Global_Discretization&);
Plate_Heterosis(int, Global_Discretization&);
Element_Formulation_Couple *make(int, Global_Discretization_Couple&);
Plate_Heterosis(int, Global_Discretization_Couple&);
};
Element_Formulation* Plate_Heterosis::make(int en, Global_Discretization& gd) {
return new Plate_Heterosis(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 },
1 ν 0
{D_*v_, D_, 0.0 }, Et 3 ν 1 0
D = -------------------------
{0.0, 0.0, D_*(1-v_)/2.0 } 12 ( 1 – ν 2 ) 1–ν
}; 0 0 ------------
C0 D = MATRIX("int, int, const double*", 3, 3, Dv[0]);
2
static const double mu_ = E_/(2*(1+v_)); static const double alpha_ = (5.0/6.0)*mu_*t_;

Workbook of Applications in VectorSpace C++ Library 543


Chapter 5 Advanced Finite Element Methods
Plate_Heterosis::Plate_Heterosis(int en, Global_Discretization& gd) :
Element_Formulation_Couple(en, gd) {

Kb matrix; θ-field
if(gd.type() == theta_type) {
Quadrature qp(2, 9);
H1 Z(2, (double*)0, qp), Zai, Eta, 3x3 Gauss integration for bending
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 9, 2, qp);
Zai &= Z[0]; Eta &= Z[1];
Lagrangian shape functions for θ dof
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());
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 ) & Kb = ∫ ( LNθ ) T DLNθ dΩ
(~Wy || ~Wx ); Ω
stiff &= (~B)*(D*B) | dV;
} else if(gd.type() == S_type) {
H matrix; S-field
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.0-Zai)*(1.0-Eta)/4.0; N[1] = (1.0+Zai)*(1.0-Eta)/4.0; bilinear corner node shape function for
N[2] = (1.0+Zai)*(1.0+Eta)/4.0; N[3] = (1.0-Zai)*(1.0+Eta)/4.0; coordinate transformation
C0 x = MATRIX("int, int, C0&, int, int", 4, 2, xl, 4, 0);
H1 X = N*x; J dv(d(X).det());
double sqrt3 = sqrt(3.0); C0 zero(0.0);
H0 n = INTEGRABLE_VECTOR("int, Quadrature", 4, qp), n0, n1, n2, n3, zai, eta;
zai &= ((H0)Z[0]); eta &= ((H0)Z[1]);
n[0] = (1.0-sqrt3*zai)*(1.0-sqrt3*eta)/4.0; n[1] = (1.0+sqrt3*zai)*(1.0-sqrt3*eta)/4.0;
n[2] = (1.0+sqrt3*zai)*(1.0+sqrt3*eta)/4.0; n[3] = (1.0-sqrt3*zai)*(1.0+sqrt3*eta)/4.0; S-shape functions:
n0 &= n[0]; n1 &= n[1]; n2 &= n[2]; n3 &= n[3]; bilinear four Gauss point nodes
H0 N_S = (( n0 | zero | n1 | zero | n2 | zero | n3 | zero ) &
(zero | n0 | zero | n1 | zero | n2 | zero | n3 ));
stiff &= MATRIX("int, int", 16, 16);
H = – ∫ N ST  --- N S dΩ
C0 stiff_sub = MATRIX("int, int, C0&, int, int", 8, 8, stiff, 0, 0);
1
stiff_sub = -( ((~N_S) * N_S) | dv)/alpha_; α 

}
}
Element_Formulation_Couple* Plate_Heterosis::make(
int en, Global_Discretization_Couple& gdc) { return new Plate_Heterosis(en,gdc); }
Plate_Heterosis::Plate_Heterosis(int en, Global_Discretization_Couple& gdc) :
Element_Formulation_Couple(en, gdc) {
if(gdc.type() == S_theta_type) { C matrix; S-θ couple
Quadrature qp(2, 9);
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]; bilinear corner node shape function for
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;
coordinate transformation
C0 x = MATRIX("int, int, C0&, int, int", 4, 2, xl, 4, 0);
H1 X = N*x;
J dv(d(X).det());

544 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
double sqrt3 = sqrt(3.0); C0 zero(0.0);
H0 n = INTEGRABLE_VECTOR("int, Quadrature", 4, qp), n0, n1, n2, n3, zai, eta;
zai &= ((H0)Z[0]); eta &= ((H0)Z[1]);
n[0] = (1.0-sqrt3*zai)*(1.0-sqrt3*eta)/4.0; n[1] = (1.0+sqrt3*zai)*(1.0-sqrt3*eta)/4.0; S-shape functions:
n[2] = (1.0+sqrt3*zai)*(1.0+sqrt3*eta)/4.0; n[3] = (1.0-sqrt3*zai)*(1.0+sqrt3*eta)/4.0; bilinear four Gauss point nodes
n0 &= n[0]; n1 &= n[1]; n2 &= n[2]; n3 &= n[3];
H0 N_S = (( n0 | zero | n1 | zero | n2 | zero | n3 | zero ) &
(zero | n0 | zero | n1 | zero | n2 | zero | n3 )); NS
H0 nt = INTEGRABLE_VECTOR("int, Quadrature", 9, qp),

θ-shape functions:
nt0, nt1, nt2, nt3, nt4, nt5, nt6, nt7, nt8;
nt[0] = (1-zai)*(1-eta)/4; nt[1] = (1+zai)*(1-eta)/4;
nt[2] = (1+zai)*(1+eta)/4; nt[3] = (1-zai)*(1+eta)/4; Lagrangian shape function
nt[8] = (1-zai.pow(2))*(1-eta.pow(2));
nt[0] -= nt[8]/4; nt[1] -= nt[8]/4; nt[2] -= nt[8]/4; nt[3] -= nt[8]/4;
nt[4] = ((1-zai.pow(2))*(1-eta)-nt[8])/2; nt[5] = ((1-eta.pow(2))*(1+zai)-nt[8])/2;
nt[6] = ((1-zai.pow(2))*(1+eta)-nt[8])/2; nt[7] = ((1-eta.pow(2))*(1-zai)-nt[8])/2;
nt[0] -= (nt[4]+nt[7])/2; nt[1] -= (nt[4]+nt[5])/2;
nt[2] -= (nt[5]+nt[6])/2; nt[3] -= (nt[6]+nt[7])/2;
nt0 &= nt[0]; nt1 &= nt[1]; nt2 &= nt[2]; nt3 &= nt[3]; nt4 &= nt[4];
nt5 &= nt[5]; nt6 &= nt[6]; nt7 &= nt[7]; nt8 &= nt[8];
H0 N_theta=((nt0|zero|nt1|zero|nt2|zero|nt3|zero|nt4|zero|nt5|zero|nt6|zero|nt7|zero|nt8|zero)& Nθ
(zero|nt0|zero|nt1|zero|nt2|zero|nt3|zero|nt4|zero|nt5|zero|nt6|zero|nt7|zero|nt8));
stiff &= MATRIX("int, int", 16, 18);
C0 stiff_sub = MATRIX("int, int, C0&, int, int", 8, 18, stiff, 0, 0); C = – ∫ N ST N θ dΩ
stiff_sub = -( ((~N_S) * N_theta) | dv);

} else if(gdc.type() == w_S_type) {
Quadrature qp(2, 9);
H1 Z(2, (double*)0, qp), , Zai, Eta E matrix; w-S couple
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 8, 2, qp); w shape function:
Zai &= Z[0]; Eta &= Z[1];
N[0] = (1-Zai)*(1-Eta)/4; N[1] = (1+Zai)*(1-Eta)/4; serendipity shape functions
N[2] = (1+Zai)*(1+Eta)/4; N[3] = (1-Zai)*(1+Eta)/4;
N[4] = (1-Zai.pow(2))*(1-Eta)/2; N[5] = (1-Eta.pow(2))*(1+Zai)/2;
N[6] = (1-Zai.pow(2))*(1+Eta)/2; N[7] = (1-Eta.pow(2))*(1-Zai)/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());
H0 W_x = INTEGRABLE_SUBMATRIX("int, int, H0&", 1, 2/*nsd*/, Nx), Wx, Wy, grad_W;
Wx &= W_x[0][0]; Wy &= W_x[0][1];
grad_W &= (~Wx) & ∇w
(~Wy);
double sqrt3 = sqrt(3.0);
C0 zero(0.0);
H0 n = INTEGRABLE_VECTOR("int, Quadrature", 4, qp), n0, n1, n2, n3, zai, eta;
zai &= ((H0)Z)[0]; eta &= ((H0)Z)[1]; S-shape functions:
n[0] = (1.0-sqrt3*zai)*(1.0-sqrt3*eta)/4.0; n[1] = (1.0+sqrt3*zai)*(1.0-sqrt3*eta)/4.0; bilinear four Gauss point nodes
n[2] = (1.0+sqrt3*zai)*(1.0+sqrt3*eta)/4.0; n[3] = (1.0-sqrt3*zai)*(1.0+sqrt3*eta)/4.0;
n0 &= ((H0)n)[0]; n1 &= ((H0)n)[1]; n2 &= ((H0)n)[2]; n3 &= ((H0)n)[3];
H0 n_S = (n0|zero|n1|zero|n2|zero|n3|zero) & NS
(zero|n0|zero|n1|zero|n2|zero|n3);
stiff &=MATRIX("int, int", 8, 16);
C0 stiff_sub=MATRIX("int, int, C0&, int, int", 8, 8, stiff, 0, 0); E = ∫ ( ∇Nw )T NS dΩ
stiff_sub = ( (~grad_W) * n_S) | dv; Ω
double f_0 = 1.0;
force &= (((H0)N)*f_0) | dv;
} fw = ∫ NwT q dΩ
} Ω
Element_Formulation* Element_Formulation::type_list = 0;
static Element_Type_Register element_type_register_instance;
static Plate_Heterosis plate_heterosis_instance(element_type_register_instance);

Workbook of Applications in VectorSpace C++ Library 545


Chapter 5 Advanced Finite Element Methods
int main() {

θ
const int theta_ndf = 2;
Omega_h_i oh_theta(0);
gh_on_Gamma_h_i theta_gh(0, theta_ndf, oh_theta);
U_h theta_h(theta_ndf, oh_theta);
Global_Discretization theta_gd(oh_theta, theta_gh, theta_h, theta_type);
const int S_ndf = 2; S
Omega_h_i oh_S(1);
gh_on_Gamma_h_i S_gh(1, S_ndf, oh_S);
U_h S_h(S_ndf, oh_S);
Global_Discretization S_gd(oh_S, S_gh, S_h, S_type);
const int w_ndf = 1; w
Omega_h_i oh_w(2);
gh_on_Gamma_h_i w_gh(2, w_ndf, oh_w);
U_h w_h(w_ndf, oh_w);
Global_Discretization w_gd(oh_w, w_gh, w_h); S-θ
Global_Discretization_Couple gdc_S_theta(S_gd, theta_gd, S_theta_type);
w−S
Global_Discretization_Couple gdc_w_S(w_gd, S_gd, w_S_type);
Matrix_Representation mr_theta(theta_gd); Matrix_Representation mr_S(S_gd);
Matrix_Representation_Couple mrcE(gdc_w_S,0, 0,&(mr_S.rhs()),&mr_S);
Matrix_Representation_Couple mrcC(gdc_S_theta,0,
&(mr_S.rhs()),&(mr_theta.rhs()), &mr_theta);
mr_theta.assembly();
mr_S.assembly();
mrcC.assembly();
Kb ,
mrcE.assembly();
C0 K = ((C0)(mr_theta.lhs())), H,
H = ((C0)(mr_S.lhs())), C,
C = ((C0)(mrcC.lhs())),

f_theta = ((C0)(mr_theta.rhs())),
E = ((C0)(mrcE.lhs())), E
f_S = ((C0)(mr_S.rhs())), fS
f_w = ((C0)(mrcE.rhs()));
fw
Cholesky dK(K);
C0 K_inv = dK.inverse(),
CK_inv = C*K_inv,
A = H-CK_inv*(~C);
Cholesky dnA(-A);
C0 A_inv = -(dnA.inverse()),
EA_inv = E*A_inv, ŵ =
EA_invEt = EA_inv*(~E);
(EA-1ET)-1(EA-1fS - EA-1CKb-1fθ−fw)
Cholesky dnEA_invEt(-EA_invEt); ˆ
C0 w = -(dnEA_invEt*( E*-(dnA*f_S) - E*-(dnA*(C*(dK*f_theta))) -f_w) ), S = A-1(fS−ET ŵ −CKb-1fθ)
S = -(dnA*(f_S-(~E)*w-C*(dK*f_theta))),
theta = dK*(f_theta-(~C)*S);
theta_h = theta;
θ̂ = Kb-1(fθ-CT Sˆ )
theta_h = theta_gd.gh_on_gamma_h();
cout << "rotation (theta_x, theta_y):" << endl;
for(int i = 0; i < theta_h.total_node_no(); i++) cout << theta_h[i] << endl;
S_h = S;
S_h = S_gd.gh_on_gamma_h();
cout <<"shear force S:" << endl << S_h << endl;
w_h = w;
w_h = w_gd.gh_on_gamma_h();
cout << "deflection w:" << endl << w_h << endl;
return 0; }

Listing 5•15 Heterosis element for θ-S-w mixed thick plate formulation (project: “mixed_thick_plate” in
project workspace file “fe.dsw”).

546 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
Collocation of Shear Constraints on Reissner-Mindlin Plate
The so-called discrete Reissner-Mindlin method is to use the collocation method to enforce the shear con-
straints on the element boundaries. We consider a simple modification from the θ-w irreducible thick plate for-
mulation to enforce the shear constraints. The shear force on the boundaries of an element is, from Eq. 5•184,

S = α [ ∇w – θ ] Eq. 5•201

For a rectangular plate with edges parallel to x and y axis (Figure 5•9a), these constraints, on the mid-side nodes
of an element boundaries, can be easily computed from bilinear four-node shape functions for both θ and w as 1

w 1 – w 0 θx + θ x
0 1
ˆ4
S x = α ------------------- – ------------------
a 2

w2 – w 1 θy + θy
1 2
ˆ5
S y = α ------------------- – ------------------
b 2

w2 – w 3 θx + θx
2 3
ˆ6
S x = α ------------------- – ------------------
a 2

w 3 – w0 θy + θy
3 0
ˆ7
S y = α ------------------- – ------------------ Eq. 5•202
b 2

Eq. 5•202 can be written in matrix form

3 6
2
NS
x
4 = (1−η)/2

b 7 5
NS 5 = (1+ξ)/2
y

1
0 4 NS 6 = (1+η)/2
x
a

: θ and w : Sx : Sy
NS
y
7 = (1−ξ)/2
(a)
(b)
Figure 5•9 Discrete Reissner-Mindlin method for rectangular element with bilinear four-node
shape functions for both θ and w, and four shear constraints enforced on the element boundaries.

1. p.87-88 in Zienkiewicz, O.C., and R.L. Taylor, 1991, “ The finite element method: solid and fluid mechanics, dynamics,
and non-linearity”, 4-th eds., McGraw-Hill Inc., UK.

Workbook of Applications in VectorSpace C++ Library 547


Chapter 5 Advanced Finite Element Methods

S = α [ Q w ŵ – Q θ θ̂ ]
ˆ
Eq. 5•203

where

–1 1 –1 –1
------ --- 0 0 ------ 0 ------ 0 0 0 0 0
a a 2 2
1 –1 –1 –1
0 --- ------ 0 0 0 0 ------ 0 ------ 0 0
b b 2 2
Qw = , Qθ = Eq. 5•204
1 –1 –1 –1
0 0 --- ------ 0 0 0 0 ------ 0 ------ 0
a a 2 2
1 –1 –1 –1
--- 0 0 ------ 0 ------ 0 0 0 0 0 ------
b b 2 2

S =NS Sˆ with NS shape functions for these four mid-side nodes are shown in Figure 5•9(b). The matrix form of
the problem becomes

T
K ww K wθ ŵ fw
= Eq. 5•205
K wθ K θθ θ̂ fθ

where

K ww = ∫ ( NS Qw ) T αNS Qw dΩ Eq. 5•206


K wθ = ∫ ( NS Qw )T αNS Qθ dΩ Eq. 5•207


K θθ = ∫ [ ( LNθ ) T DLNθ + ( NS Qθ )T αNS Qθ ] dΩ Eq. 5•208


fw and fθ are the same as those defined in the irreducible formulation. The Program Listing 5•16 implements the
discrete Reissner-Mindlin formulation in the above. The center deflection for this bilinear four-node element
with discrete shear constraints is 325634, which is greater than the analytical solution for the thin-plate theory of
226800.

548 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods

#include "include\fe.h"
#include "include\omega_h_n.h"
Matrix_Representation_Couple::assembly_switch
Matrix_Representation_Couple::Assembly_Switch = Matrix_Representation_Couple::ALL;
EP::element_pattern EP::ep = EP::QUADRILATERALS_4_NODES; Ωθ & Ωw; bilinear 4-node element
static int row_node_no = 9; static int col_node_no = row_node_no;
Omega_h_i::Omega_h_i(int i) : Omega_h(0) {
if(i == 0 || i == 1) {
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_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);
boundary conditions
if(i == 0) {
for(int j = 0; j < row_node_no; j++) {
for(int k = 0; k < df; k++) {
the_gh_array[node_order((j+1)*row_node_no-1)](k) =
the_gh_array[node_order(row_node_no*(row_node_no-1)+j)](k) =
gh_on_Gamma_h::Dirichlet;
}
the_gh_array[node_order(j)](1) = gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(j*row_node_no)](0) = gh_on_Gamma_h::Dirichlet;
}
} else if(i == 1) {
for(int j = 0; j < 9; j++) {
the_gh_array[node_order((j+1)*row_node_no-1)](0) =
the_gh_array[node_order(row_node_no*(row_node_no-1)+j)](0) =
gh_on_Gamma_h::Dirichlet;
}
}
}
static Global_Discretization *w_type = new Global_Discretization;
static Global_Discretization *theta_type = new Global_Discretization;
class Plate_Discrete_Reissner_Mindlin : public Element_Formulation_Couple {
public:
Plate_Discrete_Reissner_Mindlin(Element_Type_Register a) :
Element_Formulation_Couple(a) {}
Element_Formulation *make(int, Global_Discretization&);
Plate_Discrete_Reissner_Mindlin(int, Global_Discretization&);
Element_Formulation_Couple *make(int, Global_Discretization_Couple&);
Plate_Discrete_Reissner_Mindlin(int, Global_Discretization_Couple&);
};
Element_Formulation* Plate_Discrete_Reissner_Mindlin::make( int en,
Global_Discretization& gd) { return new Plate_Discrete_Reissner_Mindlin(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] = { 1 ν 0
{D_, D_*v_, 0.0 }, Et 3 ν 1 0
{D_*v_, D_, 0.0 }, D = -------------------------
{0.0, 0.0, D_*(1-v_)/2.0} 12 ( 1 – ν 2 ) 1–ν
0 0 ------------
}; 2
C0 D = MATRIX("int, int, const double*", 3, 3, Dv[0]);
static const double mu_ = E_/(2*(1+v_));
static const double alpha_ = (5.0/6.0)*mu_*t_;

Workbook of Applications in VectorSpace C++ Library 549


Chapter 5 Advanced Finite Element Methods
Plate_Discrete_Reissner_Mindlin::Plate_Discrete_Reissner_Mindlin(int en,
Global_Discretization& gd) : Element_Formulation_Couple(en, gd) {
if(gd.type() == theta_type) { Kθθ matrix;
Quadrature qp(2, 4);
H1 Z(2, (double*)0, qp), Zai, Eta, θ-shape functions:
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE(
"int, int, Quadrature", 4, 2, qp); Nθ
Zai &= Z[0]; Eta &= Z[1];
N[0] = (1-Zai)*(1-Eta)/4; N[1] = (1+Zai)*(1-Eta)/4; bilinear four-node shape function
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, B;
Wx &= W_x[0][0]; Wy &= W_x[0][1];
B &= (~Wx || C0(0.0)) & (C0(0.0) || ~Wy ) & (~Wy || ~Wx );
H0 n = INTEGRABLE_VECTOR("int, Quadrature", 4, qp), zai, eta;
zai = ((H0)Z)[0]; eta &= ((H0)Z)[1]; S-shape functions:
n[0] = (1-eta)/2.0; n[1] = (1+zai)/2.0; n[2] = (1+eta)/2.0; n[3] = (1-zai)/2.0; NS
C0 half(0.5), zero(0.0);
C0 Q_theta = ( -half | zero | -half | zero | zero | zero | zero | zero ) & Qθ
( zero | zero | zero | -half | zero | -half | zero | zero ) &
( zero | zero | zero | zero | -half | zero | -half | zero ) & K θθ = ∫ ( ( LN θ ) T DLN θ ) dΩ +
( zero | -half | zero | zero | zero | zero | zero | -half );
stiff &= ((~B)*(D*B)+(((~Q_theta)*n)*((~n)*Q_theta))/alpha_) | dv; Ω
} else if (gd.type() == w_type) {
Quadrature qp(2, 4);
H1 Z(2, (double*)0, qp), Zai, Eta,
∫ ( NS Qθ ) T αNS Qθ dΩ

N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE(
"int, int, Quadrature", 4, 2, qp); Kww matrix
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; J dv(d(X).det());
H0 n = INTEGRABLE_VECTOR("int, Quadrature", 4, qp), zai, eta; S-shape functions:
zai = ((H0)Z)[0]; eta &= ((H0)Z)[1];
n[0] = (1-eta)/2.0; n[1] = (1+zai)/2.0; n[2] = (1+eta)/2.0; n[3] = (1-zai)/2.0; NS
C0 a_inv(1.0/norm(xl[1]-xl[0])), b_inv(1.0/norm(xl[2]-xl[1])), zero(0.0); Qw
C0 Q_w = ( -a_inv | a_inv | zero | zero ) &
( zero | b_inv | -b_inv | zero ) &
( zero | zero | a_inv | -a_inv ) &
( b_inv | zero | zero | -b_inv );

}
stiff &= ( ( ((~Q_w)*n)*((~n)*Q_w) ) | dv )/alpha_; K ww = ∫ ( NS Qw )T αNS Qw dΩ
} Ω
Element_Formulation_Couple* Plate_Discrete_Reissner_Mindlin::make(int en,
Global_Discretization_Couple& gdc) {
return new Plate_Discrete_Reissner_Mindlin(en,gdc);
}
Plate_Discrete_Reissner_Mindlin::Plate_Discrete_Reissner_Mindlin(int en,
Global_Discretization_Couple& gdc) : Element_Formulation_Couple(en, gdc) {
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.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; J dv(d(X).det()); S-shape functions:
H0 n = INTEGRABLE_VECTOR("int, Quadrature", 4, qp), zai, eta; NS
zai = ((H0)Z)[0]; eta &= ((H0)Z)[1];
n[0] = (1-eta)/2.0; n[1] = (1+zai)/2.0; n[2] = (1+eta)/2.0; n[3] = (1-zai)/2.0;

550 Workbook of Applications in VectorSpace C++ Library


Mixed and Hybrid Finite Element Methods
C0 a_inv(1.0/norm(xl[1]-xl[0])), b_inv(1.0/norm(xl[2]-xl[1])), half(0.5), zero(0.0);
C0 Q_w = ( -a_inv | a_inv | zero | zero ) &
( zero | b_inv | -b_inv | zero ) & Qw
( zero | zero | a_inv | -a_inv ) &
( b_inv | zero | zero | -b_inv ); Qθ
C0 Q_theta = ( -half | zero | -half | zero | zero | zero | zero | zero ) &
( zero | zero | zero | -half | zero | -half | zero | zero ) & K wθ = ∫ ( NS Qw )T αNS Qθ dΩ
( zero | zero | zero | zero | -half | zero | -half | zero ) & Ω
( zero | -half | zero | zero | zero | zero | zero | -half );
stiff &= ( ( ((~Q_w)*n)*((~n)*Q_theta) ) | dv )/alpha_;
double f_0 = 1.0; fw = ∫ NwT q dΩ
force &= (((H0)N)*f_0) | dv; Ω
}
Element_Formulation* Element_Formulation::type_list = 0;
static Element_Type_Register element_type_register_instance;
static Plate_Discrete_Reissner_Mindlin
plate_discrete_reissner_mindlin_instance(element_type_register_instance);
int main() {
const int theta_ndf = 2;
Omega_h_i oh_theta(0);
gh_on_Gamma_h_i theta_gh(0, theta_ndf, oh_theta);
U_h theta_h(theta_ndf, oh_theta);
Global_Discretization theta_gd(oh_theta, theta_gh, theta_h, theta_type);
const int w_ndf = 1;
Omega_h_i oh_w(1);
gh_on_Gamma_h_i w_gh(1, w_ndf, oh_w);
U_h w_h(w_ndf, oh_w);
Global_Discretization w_gd(oh_w, w_gh, w_h, w_type);
Global_Discretization_Couple gdc(w_gd, theta_gd);
Matrix_Representation mr_theta(theta_gd);
Matrix_Representation mr_w(w_gd);
Matrix_Representation_Couple mrc(gdc, 0, &(mr_w.rhs()), &(mr_theta.rhs()), &mr_theta);
mr_w.assembly();
mr_theta.assembly();
mrc.assembly(); Kθθ
C0 Ktt = ((C0)(mr_theta.lhs())), Kww
Kww = ((C0)(mr_w.lhs())), Kwθ
Kwt = ((C0)(mrc.lhs())),
f_theta = ((C0)(mr_theta.rhs())), fθ
f_w = ((C0)(mr_w.rhs())); fw
Cholesky dKww(Kww);
C0 Kww_inv = dKww.inverse(),
KwttKww_invKwt = (~Kwt)*(Kww_inv*Kwt),
K = Ktt-KwttKww_invKwt,
f = f_theta-(~Kwt)*(dKww*f_w);
Cholesky dK(K);
θ̂ = [Kθθ−(Kwθ)T(Kww)-1Kwθ] -1
C0 theta = dK*f; [fθ−(Kwθ)T(Kww)-1fw ]
theta_h = theta; ŵ = (Kww) [fw-(Kwθ)Tθ̂ ]
-1
theta_h = theta_gd.gh_on_gamma_h();
cout << "rotation:" << endl << theta_h << endl;
C0 w = dKww*(f_w-Kwt*theta);
w_h = w;
w_h = w_gd.gh_on_gamma_h();
cout << "deflection:" << endl << w_h << endl;
return 0;
}

Listing 5•16 Discrete Reissner-Mindlin plate formulation (project: “discrete_reissner_mindlin” in project


workspace file “fe.dsw”).

Workbook of Applications in VectorSpace C++ Library 551


Chapter 5 Advanced Finite Element Methods
5.2 Contact Mechanics
With the development in the hybrid irreducible subdomain formulation in Section 5.1.3, we are most ready to
tackle the contact problem. The contact problem in this section is assumed to be frictionless. Friction makes the
contact problem becomes a material nonlinear one. We begin the nonlinear problem on the subject of elastoplas-
ticity in Section 5.3. We do not include the frictional case in this workbook, because the best understanding on
the treatment of the “stick-slip” friction on the contact surface is considered as a degeneration, 1 dimensionless,
from that of the elastoplastic behavior on a continuum.

5.2.1 Basic Physics


The stress tensor σ0 at the point x0 on subdomain Ω0 is related to the traction t0 according to Cauchy’s for-
mula1 (see Figure 5•4)

t0 = n σ0 Eq. 5•209

where n is the outward unit surface normal at the point x0 on subdomain Ω0. Since the outward unit surface nor-
mal at the point x1 on subdomain Ω1 is opposite in direction to n. We also have

t1 = (- n) σ1 Eq. 5•210

Because we assumed the contact is frictionless, only the normal component of the tractions, t 0 • n = – t 1 • n ≡ λ ,
on the contact surface are required to be in equilibrium according to Newton’s third law of motion. The contact
conditions on contact surface Γc is2

0
g ≡ [ u1 – u 0 ] • n + g = 0 ⇒ t0 • n = – t1 • n < 0
0
g ≡ [ u1 – u 0 ] • n + g > 0 ⇒ t 0 • n = –t 1 • n = 0 Eq. 5•211

where “g” is the “gap” between the two bodies, and “g0” is the initial gap. In constraint optimization (see Eq.
2•14 in Chapter 2), Eq. 5•211 can be concisely written as the Kuhn-Tucker condition

g λ = 0, λ ≤ 0, g ≥ 0 Eq. 5•212

We note that λ, the normal component of the tractions, also plays the role of the Lagrange multipliers in the con-
text of constraint optimization problem. The geometry of contact element is shown in Figure 5•10. The node
number “8”, with coordinates x11, is projected to its opposite boundary on x0. The segment length proportional
parameter α is computed from

1. see p. 63 in Fung Y.C., 1965, “Foundations of solid mechanics”, Prentice-Hall, Inc., Englewood Cliffs, New Jersey.
2. Simo, J.C. Wriggers, P., and R.L.Taylor, 1985, “A perturbed Lagrangian formulation for the finite element solution of
contact problem”, Computer Methods in Applied Mechanics and Engineering, vol. 50, p. 163-180, Elsevier Science Publish-
ers, North-Holland.

552 Workbook of Applications in VectorSpace C++ Library


Contact Mechanics

9x1
nodes
6 Ω1 x11 1

projected x10
7
nodes
n0 8
y2
contact element n1
1 numbers 0 5
1 2 4 y1
3 4x 1
unit surface 3 0
normal 1 x00 x00 5
2
Ωi domains 0
Ω0 αl
l = ||x01-x00||
Figure 5•10 Contact element geometry. Six contact line elements are formed
through projection of real nodes onto the boundary of the opposite boundaries.

( x 10 – x 00 ) • ( x 01 – x 00 )
α = -------------------------------------------------- Eq. 5•213
x 01 – x 00

and the coordinates of the projected point x00, and displacement on the projected node u0 can be linearly interpo-
lated from the level rule as

0
x 0 = ( 1 – α )x 00 + αx 01, and u 0 = ( 1 – α )u 00 + αu 01 Eq. 5•214

For the contact element number “5” in Figure 5•10 we recognize that, on Ω0, the node numbers “3”, “4”, in glo-
bal node numbering, are involved in the element. The contact element node on Ω0 associated with the current
element number “5” has the coordinates of {x00, x01}. The displacement associated with these two nodes is
denoted as u = [u00, u01]T which is related to the displacement of “real” nodes (global node numbers “3” and “4”,
0 1
and redefined as local element node number “0” and “1”) û = [ û 0 , û0 ]T by the same level rule as

0 0
u0 ( 1 – α 0 )n 0 α0 n0 û 0
u= = = cû Eq. 5•215
u0
1 α1 n1 ( 1 – α 1 )n 1 û 1
0

where α0 for node number “3” is computed from Eq. 5•213, and α1= 0 (consistent with Eq. 5•213) for node num-
ber “4” which is not a projected node. The initial gap, g0, and the tangent vector, s, along boundary Γ34 are

0 ( x 01 – x 00 )
g = x 10 – x 0 , s = ---------------------
- Eq. 5•216
x 01 – x 00

The outward unit surface normal vector, n0, is defined from rotating the tangent vector 90o counter-clockwise

Workbook of Applications in VectorSpace C++ Library 553


Chapter 5 Advanced Finite Element Methods

π π
cos --- sin --- s
2 2 x
n0 = Rs = = [sy, -sx]T Eq. 5•217
π π sy
– sin --- cos ---
2 2
The Lagrangian functional to the contact problem is (with domain index i = 0, 1)

( u 0, u 1, λ ) = --- ∫ ( Lu i ) T D Lu i dΩ – ∫ uiT b dΩ – ∫ uiT h i dΓ + ∫ λT { [ u2 – u1 ] • n + g


1 0
} dΓ Eq. 5•218
2
Ωi Ωi Γh Γc
i

The Euler-Lagrange equations are obtained by taking the directional derivatives of Lagrangian functional with
respect to ui and λ, then, set to zero.

∫ δuiT L T ( D Lui ) dΩ – ∫ δuiT b dΩ – ∫ δuiT hi dΓ + ∫ { [ δu2T – δu1T ] • n } λ dΓ = 0 Eq. 5•219


Ωi Ωi Γi Γc

∫ δ λT { [ u2 – u1 ] • n + g 0 } dΓ = 0 Eq. 5•220
Γc

The first three terms in Eq. 5•219 are the same as the standard irreducible formulation. The last term in Eq.
5•219 and Eq. 5•220 are needed for the contact formulation. The Lagrangian multiplier and the displacement (on
the contact elements) are approximated as linear line element from their nodal values (with “hat”)

λ = Nλa λ̂ a, u = N a u a = N a cû a = N ua û a Eq. 5•221

where N ua ≡ N a c ; i.e., the projected displacement u of contact nodes is related to u of global nodes by Eq. 5•215.
The off-diagonal element stiffness submatrices in the mixed formulation of Eq. 5•219 and Eq. 5•220 are

Q = ∫ NλT Nu dΓ Eq. 5•222


Γc

Eq. 5•222 adds nothing new from mixed formulation point of view. What is new to the program is we need to
feed the information, at the element level, on the corresponding λ and u on domain Ω0 and Ω1 that are associ-
ated with the particular contact segment.

5.2.2 Frictionless Rigid Punch On Ealstic Foundation


To bypass, for the moment, the difficulty in dealing with the active set method (page 118 in Chapter 2) to
implement the Kuhn-Tucker condition in Eq. 5•212 by inspecting an example with a rigid punch on an elastic
foundation. In this example, The contact geometry does not change significantly during deformation. The con-
tact geometry (element-node connection, projection parameters α) is defined manually at the beginning. In the
next section, the contact searching algorithm is to be implemented, which automatically re-defines the changing
contact geometry at each step.

554 Workbook of Applications in VectorSpace C++ Library


Contact Mechanics
The present example is illustrated in Figure 5•11a. The contact elements of this problem are illustrated in
details in Figure 5•11b. The two domains Ωrigid punchand Ωelastic foundation are defined as they are defined in stan-
dard irreducible formulation. In addition, from Figure 5•11b, we need to define Γrigid punch , and Γelastic foundation
which are the potential contact boundaries on Ωrigid punch and Ωelastic foundation, respectively. The contact ele-
ments per se are lied on an imaginary surface Γc in the middle of Γrigid punch and Γelastic foundation. The off-diago-
nal element stiffness submatrix C from Eq. 5•222 are contributed by the degree of freedoms (λ-ui) from the
Global_Discretization_Couple consists of Γc-Γi.
Γi are boundaries which consist of line elements which share some common variables with the boundary of
Ωi. Γi can be defined similar to regular domains Ωi without instantiating variables of their own. Lines 2-13 is to
instantiate objects for the elastic foundation (the lower part).

1 int ndf = 2;
2 Omega_h_i elastic_foundation_oh(0); // elastic foundation; Ω elastic foundation
3 gh_on_Gamma_h_i elastic_foundation_gh(0, ndf, elastic_foundation_oh);
4 U_h u_0(ndf, elastic_foundation_oh); // uelastic foundation
5 Global_Discretization *elastic_foundation_type = new Global_Discretization();
6 Global_Discretization
7 elastic_foundation_gd(elastic_foundation_oh, elastic_foundation_gh, u_0,
8 elastic_foundation_type);
9 Gamma_h_i // potential contact segment; Γelastic founcation
10 elastic_foundation_gamma_h(0, elastic_foundation_oh);
11 Global_Discretization_Gamma_h_i
12 elastic_foundation_gamma_h_gd(0, elastic_foundation_gd, elastic_foundation_type,
13 elastic_foundation_gamma_h);

where line 9 defines the potential contact surface for the elastic foundation with a new class Gamma_h_i for Γi .
The Gamma_h_i class does not have variables of its own. The Global_Discretization of this class,
Global_Discretization_Gamma_h_i, uses the variable u_0 instantiated for the Ωi. The following lines define the
rigid punch (the upper part) in a similar manner.

1 Omega_h_i rigid_punch_oh(1); // Ω rigid punch


2 gh_on_Gamma_h_i rigid_punch_gh(1, ndf, rigid_punch_oh);
3 U_h u_1(ndf, rigid_punch_oh); // urigid punch
4 Global_Discretization *rigid_punch_type = new Global_Discretization();
5 Global_Discretization
6 rigid_punch_gd(rigid_punch_oh, rigid_punch_gh, u_1, rigid_punch_type);
7 Gamma_h_i // potentail contact segment; Γrigid punch
8 rigid_punch_gamma_h(1, rigid_punch_oh);
9 Global_Discretization_Gamma_h_i
10 rigid_punch_gamma_h_gd(1, rigid_punch_gd, rigid_punch_type, rigid_punch_gamma_h);
After domains Ωi and their potential contact surfaces Γi have been defined, we define the contact surface, Γc,
itself with the variables λ associated with it to form its Global_Discretization object.

Workbook of Applications in VectorSpace C++ Library 555


Chapter 5 Advanced Finite Element Methods

B.C.
rigid punch problem v = -2
(a)
8 9 10 11 E = 1.e10
(B.C for the next section ν = 0.5
on rigid sled problem 4 5 6 7
4 u = 0.2 t)
0 1 2 3
14 15 16 17 18 19 20

7 8 9 10 11 12 13

20

E = 1.e5
ν = 0.5
0 1 2 3 4 5 6

60

(b) Ωrigid punch Γrigid puch


0 0 1 1 2 2 3
Γc
0 1 2 3 4 5 6
g0
0 1 2 3 4 5

15 0 16 1 17 2 18 3 19 4
Ωelastic foundation
Γelastic foundation

Figure 5•11 Frictionless contact problem with rigid punch (or sled) on top an elastic foundation.

556 Workbook of Applications in VectorSpace C++ Library


Contact Mechanics
1 Omega_h_i interface_oh(2); // Γc in between Γelastic founcation and Γrigid punch
2 gh_on_Gamma_h_i interface_gh(2, ndf, interface_oh); // get initialized
3 U_h lambda(ndf, interface_oh); // λ
4 Global_Discretization interface_gd(interface_oh, interface_gh, lambda);
The Global_Discretization_Couple consists of two pairs of boundaries, {Γc-Γelastic founcation } and {Γc-Γrigid
punch}, are defined as

1 Global_Discretization_Couple *interface_elastic_foundation_gamma_h_type
2 = new Global_Discretization_Couple();
3 Global_Discretization_Couple // {Γc-Γelastic founcation}
4 interface_elastic_foundation_gdc(interface_gd, elastic_foundation_gamma_h_gd,
5 interface_elastic_foundation_gamma_h_type);
6 Global_Discretization_Couple *interface_rigid_punch_gamma_h_type
7 = new Global_Discretization_Couple();
8 Global_Discretization_Couple // {Γc-Γrigid punch}
9 interface_rigid_punch_gdc(interface_gd, rigid_punch_gamma_h_gd,
10 interface_rigid_punch_gamma_h_type);
The contact element domain (Ωeh)c is defined as a new C++ class “Contact_Omega_eh” derived from class of
element domain Ωeh as

1 class Contact_Omega_eh : public Omega_eh {


2 int the_associate_element[2];
3 double the_projection_parameter[4];
4 double the_normal_vector[4];
5 public:
6 Contact_Omega_eh(int* ae, double* pp, double* nv, Omega_eh& oh) : Omega_eh(oh) {
7 for(int i = 0; i < 2; i++) the_associate_element[i] = ae[i];
8 for(int i = 0; i < 4; i++) the_projection_parameter[i] = pp[i];
9 for(int i = 0; i < 4; i++) the_normal_vector[i] = nv[i];
10 }
11 int* associate_element() { return the_associate_element; }
12 double* projection_parameter() { return the_projection_parameter; }
13 double* normal_vector() { return the_normal_vector; }
14 };

The public inheritance relationship makes the class of contact element domain inherits what is available in the
class of element domain. In addition, the derived class is equipped with new private data which define what
boundary elements on the surrounding bodies are associated with the contact element domain. For contact ele-
ment number 3 in Figure 5•11b, the associated elements to the domain from the lower body is the boundary ele-
ment number 2, and from the upper body is the boundary element number 1. A contact element domain for
contact element number 3 is defined inside the constructor of class Omega_h_i as (see also Eq. 5•12)

Workbook of Applications in VectorSpace C++ Library 557


Chapter 5 Advanced Finite Element Methods

Ω1
1
α11 = 0

α10=1/2
n1
3 4
n0 3
α01 =1/2

α00 = 0 2
Ω0
Figure 5•12 Details of the contact element number “3”.

1 int ena[2];
2 ena[0] = 3; ena[1] = 4;
3 Omega_eh *elem = new Omega_eh(3, 0, 0, 2, ena); // store standard element information
4 int aen[2]; // associated element number array
5 aen[0] = 2; aen[1] = 1;
6 double pp[4], nv[4]; // projection parameters and unit normals
7 pp[0] = 0.0; pp[1] = 0.5; pp[2] = 0.5; pp[3] = 0.0; // α00, α01 (lower), and α10, α11 (upper)
8 nv[0] = 0.0; nv[1] = -1.0; // n0 = [n0x, n0y]T
9 nv[2] = 0.0; nv[3] = 1.0; // n1 = [n1x, n1y]T
10 Contact_Omega_eh *c_elem = new Contact_Omega_eh(aen, pp, nv, elem); // contact element
11 omega_eh_array().add(c_elem); // element array

The associated element number array supplies the element numbers of the lower and the upper boundary associ-
ated with the contact element number “3”. The element number for the lower boundary is “2”, and the upper ele-
ment number for the upper boundary is “1”. That is, in line 5,

aen[0] = 2; aen[1] = 1;

This information is needed for the contact element, so the contact element will know which real nodes are asso-
ciated with the contact element. The projection parameters, α00 and α01, for the lower boundary nodes are store
in pp[0] and pp[1]. The first node on the lower boundary associated with the contact element number 3 is the ele-
ment number 17 which is a real node α00 = 0. The second node is a projection node in the middle of element
number 17 and 18. Therefore, the projection parameter α01 = 0.5. Alternatively, we can use Eq. 5•213 to com-
pute the value of α when the geometrical condition becomes more complicated. α10 and α11 for the upper
boundary can be computed similarly. The first node again lies in the middle of node number “1” and node num-
ber “2” of the upper boundary. The projection parameter α10 = 0.5. The second node on the upper boundary is a
real node number “2” of the upper boundary and we have α11 = 0. We write in line 7

pp[0] = 0.0; pp[1] = 0.5; // α00, α01 (Ω0; lower)


pp[2] = 0.5; pp[3] = 0.0; // α10, α11 (Ω1; upper)

558 Workbook of Applications in VectorSpace C++ Library


Contact Mechanics
In lines 10-11, the contact element domain is instantiated and added to the global element array. This eleven lines
of codes are to be automatized in “contact searching algorithm” when contact relationship is changed signifi-
cantly during the deformation process. That is the definition of the contact element domains are created on-the-
fly. The contact searching algorithm will be programmed in the next section.
The Program Listing 5•17 implements the frictionless rigid punch problem. The solution of this problem is
shown in Figure 5•13.
The definition of class “Contact_Omega_eh” bring a significant impact on the object dependency hierarchy
as shown in Section 4.1.3 “Object-Oriented Analysis and Design of Finite Element Method” on page 285. The
reason is that the Ωh is at the top of class dependency graph. Any change bring to it will affect the classes below
it. The contact element domain (Ωeh)c is defined to be associated with two other boundary element by
“associated_element_array”. This information needs to be pass to Element_Formulation, by a switch, so the
Element_Formulation knows whether we are computing the off-diagonal submatrix C with respect to Γelastic
founcation or Γrigid punch. The same switch is also needed for “Global_Discretization_Couple::assembly()” mem-
ber function and “Global_Tensor::operator+=()” operator. An ad hoc extension will be just to re-write these
function definitions within the application source file. These definitions will then be adopted first in the compil-
ing phase, and the linker will not seek their definition in “fe.lib”. The strategy is to let the general purpose library
“fe.lib” remains as simple as possible. The ad hoc extension codes are use for one-shot situation. One certainly
can integrate this impact into the “fe.lib” in a more systematic manner. However, when a beginner of “fe.lib”
read the source code of it, he will be facing a huge set of problems all at once. The advantage of an ad hoc imple-
mentation is that both the code-reuse and simplicity of the “fe.lib” is achieved. The decision on inclusion of
exclusion of certain algorithm into the library is, therefore, a development policy issue.

Figure 5•13 Rigid punch on elastic foundation. The punch has been pushed down 2
unit.

Workbook of Applications in VectorSpace C++ Library 559


Chapter 5 Advanced Finite Element Methods

#include "include\fe.h"
#include "include\omega_h_n.h"
Matrix_Representation_Couple::assembly_switch
Matrix_Representation_Couple::Assembly_Switch = Matrix_Representation_Couple::ALL;
#include "include\global_discretization_gamma_h_n.h"
class Contact_Omega_eh : public Omega_eh {
int the_associate_element[2]; defininig class of a contact element
double the_projection_parameter[4]; domain
double the_normal_vector[4];
public:
Contact_Omega_eh(int* ae, double* pp, double* nv, Omega_eh& oh) : Omega_eh(oh) {
for(int i = 0; i < 2; i++) the_associate_element[i] = ae[i];
for(int i = 0; i < 4; i++) the_projection_parameter[i] = pp[i];
for(int i = 0; i < 4; i++) the_normal_vector[i] = nv[i];
}
int* associate_element() { return the_associate_element; }
double* projection_parameter() { return the_projection_parameter; }
double* normal_vector() { return the_normal_vector; }
};
static const double E_[2] = {1.e5, 1.e10}; Young’s moduli
static const double v_ = (0.5-1.e-12); Poisson ratios
static const double lambda_[2] = { v_*E_[0]/((1+v_)*(1-2*v_)),
v_*E_[1]/((1+v_)*(1-2*v_))};
static const double mu_[2] = {E_[0]/(2*(1+v_)), shear moduli
E_[1]/(2*(1+v_))};
static const double lambda_bar[2] = {2*lambda_[0]*mu_[0]/(lambda_[0]+2*mu_[0]),
2*lambda_[1]*mu_[1]/(lambda_[1]+2*mu_[1])}; λ
Omega_h_i::Omega_h_i(int i) : Omega_h(0){
if(i == 0) { elastic foundation; Ωelastic founcation
double v[2]; Node *node;
v[0] = -30.0; v[1] = -20.0; node = new Node(0, 2, v); node_array().add(node); define nodes
v[0] = -16.0; node = new Node(1, 2, v); node_array().add(node);
v[0] = -8.0; node = new Node(2, 2, v); node_array().add(node);
v[0] = 0.0; node = new Node(3, 2, v); node_array().add(node);
v[0] = 8.0; node = new Node(4, 2, v); node_array().add(node);
v[0] = 16.0; node = new Node(5, 2, v); node_array().add(node);
v[0] = 30.0; node = new Node(6, 2, v); node_array().add(node);
v[0] = -30.0; v[1] = -8.0; node = new Node(7, 2, v); node_array().add(node);
v[0] = -16.0; node = new Node(8, 2, v); node_array().add(node);
v[0] = -8.0; node = new Node(9, 2, v); node_array().add(node);
v[0] = 0.0; node = new Node(10, 2, v); node_array().add(node);
v[0] = 8.0; node = new Node(11, 2, v); node_array().add(node);
v[0] = 16.0; node = new Node(12, 2, v); node_array().add(node);
v[0] = 30.0; node = new Node(13, 2, v); node_array().add(node);
v[0] = -30.0; v[1] = 0.0; node = new Node(14, 2, v); node_array().add(node);
v[0] = -16.0; node = new Node(15, 2, v); node_array().add(node);
v[0] = -8.0; node = neww Node(16, 2, v); node_array().add(node);
v[0] = 0.0; node = new Node(17, 2, v); node_array().add(node);
v[0] = 8.0; node = new Node(18, 2, v); node_array().add(node);
v[0] = 16.0; node = new Node(19, 2, v); node_array().add(node);
v[0] = 30.0; node = new Node(20, 2, v); node_array().add(node);
int ena[4]; Omega_eh *elem;
ena[0] = 0; ena[1] = 1; ena[2] = 8; ena[3] = 7; define elements
elem = new Omega_eh(0, 0, 1, 4, ena); omega_eh_array().add(elem);
ena[0] = 1; ena[1] = 2; ena[2] = 9; ena[3] = 8;
elem = new Omega_eh(1, 0, 1, 4, ena); omega_eh_array().add(elem);
ena[0] = 2; ena[1] = 3; ena[2] = 10; ena[3] = 9;
elem = new Omega_eh(2, 0, 1, 4, ena); omega_eh_array().add(elem);

560 Workbook of Applications in VectorSpace C++ Library


Contact Mechanics
ena[0] = 3; ena[1] = 4; ena[2] = 11; ena[3] = 10;
elem = new Omega_eh(3, 0, 1, 4, ena); omega_eh_array().add(elem);
ena[0] = 4; ena[1] = 5; ena[2] = 12; ena[3] = 11;
elem = new Omega_eh(4, 0, 1, 4, ena); omega_eh_array().add(elem);
ena[0] = 5; ena[1] = 6; ena[2] = 13; ena[3] = 12;
elem = new Omega_eh(5, 0, 1, 4, ena); omega_eh_array().add(elem);
ena[0] = 7; ena[1] = 8; ena[2] = 15; ena[3] = 14;
elem = new Omega_eh(6, 0, 1, 4, ena); omega_eh_array().add(elem);
ena[0] = 8; ena[1] = 9; ena[2] = 16; ena[3] = 15;
elem = new Omega_eh(7, 0, 1, 4, ena); omega_eh_array().add(elem);
ena[0] = 9; ena[1] = 10; ena[2] = 17; ena[3] = 16;
elem = new Omega_eh(8, 0, 1, 4, ena); omega_eh_array().add(elem);
ena[0] = 10; ena[1] = 11; ena[2] = 18; ena[3] = 17;
elem = new Omega_eh(9, 0, 1, 4, ena); omega_eh_array().add(elem);
ena[0] = 11; ena[1] = 12; ena[2] = 19; ena[3] = 18;
elem = new Omega_eh(10, 0, 1, 4, ena); omega_eh_array().add(elem);
ena[0] = 12; ena[1] = 13; ena[2] = 20; ena[3] = 19;
elem = new Omega_eh(11, 0, 1, 4, ena); omega_eh_array().add(elem);
} else if(i == 1) {
double v[2]; Node *node;
rigid punch; Ω rigid punch
v[0] = -10.0; v[1] = 0.0; node = new Node(0, 2, v); node_array().add(node); define nodes
v[0] = -4.0; node = new Node(1, 2, v); node_array().add(node);
v[0] = 4.0; node = new Node(2, 2, v); node_array().add(node);
v[0] = 10.0; node = new Node(3, 2, v); node_array().add(node);
v[0] = -10.0; v[1] = 2.0; node = new Node(4, 2, v); node_array().add(node);
v[0] = -4.0; node = new Node(5, 2, v); node_array().add(node);
v[0] = 4.0; node = new Node(6, 2, v); node_array().add(node);
v[0] = 10.0; node = new Node(7, 2, v); node_array().add(node);
v[0] = -10.0; v[1] = 4.0; node = new Node(8, 2, v); node_array().add(node);
v[0] = -4.0; node = new Node(9, 2, v); node_array().add(node);
v[0] = 4.0; node = new Node(10, 2, v); node_array().add(node);
v[0] = 10.0; node = new Node(11, 2, v); node_array().add(node);
int ena[4]; Omega_eh *elem; define elements
ena[0] = 0; ena[1] = 1; ena[2] = 5; ena[3] = 4;
elem = new Omega_eh(0, 0, 0, 4, ena); omega_eh_array().add(elem);
ena[0] = 1; ena[1] = 2; ena[2] = 6; ena[3] = 5;
elem = new Omega_eh(1, 0, 0, 4, ena); omega_eh_array().add(elem);
ena[0] = 2; ena[1] = 3; ena[2] = 7; ena[3] = 6;
elem = new Omega_eh(2, 0, 0, 4, ena); omega_eh_array().add(elem);
ena[0] = 4; ena[1] = 5; 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] = 10; ena[3] = 9;
elem = new Omega_eh(4, 0, 0, 4, ena); omega_eh_array().add(elem);
ena[0] = 6; ena[1] = 7; ena[2] = 11; ena[3] = 10;
elem = new Omega_eh(5, 0, 0, 4, ena); omega_eh_array().add(elem);
} else if(i == 2) { Γc; contact line elements
double v[2]; Node *node; int ena[2]; int aea[4]; double pp[4], nv[4];
v[0] = -10.0; v[1] = 0.0; node = new Node(0, 2, v); node_array().add(node);
define nodes
v[0] = -8.0; node = new Node(1, 2, v); node_array().add(node);
v[0] = -4.0; node = new Node(2, 2, v); node_array().add(node);
v[0] = 0.0; node = new Node(3, 2, v); node_array().add(node);
v[0] = 4.0; node = new Node(4, 2, v); node_array().add(node);
v[0] = 8.0; node = new Node(5, 2, v); node_array().add(node);
v[0] = 10.0; node = new Node(6, 2, v); node_array().add(node);
Omega_eh *elem; Contact_Omega_eh *c_elem;
ena[0] = 0; ena[1] = 1; elem = new Omega_eh(0, 0, 0, 2, ena);
define elements
aea[0] = 0; aea[1] = 0; pp[0] = 3.0/4.0; pp[1] = 0.0; pp[2] = 0.0; pp[3] = 2.0/3.0;
nv[0] = 0.0; nv[1] = 1.0; nv[2] = 0.0; nv[3] = -1.0;
c_elem = new Contact_Omega_eh(aea, pp, nv, *elem); omega_eh_array().add(c_elem);

Workbook of Applications in VectorSpace C++ Library 561


Chapter 5 Advanced Finite Element Methods
ena[0] = 1; ena[1] = 2; elem = new Omega_eh(1, 0, 0, 2, ena);
aea[0] = 1; aea[1] = 0; pp[0] = 0.0; pp[1] = 1.0/2.0; pp[2] = 1.0/3.0; pp[3] = 0.0;
nv[0] = 0.0; nv[1] = -1.0; nv[2] = 0.0; nv[3] = 1.0;
c_elem = new Contact_Omega_eh(aea, pp, nv, *elem); omega_eh_array().add(c_elem);
ena[0] = 2; ena[1] = 3; elem = new Omega_eh(2, 0, 0, 2, ena);
aea[0] = 1; aea[1] = 1; pp[0] = 1.0/2.0; pp[1] = 0.0; pp[2] = 0.0; pp[3] = 1.0/2.0;
nv[0] = 0.0; nv[1] = 1.0; nv[2] = 0.0; nv[3] = -1.0;
c_elem = new Contact_Omega_eh(aea, pp, nv, *elem); omega_eh_array().add(c_elem);
ena[0] = 3; ena[1] = 4; elem = new Omega_eh(3, 0, 0, 2, ena);
aea[0] = 2; aea[1] = 1; pp[0] = 0.0; pp[1] = 1.0/2.0; pp[2] = 1.0/2.0; pp[3] = 0.0;
nv[0] = 0.0; nv[1] = -1.0; nv[2] = 0.0; nv[3] = 1.0;
c_elem = new Contact_Omega_eh(aea, pp, nv, *elem); omega_eh_array().add(c_elem);
ena[0] = 4; ena[1] = 5; elem = new Omega_eh(4, 0, 0, 2, ena);
aea[0] = 2; aea[1] = 2; pp[0] = 1.0/2.0; pp[1] = 0.0; pp[2] = 0.0; pp[3] = 1.0/3.0;
nv[0] = 0.0; nv[1] = 1.0; nv[2] = 0.0; nv[3] = -1.0;
c_elem = new Contact_Omega_eh(aea, pp, nv, *elem); omega_eh_array().add(c_elem);
ena[0] = 5; ena[1] = 6; elem = new Omega_eh(5, 0, 0, 2, ena);
aea[0] = 3; aea[1] = 2; pp[0] = 0.0; pp[1] = 3.0/4.0; pp[2] = 2.0/3.0; pp[3] = 0.0;
nv[0] = 0.0; nv[1] = -1.0; nv[2] = 0.0; nv[3] = 1.0;
c_elem = new Contact_Omega_eh(aea, pp, nv, *elem); omega_eh_array().add(c_elem);
}
}
Gamma_h_i::Gamma_h_i(int i, Omega_h &oh) : Omega_h(oh), the_index(i) {
new ena[2]; Omega_eh *elem; potential contact boundaries
if(i == 0) { // elastic foundation upper boundary as potential contact surface Γelastic founcation
ena[0] = 15;ena[1] = 16;elem = new Omega_eh(0, 0, 0, 2, ena);the_omega_eh_array.add(elem);
ena[0] = 16;ena[1] = 17;elem = new Omega_eh(1, 0, 0, 2, ena);the_omega_eh_array.add(elem);
ena[0] = 17;ena[1] = 18;elem = new Omega_eh(2, 0, 0, 2, ena);the_omega_eh_array.add(elem);
ena[0] = 18;ena[1] = 19;elem = new Omega_eh(3, 0, 0, 2, ena);the_omega_eh_array.add(elem);
ena[0] = 19;ena[1] = 20;elem = new Omega_eh(4, 0, 0, 2, ena);the_omega_eh_array.add(elem);
} else if(i == 1) { // rigid punch lower bounary as potential contact surface
ena[0] = 0;ena[1] = 1;elem = new Omega_eh(0, 0, 0, 2, ena);the_omega_eh_array.add(elem); Γrigid punch
ena[0] = 1;ena[1] = 2;elem = new Omega_eh(1, 0, 0, 2, ena);the_omega_eh_array.add(elem);
ena[0] = 2;ena[1] = 3;elem = new Omega_eh(2, 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() { Boundary conditions
gh_on_Gamma_h::__initialization(df, omega_h);
if(i == 0) {.
the_gh_array[node_order(0)](0) = the_gh_array[node_order(0)](1) = elastic foundation
the_gh_array[node_order(1)](1) = the_gh_array[node_order(2)](1) =
the_gh_array[node_order(3)](1) = the_gh_array[node_order(4)](1) =
the_gh_array[node_order(5)](1) = the_gh_array[node_order(6)](0) =
the_gh_array[node_order(6)](1) = the_gh_array[node_order(7)](0) =
the_gh_array[node_order(13)](0) = the_gh_array[node_order(14)](0) =
the_gh_array[node_order(20)](0) = gh_on_Gamma_h::Dirichlet;
} else if(i == 1) {
the_gh_array[node_order(8)](0) = the_gh_array[node_order(8)](1) = rigid punch; push downwards 2 units
the_gh_array[node_order(9)](0) = the_gh_array[node_order(9)](1) =
the_gh_array[node_order(10)](0) = the_gh_array[node_order(10)](1) =
the_gh_array[node_order(11)](0) = the_gh_array[node_order(11)](1) =
gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(8)][1] = the_gh_array[node_order(9)][1] =
the_gh_array[node_order(10)][1] = the_gh_array[node_order(11)][1] =-2.0;
}
}

562 Workbook of Applications in VectorSpace C++ Library


Contact Mechanics
static int ndf = 2; instantiation of global objects
static Omega_h_i elastic_foundation_oh(0); elastic foundation begins
static gh_on_Gamma_h_i elastic_foundation_gh(0, ndf, elastic_foundation_oh);
static U_h u_0(ndf, elastic_foundation_oh);
static Global_Discretization *elastic_foundation_type = new Global_Discretization();
static Global_Discretization Ωelastic founcation
elastic_foundation_gd(elastic_foundation_oh, elastic_foundation_gh, u_0,
elastic_foundation_type);
static Gamma_h_i potential contact boundary on
elastic_foundation_gamma_h(0, elastic_foundation_oh); elastic foundation; Γelastic founcation
static Global_Discretization_Gamma_h_i
elastic_foundation_gamma_h_gd(0, elastic_foundation_gd, elastic_foundation_type,
elastic_foundation_gamma_h); elastic foundation ends
static Omega_h_i rigid_punch_oh(1); rigid punch begins
static gh_on_Gamma_h_i rigid_punch_gh(1, ndf, rigid_punch_oh);
static U_h u_1(ndf, rigid_punch_oh);
static Global_Discretization *rigid_punch_type = new Global_Discretization();
static Global_Discretization Ω rigid punch
rigid_punch_gd(rigid_punch_oh, rigid_punch_gh, u_1, rigid_punch_type);
static Gamma_h_i potential contact boundary on
rigid_punch_gamma_h(1, rigid_punch_oh); rigid punch; Γrigid punch
static Global_Discretization_Gamma_h_i rigid punch ends
rigid_punch_gamma_h_gd(1, rigid_punch_gd, rigid_punch_type, rigid_punch_gamma_h);
static Omega_h_i interface_oh(2); Contact interface; Γc
static gh_on_Gamma_h_i interface_gh(2, ndf, interface_oh);
static U_h lambda(ndf, interface_oh);
static Global_Discretization interface_gd(interface_oh, interface_gh, lambda);
static Global_Discretization_Couple *interface_elastic_foundation_gamma_h_type λ
= new Global_Discretization_Couple(); for {Γc-Γelastic founcation}
static Global_Discretization_Couple
interface_elastic_foundation_gdc(interface_gd, elastic_foundation_gamma_h_gd,
interface_elastic_foundation_gamma_h_type);
static Global_Discretization_Couple *interface_rigid_punch_gamma_h_type
= new Global_Discretization_Couple(); for {Γc-Γrigid punch}
static Global_Discretization_Couple
interface_rigid_punch_gdc(interface_gd, rigid_punch_gamma_h_gd,
interface_rigid_punch_gamma_h_type);
class Elastic_Contact_Q4 : public Element_Formulation_Couple { begin contact element formulation
public:
Elastic_Contact_Q4(Element_Type_Register a) : Element_Formulation_Couple(a) {}
Element_Formulation *make(int, Global_Discretization&); diagonal block formulations; Ki
Elastic_Contact_Q4(int, Global_Discretization&);
Element_Formulation_Couple *make(int, Global_Discretization_Couple&);
Elastic_Contact_Q4(int, Global_Discretization_Couple&); off-diagonal block formulations; Qi
};
Element_Formulation* Elastic_Contact_Q4::make(int en, Global_Discretization& gd) {
return new Elastic_Contact_Q4(en,gd);
}
Elastic_Contact_Q4::Elastic_Contact_Q4(int en, Global_Discretization& gd) :
Element_Formulation_Couple(en, gd) {
int index;
if(gd.type() == elastic_foundation_type) index = 0;
else if(gd.type() == rigid_punch_type) index = 1; K0(elsatic foundation)
Quadrature qp2(2, 4); K1(rigid punch)
H1 Z(2, (double*)0, qp2), Zai, Eta,
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 4, 2, qp2);
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;

Workbook of Applications in VectorSpace C++ Library 563


Chapter 5 Advanced Finite Element Methods
H1 X = N*xl; H0 Nx = d(N) * d(X).inverse(); J dv2(d(X).det());
C0 e = BASIS("int", ndf), E = BASIS("int", nen), u = e*E, 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_dev = mu_[index]* (
+( ((2.0*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.0*Wy*~Wy)+(Wx*~Wx))*((e[1]%e[1])*(E%E)) )
| dv2);
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 dv1(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_vol = lambda_bar[index]*(
+( wx*~wx*U[0][0]+wx*~wy*U[0][1]+
wy*~wx*U[1][0]+wy*~wy*U[1][1] )
| dv1 );
stiff &= stiff_vol + stiff_dev;
}
Element_Formulation_Couple* Elastic_Contact_Q4::make(int en,
Global_Discretization_Couple& gdc) { return new Elastic_Contact_Q4(en,gdc); }
Elastic_Contact_Q4::Elastic_Contact_Q4(int en, Global_Discretization_Couple& gdc)
: Element_Formulation_Couple(en, gdc) {
Quadrature qp(1, 2);
H1 Z(qp),
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 2, 1, qp);
N[0] = (1.0-Z)/2.0; N[1] = (1.0+Z)/2.0;
H1 X = N*xl;
J d_l(norm(d(X)(0)));
C0 zero(0.0);
H0 N_lambda = ((~(H0)N) || zero ) &
(zero || (~(H0)N));
Omega_eh *elem = &(gdc.principal_discretization().omega_h()(en));
0
double *pp = ((Contact_Omega_eh*)elem)->projection_parameter(), ( 1 – α 0 )n 0 α0 n0 û 0
*nv = ((Contact_Omega_eh*)elem)->normal_vector(); u= = cû
C0 n_0(2,nv), n_1(2, nv+2); α1 n1 ( 1 – α 1 )n 1 1
û 0
if(gdc.type() == interface_rigid_punch_gamma_h_type) pp += 2;
C0 alpha(2,pp);
H0 N_bar_0 = ( ((H0)N[0])*((1.0-alpha[0])*n_0) + ((H0)N[1])*(alpha[1]*n_1)
N_bar_1 = ( ((H0)N[0])*(alpha[0]*n_0) + ((H0)N[1])*((1.0-alpha[1])*n_1) ),
), Q = ∫ NλT Nu dΓ ; where N ≡ Nc
u
Γc
N_u_bar = ( N_bar_0[0] | zero | N_bar_1[0] | zero )&
( zero | N_bar_0[1] | zero | N_bar_1[1] ); Q0 {Γc-Γelastic founcation}
if(gdc.type() == interface_elastic_foundation_gamma_h_type)
stiff &= -(((~N_lambda)*N_u_bar)|d_l); Q1 {Γc-Γrigid punch}
else
stiff &= (((~N_lambda)*N_u_bar)|d_l);
}
Element_Formulation* Element_Formulation::type_list = 0;
Element_Type_Register element_type_register_instance;
static Elastic_Contact_Q4 elastic_contact_q4_instance(element_type_register_instance);

564 Workbook of Applications in VectorSpace C++ Library


Contact Mechanics
static Matrix_Representation mr_K_0(elastic_foundation_gd); K0
static Matrix_Representation_Couple mrc_Q_0(interface_elastic_foundation_gdc, 0, 0,
&(mr_K_0.rhs()) );
Q0
static Matrix_Representation mr_K_1(rigid_punch_gd); K1
static Matrix_Representation_Couple mrc_Q_1(interface_rigid_punch_gdc, 0, &(mrc_Q_0.rhs()), Q1
&(mr_K_1.rhs()) );
#include <time.h>
int main() {
time_t time0 = time(0);
mr_K_0.assembly();
C0 K_0 = (C0)(mr_K_0.lhs()); cout << "K_0:" << endl << K_0 << endl;
K0
time_t time1 = time(0);
cout << "elipsed time: " << (difftime(time1, time0)/60) << " minutes." << endl;
mrc_Q_0.assembly();
C0 Q_0 = (C0)(mrc_Q_0.lhs()); cout << "Q_0:" << endl << Q_0 << endl;
Q0
time1 = time(0);
cout << "elipsed time: " << (difftime(time1, time0)/60) << " minutes." << endl;
mr_K_1.assembly();
C0 K_1 = (C0)(mr_K_1.lhs()); cout << "K_1:" << endl << K_1 << endl;
K1
time1 = time(0);
cout << "elipsed time: " << (difftime(time1, time0)/60) << " minutes." << endl;
mrc_Q_1.assembly();
C0 Q_1 = (C0)(mrc_Q_1.lhs()), f_0 = (C0)(mr_K_0.rhs()),
Q1
f_1 = (C0)(mr_K_1.rhs()), f_i = (C0)(mrc_Q_0.rhs()); f0, f1, fi
cout << "Q_1:" << endl << Q_1 << endl; cout << "f_0:" << endl << f_0 << endl;
cout << "f_1:" << endl << f_1 << endl; cout << "f_i:" << endl << f_i << endl;
time1 = time(0);
cout << "elipsed time: " << (difftime(time1, time0)/60) << " minutes." << endl;
cout << endl << "solution phase begins... " << endl;
Cholesky dK0(K_0);
C0 K0_inv = dK0.inverse(); C0 QK_inv_0 = Q_0*K0_inv; C0 QKQ_0 = QK_inv_0*(~Q_0);
K0-1, Q0 K0-1 Q0T
Cholesky dK1(K_1);
C0 K1_inv = dK1.inverse(); C0 QK_inv_1 = Q_1*K1_inv; C0 QKQ_1 = QK_inv_1*(~Q_1); K1-1, Q1 K1-1 Q1T
Cholesky mdQKQ(QKQ_0+QKQ_1, 1.e-12);
double *iptr; iptr = new double(0.0); modf(log10(mdQKQ.cond()), iptr);
(Q0 K0-1 Q0T + K1-1, Q1 K1-1 Q1T)-1
if(*iptr > (2.0-1.e-12)) cout << "Warnning: lost of " << int(*iptr) <<
" significant digits in modified Cholesky decomposition." << endl;
delete iptr;
C0 lambda = mdQKQ * (QK_inv_0*f_0+QK_inv_1*f_1-f_i);
λ̂ =(Q0 K0-1 Q0T + K1-1, Q1 K1-1 Q1T)-1
C0 u_0 = dK0*(f_0-(~Q_0)*lambda); (Q0 K0-1 f0 + Q1K1-1 f1 - fi)
C0 u_1 = dK1*(f_1-(~Q_1)*lambda); û 0 = K0-1 (f0 - Q0T λ̂ )
interface_gd.u_h() = lambda;
interface_gd.u_h() = interface_gd.gh_on_gamma_h();
û 1 = K1-1 (f1 - Q1T λ̂ )
cout << "contact forces:" << endl << interface_gd.u_h();
elastic_foundation_gd.u_h() = u_0;
elastic_foundation_gd.u_h() = elastic_foundation_gd.gh_on_gamma_h();
cout << "elastic foundation displacement:" << endl << elastic_foundation_gd.u_h();
rigid_punch_gd.u_h() = u_1;
rigid_punch_gd.u_h() = rigid_punch_gd.gh_on_gamma_h();
cout << "rigid punch displacement:" << endl << rigid_punch_gd.u_h();
time1 = time(0);
cout << "elipsed time: " << (difftime(time1, time0)/60) << " minutes." << endl;
return 0;
}

Listing 5•17 Frictionless rigid punch on elastic foundation ( project: “contact_rigid_punch_on_elastic_


foundation” in project workspace file “fe.dsw”).

Workbook of Applications in VectorSpace C++ Library 565


Chapter 5 Advanced Finite Element Methods
5.2.3 Frictionless Rigid Sled on Elastic Foundation
In the context of optimization, the “active set” method for the rigid punch problem in the previous section is
defined manually. For the rigid sled problem the contact geometry change dramatically for each time step, where
a contact searching algorithm is used to implement the “active set” automatically. The contact searching algo-
rithm is resided in class Omega_h_i (included in the source code and the header file in “fe.lib”), which is to
define geometrical relationship in two pairs of boundaries, {Γc-Γelastic founcation } and {Γc-Γrigid sled}. We use the
procedure programming method for the implementation of the contact searching algorithm. The reason of using
the procedure programming method instead of the object-oriented programming method in this case is that the
object-oriented programming is usually applied to implement a set of problems, which is a more systematic and
thorough approach, and is naturally more time consuming to establish such a framework for serving a set of
problems. Therefore, it is a design decision meaning we are dealing with contact problem, which is not the sole
focus in this workbook, only in an ad hoc manner.
The basic elements of the procedure programming method are data structure and algorithm. In the spirit of
the relational database, we use a table to represent the data structure of the information in Figure 5•11 b.
TABLE5• 2. contains two rows, the first row corresponding to the node numbers in Γelastic founcation and the sec-
ond row in Γrigid punch. The header numbers (columns) are the contact boundary node numbers. All the cells in
the table are given the default value of “-1”. These default values are to be over-written with node numbers
(greater of equal to zero) of the Γelastic founcation and Γrigid punch encountered. Therefore, if in a cell contains the
default value (-1) after the over-writing process, it means the corresponding node on the two boundaries, for the
contact surface, is a projected node as in Figure 5•11 (b).

Contact node# 0 1 2 3 4 5 6
Γelastic founcation 0 -1 1 -1 2 -1 3
Γrigid sled -1 16 -1 17 -1 18 -1
TABLE5• 2. Contact node number table.

What is not shown in the present example is, if for a particular contact node number both cells contain the
default value “-1” it means that there is a detachment segment along the contact surface. This situation occurred
either there is no node project onto certain segments or the projection form a gap that is beyond a pre-defined
tolerance value. If both cell contains no default values, that is the case when the node on one side are projected
to the node on the other side within a tolerance distance. The contact searching algorithm is to (1) construct
TABLE5• 2. automatically, then (2) use this table to define contact nodes and contact elements. The second part
is straight forward, since we have the manual input experience gained from the rigid punch problem in the previ-
ous section. We focus on the first part. The basic idea is to scan through the contact surface Γc to find a leading
node from either side of the potential contact boundaries Γi . The leading node is the first node encountered that
is successful projected to the opposite boundary segment. For example, if we are now in the middle of the pro-
cess at the contact node number 2, the next leading node that can be successfully project to the opposing Γi is
node number 17 on the Γelastic foundation. Successive leading nodes are filled in to the rows that they are corre-
sponding to, and leave the opposite row with the default value (-1) which indicates a projected node. Inspecting
on yet another example in Figure 5•10, we find that the leading node is not necessary always alternating on rows
in their appearance on the contact searching number table as in TABLE5• 2. In general, they may appear consec-

566 Workbook of Applications in VectorSpace C++ Library


Contact Mechanics
utive on the same side for indefinite number of times. This is implemented in file “contact_searching.cpp” under
directory “vs\fe\lib”.
We need some geometrical utilities to perform the node projection on boundary. Eq. 5•213, which basically is
the cosine theorem, can be used for this purpose. For example if the projection parameter has the value of

0≤α≤1 Eq. 5•223

Then the projection of a node onto its opposite target segment is successful, on condition that the gap as com-
puted from Eq. 5•211 between the current node and the projected node is within a tolerance value. In a special
condition, when α is very close to 0 or 1 and the distance between the projected node and the ends of the target
segment is small, we have a node-on-node projection situation. These functions are implemented in the same file
“contact_searching.cpp” under its “geometrical utilities” section.
The contact searching algorithm should be invoked upon every iteration. The Q matrix will be changed
accordingly. The rigid sled is push from left to the right for 8 steps with incremental displacement of ∆v = 0.2;
i.e., vt = ∆v t ( where t = 0, 8). The Program implements the frictionless sled on elastic foundation problem. The
Program Listing 5•18 implements the frictionless rigid punch problem. The solution of this problem is shown in
Figure 5•14.

Workbook of Applications in VectorSpace C++ Library 567


Chapter 5 Advanced Finite Element Methods

t=2

t=5

t=8

Figure 5•14 Rigid sled on elastic foundation.

568 Workbook of Applications in VectorSpace C++ Library


Contact Mechanics

#include "vs.h"
#include "fe.h"
#include "omega_h_n.h"
Matrix_Representation_Couple::assembly_switch
Matrix_Representation_Couple::Assembly_Switch = Matrix_Representation_Couple::ALL;
#include "global_discretization_gamma_h_n.h"
#include "contact_searching.h"
static const double E_[2] = {1.e5, 1.e10}; static const double v_ = (0.5-1.e-12); Young’s moduli
static const double lambda_[2] = {v_*E_[0]/((1+v_)*(1-2*v_)), v_*E_[1]/((1+v_)*(1-2*v_))}; Poisson ratios
static const double mu_[2] = {E_[0]/(2*(1+v_)), E_[1]/(2*(1+v_))}; shear moduli
static const double lambda_bar[2] =
{2*lambda_[0]*mu_[0]/(lambda_[0]+2*mu_[0]),2*lambda_[1]*mu_[1]/(lambda_[1]+2*mu_[1])}; λ
Omega_h_i::Omega_h_i(int i) : Omega_h(0){
the_index = i;
if(i == 0) {
double v[2]; Node *node; elastic foundation; Ωelastic founcation
v[0] = -30.0; v[1] = -20.0; node = new Node(0, 2, v); node_array().add(node); define nodes
v[0] = -16.0; node = new Node(1, 2, v); node_array().add(node);
v[0] = -8.0; node = new Node(2, 2, v); node_array().add(node);
v[0] = 0.0; node = new Node(3, 2, v); node_array().add(node);
v[0] = 8.0; node = new Node(4, 2, v); node_array().add(node);
v[0] = 16.0; node = new Node(5, 2, v); node_array().add(node);
v[0] = 30.0; node = new Node(6, 2, v); node_array().add(node);
v[0] = -30.0; v[1] = -8.0; node = new Node(7, 2, v); node_array().add(node);
v[0] = -16.0; node = new Node(8, 2, v); node_array().add(node);
v[0] = -8.0; node = new Node(9, 2, v); node_array().add(node);
v[0] = 0.0; node = new Node(10, 2, v); node_array().add(node);
v[0] = 8.0; node = new Node(11, 2, v); node_array().add(node);
v[0] = 16.0; node = new Node(12, 2, v); node_array().add(node);
v[0] = 30.0; node = new Node(13, 2, v); node_array().add(node);
v[0] = -30.0; v[1] = 0.0; node = new Node(14, 2, v); node_array().add(node);
v[0] = -16.0; node = new Node(15, 2, v); node_array().add(node);
v[0] = -8.0; node = new Node(16, 2, v); node_array().add(node);
v[0] = 0.0; node = new Node(17, 2, v); node_array().add(node);
v[0] = 8.0; node = new Node(18, 2, v); node_array().add(node);
v[0] = 16.0; node = new Node(19, 2, v); node_array().add(node);
v[0] = 30.0; node = new Node(20, 2, v); node_array().add(node);
int ena[4]; Omega_eh *elem; define elements
ena[0] = 0; ena[1] = 1; ena[2] = 8; ena[3] = 7;
elem = new Omega_eh(0, 0, 1, 4, ena); omega_eh_array().add(elem);
ena[0] = 1; ena[1] = 2; ena[2] = 9; ena[3] = 8;
elem = new Omega_eh(1, 0, 1, 4, ena); omega_eh_array().add(elem);
ena[0] = 2; ena[1] = 3; ena[2] = 10; ena[3] = 9;
elem = new Omega_eh(2, 0, 1, 4, ena); omega_eh_array().add(elem);
ena[0] = 3; ena[1] = 4; ena[2] = 11; ena[3] = 10;
elem = new Omega_eh(3, 0, 1, 4, ena); omega_eh_array().add(elem);
ena[0] = 4; ena[1] = 5; ena[2] = 12; ena[3] = 11;
elem = new Omega_eh(4, 0, 1, 4, ena); omega_eh_array().add(elem);
ena[0] = 5; ena[1] = 6; ena[2] = 13; ena[3] = 12;
elem = new Omega_eh(5, 0, 1, 4, ena); omega_eh_array().add(elem);
ena[0] = 7; ena[1] = 8; ena[2] = 15; ena[3] = 14;
elem = new Omega_eh(6, 0, 1, 4, ena); omega_eh_array().add(elem);
ena[0] = 8; ena[1] = 9; ena[2] = 16; ena[3] = 15;
elem = new Omega_eh(7, 0, 1, 4, ena); omega_eh_array().add(elem);
ena[0] = 9; ena[1] = 10; ena[2] = 17; ena[3] = 16;
elem = new Omega_eh(8, 0, 1, 4, ena); omega_eh_array().add(elem);
ena[0] = 10; ena[1] = 11; ena[2] = 18; ena[3] = 17;
elem = new Omega_eh(9, 0, 1, 4, ena); omega_eh_array().add(elem);

Workbook of Applications in VectorSpace C++ Library 569


Chapter 5 Advanced Finite Element Methods
ena[0] = 11; ena[1] = 12; ena[2] = 19; ena[3] = 18;
elem = new Omega_eh(10, 0, 1, 4, ena); omega_eh_array().add(elem);
ena[0] = 12; ena[1] = 13; ena[2] = 20; ena[3] = 19;
elem = new Omega_eh(11, 0, 1, 4, ena); omega_eh_array().add(elem);
} else if(i == 1) { rigid sled; Ω rigid sled
double v[2]; Node *node;
v[0] = -10.0; v[1] = 0.0; node = new Node(0, 2, v); node_array().add(node);
define nodes
v[0] = -4.0; node = new Node(1, 2, v); node_array().add(node);
v[0] = 4.0; node = new Node(2, 2, v); node_array().add(node);
v[0] = 10.0; node = new Node(3, 2, v); node_array().add(node);
v[0] = -10.0; v[1] = 2.0; node = new Node(4, 2, v); node_array().add(node);
v[0] = -4.0; node = new Node(5, 2, v); node_array().add(node);
v[0] = 4.0; node = new Node(6, 2, v); node_array().add(node);
v[0] = 10.0; node = new Node(7, 2, v); node_array().add(node);
v[0] = -10.0; v[1] = 4.0; node = new Node(8, 2, v); node_array().add(node);
v[0] = -4.0; node = new Node(9, 2, v); node_array().add(node);
v[0] = 4.0; node = new Node(10, 2, v); node_array().add(node);
v[0] = 10.0; node = new Node(11, 2, v); node_array().add(node);
int ena[4]; Omega_eh *elem;
ena[0] = 0; ena[1] = 1; ena[2] = 5; ena[3] = 4; define elements
elem = new Omega_eh(0, 0, 0, 4, ena); omega_eh_array().add(elem);
ena[0] = 1; ena[1] = 2; ena[2] = 6; ena[3] = 5;
elem = new Omega_eh(1, 0, 0, 4, ena); omega_eh_array().add(elem);
ena[0] = 2; ena[1] = 3; ena[2] = 7; ena[3] = 6;
elem = new Omega_eh(2, 0, 0, 4, ena); omega_eh_array().add(elem);
ena[0] = 4; ena[1] = 5; 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] = 10; ena[3] = 9;
elem = new Omega_eh(4, 0, 0, 4, ena); omega_eh_array().add(elem);
ena[0] = 6; ena[1] = 7; ena[2] = 11; ena[3] = 10;
elem = new Omega_eh(5, 0, 0, 4, ena); omega_eh_array().add(elem);
} Γc; contact line elements
} are defined by “contact
searching algorithm” on the
fly
Contact_Omega_h_i::Contact_Omega_h_i(int i) : Omega_h_i(i) { /* do nothing */ }
Gamma_h_i::Gamma_h_i(int i, Omega_h &oh) : Omega_h(oh), the_index(i) {
int ena[2]; Omega_eh *elem;
if(i == 0) { potential contact boundaries
ena[0] = 15; ena[1] = 16; Γelastic founcation
elem = new Omega_eh(0, 0, 0, 2, ena); the_omega_eh_array.add(elem);
ena[0] = 16; ena[1] = 17;
elem = new Omega_eh(1, 0, 0, 2, ena); the_omega_eh_array.add(elem);
ena[0] = 17; ena[1] = 18;
elem = new Omega_eh(2, 0, 0, 2, ena); the_omega_eh_array.add(elem);
ena[0] = 18; ena[1] = 19;
elem = new Omega_eh(3, 0, 0, 2, ena); the_omega_eh_array.add(elem);
ena[0] = 19; ena[1] = 20;
elem = new Omega_eh(4, 0, 0, 2, ena); the_omega_eh_array.add(elem);
} else if(i == 1) {
ena[0] = 0; ena[1] = 1; Γrigid sled
elem = new Omega_eh(0, 0, 0, 2, ena); the_omega_eh_array.add(elem);
ena[0] = 1; ena[1] = 2;
elem = new Omega_eh(1, 0, 0, 2, ena); the_omega_eh_array.add(elem);
ena[0] = 2; ena[1] = 3;
elem = new Omega_eh(2, 0, 0, 2, ena); the_omega_eh_array.add(elem);
}
}

570 Workbook of Applications in VectorSpace C++ Library


Contact Mechanics
gh_on_Gamma_h_i::gh_on_Gamma_h_i(int i, int df, Omega_h& omega_h) : gh_on_Gamma_h() { Boundary conditions
gh_on_Gamma_h::__initialization(df, omega_h);
if(i == 0) {
the_gh_array[node_order(0)](0) = the_gh_array[node_order(0)](1) = elastic foundation
the_gh_array[node_order(1)](1) = the_gh_array[node_order(2)](1) =
the_gh_array[node_order(3)](1) = the_gh_array[node_order(4)](1) =
the_gh_array[node_order(5)](1) = the_gh_array[node_order(6)](0) =
the_gh_array[node_order(6)](1) = the_gh_array[node_order(7)](0) =
the_gh_array[node_order(13)](0) = the_gh_array[node_order(14)](0) =
the_gh_array[node_order(20)](0) = gh_on_Gamma_h::Dirichlet;
} else if(i == 1) {
the_gh_array[node_order(8)](0) = the_gh_array[node_order(8)](1) = rigid sled; push horizontally 2 units
the_gh_array[node_order(9)](0) = the_gh_array[node_order(9)](1) =
the_gh_array[node_order(10)](0) = the_gh_array[node_order(10)](1) =
the_gh_array[node_order(11)](0) = the_gh_array[node_order(11)](1) =
gh_on_Gamma_h::Dirichlet;
the_gh_array[node_order(8)][1] = the_gh_array[node_order(9)][1] =
the_gh_array[node_order(10)][1] = the_gh_array[node_order(11)][1] = -0.2;
}
} instantiation of global objects
static int ndf = 2; elastic foundation begins
Ωelastic founcation
static Omega_h_i elastic_foundation_oh(0);
static gh_on_Gamma_h_i elastic_foundation_gh(0, ndf, elastic_foundation_oh);
static U_h u_0_h(ndf, elastic_foundation_oh);
static Global_Discretization *elastic_foundation_type = new Global_Discretization();
static Global_Discretization elastic_foundation_gd(
elastic_foundation_oh, elastic_foundation_gh, u_0_h, elastic_foundation_type);
potential contact boundary on
static Gamma_h_i elastic_foundation_gamma_h(0, elastic_foundation_oh); elastic foundation; Γelastic founcation
static Global_Discretization_Gamma_h_i elastic_foundation_gamma_h_gd(0, elastic foundation ends
elastic_foundation_gd, elastic_foundation_type, elastic_foundation_gamma_h);
static Omega_h_i rigid_sled_oh(1);
rigid sled begins
static gh_on_Gamma_h_i rigid_sled_gh(1, ndf, rigid_sled_oh); Ω rigid sled
static U_h u_1_h(ndf, rigid_sled_oh);
static Global_Discretization *rigid_sled_type = new Global_Discretization();
static Global_Discretization rigid_sled_gd(rigid_sled_oh, rigid_sled_gh, u_1_h, rigid_sled_type);
static Gamma_h_i rigid_sled_gamma_h(1, rigid_sled_oh); potential contact boundary on
static Global_Discretization_Gamma_h_i rigid sled; Γrigid sled
rigid_sled_gamma_h_gd(1, rigid_sled_gd, rigid_sled_type, rigid_sled_gamma_h);
static Contact_Omega_h_i interface_oh(2);
rigid sled ends
static gh_on_Gamma_h_i interface_gh(2, ndf, interface_oh); Contact interface; Γc
static U_h lambda_h(ndf, interface_oh); λ
static Global_Discretization interface_gd(interface_oh, interface_gh, lambda_h);
static Global_Discretization_Couple *interface_elastic_foundation_gamma_h_type
for {Γc-Γelastic founcation}
= new Global_Discretization_Couple();
static Global_Discretization_Couple interface_elastic_foundation_gdc(interface_gd,
elastic_foundation_gamma_h_gd, interface_elastic_foundation_gamma_h_type);
static Global_Discretization_Couple *interface_rigid_sled_gamma_h_type
= new Global_Discretization_Couple(); for {Γc-Γrigid sled}
static Global_Discretization_Couple interface_rigid_sled_gdc(interface_gd,
rigid_sled_gamma_h_gd, interface_rigid_sled_gamma_h_type);
class Elastic_Contact_Q4 : public Element_Formulation_Couple {
public: Element Formulation
Elastic_Contact_Q4(Element_Type_Register a) : Element_Formulation_Couple(a) {}
Element_Formulation *make(int, Global_Discretization&);
Elastic_Contact_Q4(int, Global_Discretization&);
Element_Formulation_Couple *make(int, Global_Discretization_Couple&);
Elastic_Contact_Q4(int, Global_Discretization_Couple&);
};

Workbook of Applications in VectorSpace C++ Library 571


Chapter 5 Advanced Finite Element Methods
Element_Formulation* Elastic_Contact_Q4::make(int en, Global_Discretization& gd) { diagonal block formulations; Ki
return new Elastic_Contact_Q4(en,gd);
}
Elastic_Contact_Q4::Elastic_Contact_Q4(int en, Global_Discretization& gd) :
Element_Formulation_Couple(en, gd) {
int index;
if(gd.type() == elastic_foundation_type) index = 0;
K0(elsatic foundation)
else if(gd.type() == rigid_sled_type) index = 1; K1(rigid sled)
Quadrature qp2(2, 4);
H1 Z(2, (double*)0, qp2), Zai, Eta,
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 4, 2, qp2);
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 dv2(d(X).det());
C0 e = BASIS("int", ndf), E = BASIS("int", nen), u = e*E, 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_dev = mu_[index]* (
+( ((2.0*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.0*Wy*~Wy)+(Wx*~Wx))*((e[1]%e[1])*(E%E)) )
| dv2);
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 dv1(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_vol = lambda_bar[index]*( +( wx*~wx*U[0][0]+wx*~wy*U[0][1]+
wy*~wx*U[1][0]+wy*~wy*U[1][1] ) | dv1 );
stiff &= stiff_vol + stiff_dev;
}
Element_Formulation_Couple* Elastic_Contact_Q4::make(int en,
Global_Discretization_Couple& gdc) { return new Elastic_Contact_Q4(en,gdc); }
off-diagonal block formulations; Qi
Elastic_Contact_Q4::Elastic_Contact_Q4(int en, Global_Discretization_Couple& gdc)
: Element_Formulation_Couple(en, gdc) {
Omega_eh *elem = &(gdc.principal_discretization().omega_h()(en));
double *pp = ((Contact_Omega_eh*)elem)->projection_parameter(),
*nv = ((Contact_Omega_eh*)elem)->normal_vector();
C0 n_0(2,nv), n_1(2, nv+2);
C0 t(2, 2, (double*)0);
t[0][0] = ul[0]; t[0][1] = ul[1]; t[1][0] = ul[2]; t[1][1] = ul[3];
double lambda_0 = (double)(t[0]*n_0), lambda_1 = (double)(t[1]*n_1),
lambda_bar = (lambda_0+lambda_1)/2.0;
static const double EPSILON = 1.e-12;
if(lambda_bar > -EPSILON) stiff &= C0(4, 4, (double*)0);
else {
if(gdc.type() == interface_rigid_sled_gamma_h_type) pp += 2;
C0 alpha(2,pp);
Quadrature qp(1, 2);
H1 Z(qp),
N = INTEGRABLE_VECTOR_OF_TANGENT_BUNDLE("int, int, Quadrature", 2, 1, qp);
N[0] = (1.0-Z)/2.0; N[1] = (1.0+Z)/2.0;

572 Workbook of Applications in VectorSpace C++ Library


Contact Mechanics
H1 X = N*xl; J d_l(norm(d(X)(0))); C0 zero(0.0);
0
u = ( 1 – α 0 )n0 α0 n0
H0 N_lambda = ((~(H0)N) || zero ) & (zero || (~(H0)N));
û 0
H0 N_bar_0 = ( ((H0)N[0])*((1.0-alpha[0])*n_0) + ((H0)N[1])*(alpha[1]*n_1) ), = cû
N_bar_1 = ( ((H0)N[0])*(alpha[0]*n_0) + ((H0)N[1])*((1.0-alpha[1])*n_1) ), α1 n1 ( 1 – α 1 )n 1 û 1
N_u_bar = ( N_bar_0[0] | zero | N_bar_1[0] | zero )& 0

∫ NλT Nu
( zero | N_bar_0[1] | zero | N_bar_1[1] );
if(gdc.type() == interface_elastic_foundation_gamma_h_type) Q = dΓ ; where N ≡ Nc
u
stiff &= -(((~N_lambda)*N_u_bar)|d_l); Γc
else stiff &= (((~N_lambda)*N_u_bar)|d_l); Q0 {Γc-Γelastic founcation}
}
} Q1 {Γc-Γrigid punch}
Element_Formulation* Element_Formulation::type_list = 0;
Element_Type_Register element_type_register_instance;
static Elastic_Contact_Q4 elastic_contact_q4_instance(element_type_register_instance);
#include "\vs\ex\fe\contact_frictionless_sled_on_elastic_foundation\omega_h_n.cpp"
int main() {
C0 K_0, Q_0, K_1, Q_1, f_0, f_i, f_1, lambda, u_0, u_1;
interface_oh.contact_searching_algorithm(interface_oh, elastic_foundation_gamma_h_gd,
rigid_sled_gamma_h_gd, lambda_h, interface_gh, 0.05);
Matrix_Representation mr_K_0(elastic_foundation_gd);
Matrix_Representation mr_K_1(rigid_sled_gd);
Matrix_Representation_Couple mrc_Q_0(interface_elastic_foundation_gdc, 0, 0,
&(mr_K_0.rhs()) );
Matrix_Representation_Couple mrc_Q_1(interface_rigid_sled_gdc, 0, &(mrc_Q_0.rhs()),
&(mr_K_1.rhs()) );
for(int i = 0; i < 9; i++) {
mr_K_0.assembly(); K_0 &= (C0)(mr_K_0.lhs()); K0
mr_K_1.assembly(); K_1 &= (C0)(mr_K_1.lhs()); K1
mrc_Q_0.assembly(); Q_0 &= (C0)(mrc_Q_0.lhs()); Q0
mrc_Q_1.assembly(); Q_1 &= (C0)(mrc_Q_1.lhs());
f_0 &= (C0)(mr_K_0.rhs()); f_1 &= (C0)(mr_K_1.rhs()); f_i &= (C0)(mrc_Q_0.rhs()); Q1
Cholesky dK0(K_0); Cholesky dK1(K_1); f0, f1, fi
C0 K0_inv = dK0.inverse(); C0 QK_inv_0 = Q_0*K0_inv; C0 QKQ_0 = QK_inv_0*(~Q_0); K0-1, Q0 K0-1 Q0T
C0 K1_inv = dK1.inverse(); C0 QK_inv_1 = Q_1*K1_inv; C0 QKQ_1 = QK_inv_1*(~Q_1);
Cholesky mdQKQ(QKQ_0+QKQ_1, 1.e-12); K1-1, Q1 K1-1 Q1T
lambda &= mdQKQ * (QK_inv_0*f_0+QK_inv_1*f_1-f_i); (Q0 K0-1 Q0T + K1-1, Q1 K1-1 Q1T)-1
u_0 &= dK0*(f_0-(~Q_0)*lambda); u_1 &= dK1*(f_1-(~Q_1)*lambda); λ̂=(Q0 K0-1 Q0T + K1-1, Q1 K1-1 Q1T)-1
lambda_h = lambda; lambda_h = interface_gd.gh_on_gamma_h();
u_0_h = u_0; u_0_h = elastic_foundation_gd.gh_on_gamma_h(); (Q0 K0-1 f0 + Q1K1-1 f1 - fi)
u_1_h = u_1; u_1_h = rigid_sled_gd.gh_on_gamma_h(); û 0 = K0-1 (f0 - Q0Tλ̂)
cout << "contact forces:" << endl << lambda_h << "elastic foundation displacement:" << û 1 = K1-1 (f1 - Q1Tλ̂)
endl << u_0_h << "rigid sled displacement:" << endl << u_1_h;
if(i < 8) {
for(int j = 8; j < 12; j++) {
int node_order = rigid_sled_gd.gh_on_gamma_h().node_order(j);
rigid_sled_gd.gh_on_gamma_h().gh_array()[node_order][0] = ((double)(i+1));
}
cout << "step " << i << " :" << endl;
rigid_sled_gd.u_h() = rigid_sled_gd.gh_on_gamma_h();
interface_oh.contact_searching_algorithm(interface_oh, elastic_foundation_gamma_h_gd,
rigid_sled_gamma_h_gd, lambda_h, interface_gh,
mr_K_0, mr_K_1, mrc_Q_0, mrc_Q_1, 0.05);
}
}
return 0;
}

Listing 5•18 Frictionless sled on elastic foundation (project: “contact_frictionless_sled_on_elastic_


foundation.dsp” in project workspace: “fe.dsw”).

Workbook of Applications in VectorSpace C++ Library 573


Chapter 5 Advanced Finite Element Methods
5.3 Elastoplasticity
The project for the elastoplastic computation is “elastoplasticity” in the project workspace file “fe.dsw”.
In the elastoplastic problem, the stress-strain relation is path dependent. The radial return mapping algo-
rithm is introduced to accomplish the stress-strain path integration scheme. Without a special treatment, the tan-
gent moduli used for this problem will result in very slow convergent rate. Therefore, the consistent tangent
moduli, which is derived consistently with the radial return method, is used to retain the quadratic convergence
rate of the classic Newton-Raphson method. The implementation of the small deformation elastoplastic problem
in this workbook is entirely based on Simo and Taylor[1985]1.

5.3.1 Basic Theory

Non-linear Problem with Incremental Loading and Global Newton-Raphson Iteration


For general introduction on non-linear problem, we refer to Chapter 2 in this workbook. See Zienkiewicz and
Taylor[1991]2, for a more specific treatment in the context of finite element method.
An outer-loop of for control statement in the incremental loading algorithm is to advance the time step.
Then, within each time step, an inner-loop of for control statement apply the “global” Newton-Raphson iteration
to achieve a convergent solution. This solution is to be used as the initial value for the next time step. We denote
two consecutive time steps as tn and tn+1, with tn stands for the previous time step and tn+1 stands for the current
time step. The unknown displacement û in+1 is at the current time step tn+1 upon the i-th Newton-Raphson itera-
tion. The initial value of the current time step, û 0n+1, is set to the converged solution at the previous time step tn
as û 0n+1 = û n. Define the residual vector of the problem R( û i+1) at tn+1 as

∂R
R ( û i + 1 ) = R ( û i + δ û i ) ≅ R ( û i ) + ------- δ û i = 0
∂û û i Eq. 5•224

where û i+1 = û i + δ û i. The solution of the non-linear problem is obtained by setting the residual vector to van-
ish; i.e., find the root for the equation R(ui+1) = 0. Notice that the approximation step in Eq. 5•224 is done by the
Taylor expansion up to the first-order. From this approximated equation, the incremental displacement δui is the
solution of the simultaneous linear algebraic equations

∂R  – 1
δ û i = – -------  R ( û i ) ≡ K –T1 R ( û i ) Eq. 5•225
∂û û i 
∂R 
where K T ≡ –-------
∂û  is the tangent stiffness matrix.
 û 
i

1. Simo and Taylor, 1985, “Consistent tangent operators for rate-independent elastoplasticity” in Computer
Methods in Applied Mechanics and Engineering, v. 48, p.101-118
2. Chapter 7 in Zienkiewicz and Taylor, 1991, “The finite element method”, 4th ed. v. 2, McGraw-Hill, UK.

574 Workbook of Applications in VectorSpace C++ Library


Elastoplasticity
Hence, we need to define the quantities in the right-hand-side of Eq. 5•225 to compute δui. In finite element
method, the residual is the external load vector (Fext) subtracts the internal force divergence term expressed as

R = F ext – ∑ ∫ B T σ d Ω Eq. 5•226


e Ωe

Hence, the tangent stiffness matrix KT in Eq. 5•225 is obtained by taking the derivatives of R in Eq. 5•226 with
respect to the solution u as

∂R  ∂σ  T
-------
∂û
= –∑ ∫Ω BT ------
∂û
-  B dΩ = – ∑ ∫Ω B DT B dΩ ≡ – K T Eq. 5•227
û i
e e ûi e e

where, DT ≡ ∂ Ψ ⁄ ∂ ε is the tangent moduli, with the Cauchy stress σ ≡ ∂Ψ ⁄ ∂ε and where Ψ is the free energy
2 2

function. With KT defined, the incremental displacement δui is readily obtained by solving δ û i = KT–1 R ( ûi ) .
Note that the tangent moduli is the second derivatives with respect to free energy function. The minimization is
–1
with respect to free energy Ψ, and the equation δ û i = KT R ( û i ) is equivalent to the Newton’s Formula for opti-
mization of a function f(x) as δx = - df / d 2f (obtained from second-order Taylor expansion), with x += δx for

solution update.

Implementation of Incremental Loading and Global Newton-Raphson Iteration


The Program Listing 5•19 demonstrates the implementation of the incremental loading algorithm and the glo-
bal Newton-Raphson iteration of the non-linear problem described in the above. However, most intensive com-
putation is done at the element level. We defer the element level details to Section 5.3.2. The grand structure of
the algorithm is a nested loop with an outer incremental loading loop using a for statement, and an inner loop of
global Newton-Raphson iteration using a do-while statement.

Incremental Loading Loop: At the outer incremental loop, enclosed in the “for” statement, the incremental load-
ing is added to the boundary at the beginning of each time step; i.e., at the first iteration (i = 0), δu0 is set to the
incremental boundary condition δu0 of the time step. This quantity is then given to the element level for the com-
putation of the residual (R). The residual is to be modified by the reaction caused by the incremental boundary
condition as R -= KTδu0.
For the stress-strain relation in plasticity, which is path-dependent, we also need to introduce total incremen-
tal displacement ∆ û = ∑ δ û (with k = 0,1, 2, ..., i). ∆ui is the “total increment”; the summation of all incre-
i k

mental displacement from the first iteration up to the current iteration within the current time step. This quantity
is updated at the global level in the “main()” function, and is referred to in the element level.

Global Newton-Raphson Iteration: The inner loop is the global Newton-Raphson iteration inside the do-while
control statement. The heart of the algorithm is the member function “Matrix_Representation::assembly()” which
performs three heavy-duty steps to be coded in the Element_Formulation. These three steps are (1) apply exter-
nal load (Fext in Eq. 5•226) and subtract the internal force divergence term as in Eq. 5•226. The details of these
three steps in the element level is in Section 5.3.2.

Workbook of Applications in VectorSpace C++ Library 575


Chapter 5 Advanced Finite Element Methods

static ofstream ofs("temp.cpp", ios::out | ios::trunc);


int main() {
Matrix_Representation mr(gd);
delta_uh.matrix_representation() = &mr;
d_uh.matrix_representation() = &mr; total increment ∆u,
C0 delta_u,
du;
and increment δu
for(int i = 0; i < 10; i++) { Incremental loading loop
int count = 0;
const int MAX_ITERATION_NO = 50; const double EPSILON = 1.e-9;
double residual_norm, energy, energy_0;
if(i != 0) { ∆ û = 0.0, if i == 0
delta_u = 0.0; delta_uh = delta_u; length of ∆ û not known yet
}
for(int j = 86; j <= 92; j++) {
incremental boundary condition
d_gh[j][1] = 0.01; delta_gh[j][1] = 0.0; δu = 0.01
} ∆u = 0.0
d_uh = d_gh; delta_uh = delta_gh; uh = gh;
do {
ofs << (i+1) << "-time step, " << (++count) << "-iteration, " << endl; Global Newton-Raphson iteration
mr.assembly(); form KT, and Fext
du = ((C0)(mr.rhs())) / ((C0)(mr.lhs()));
if(count == 1) {
R -= KT δu0
for(int j = 86; j <= 92; j++) { u += δu
gh[j][1] += d_gh[j][1]; delta_gh[j][1] = d_gh[j][1]; d_gh[j][1] = 0.0; ∆u =δu
}
uh = gh; delta_uh = delta_gh; d_uh = d_gh;
δu = 0.0 (reset)
}
if(i == 0 && count == 1) delta_u &= C0(du.length(), (double*)0); ∆ û +=δ û
delta_uh = delta_u += du;
û += δ û
uh += du;
residual_norm = norm((C0)(mr.rhs())); residual norm = R • R
energy = fabs( (double)(du * ((C0)(mr.rhs())) ) ); Energy norm = δ û • R
if(count == 1) energy_0 = energy;
ofs << " residual norm: " << residual_norm <<" energy: " << energy << endl;
cout << " energy: " << energy << endl;
(C0)(mr.lhs()) = 0.0; (C0)(mr.rhs()) = 0.0;
} while(energy > (EPSILON*energy_0) &&
(count < MAX_ITERATION_NO) );
if(count == MAX_ITERATION_NO) {
ofs << " Warning: global Newton iteration failed!" << endl <<
" current solution: " << endl << uh;
return 0;
}
for(int j = 0; j < oh.total_element_no(); j++) { Update history data
Omega_eh *elem = &(oh(j));
((Non_Linear_Omega_eh*) elem)->update_history_data();
(.)n = (.)n+1
}
ofs << endl << (i+1) << "-time step solution: " << endl;
ofs << uh << endl;
}
ofs.close();
return 0;
}

Listing 5•19 Incremental loading algorithm and the global Newton-Raphson iteration of the non-linear
problem

576 Workbook of Applications in VectorSpace C++ Library


Elastoplasticity
Next, δ û is solved for by a matrix solver embedded in the member function “C0::operator / ()”, and the
update procedure includes updating (1) the total incremental displacement ∆ û += δ û , and (2) total displacement
û += δ û . The residual norm = R • R , and the energy norm = δ û • R , is reported to an output file. The energy
norm is used as the convergence criterion to terminate the Newton-Raphson iteration. After the first Newton-
Raphson iteration, the incremental boundary condition δu 0 is assigned to the total incremental boundary condi-
tion ∆u 1 = δu 0, and then set δu i = 0 for all i > 0, so no further subtraction of reaction of R -= KTδu i caused by
incremental boundary conditions is repeated unnecessarily by the default behavior of “fe.lib”.

5.3.2 Radial Return Mapping Algorithm


For the fundamentals of plasticity theory, we refer to two popular graduate texts by Fung[1965]1, and Malv-
ern[1969]2.

Theory of Elastoplasticity
Following the development in Simo and Taylor[1985]3 for the infinitesimal case, the deviatoric stress (s) and
deviatoric strain (e) tensors are defined as

s = σ – 1--3- tr ( σ )1 , and e = ε – 1--3- tr ( ε )1 Eq. 5•228

with second-order unit tensor, 1, and the trace operator, tr( ). The von Mises yield criterion is

f ( ξ, α, κ ) ≡ ξ – 2- κ ( e p ) ≤ 0 , Eq. 5•229
3

The plastic strain is integrated as


t
2
ep = -
3
d p ( τ ) dτ , Eq. 5•230
0

with ξ = s – α . where α is the back stress for the kinematic hardening. The kinematic hardening function κ(e p),
depends on plastic strain ep, is for the isotropic hardening, where dp is the plastic strain rate. The consistency
condition, or the normality rule, in the theory of plasticity requires that stress remains on the yield surface during
the plastic deformation. This is equivalent to assuming maximum plastic dissipation for a quasi-equilibrium pro-
cess in the context of irreversible thermodynamics. From the consistency condition, we get the deviatoric stress-
strain rate constitutive equation as

1. “Foundations of solid mechanics”, Prentice-Hall, Inc.


2. “Introduction to the mechanics of a continuous medium”, Prentice-Hall, Inc.
3. “Consistent tangent operators for rate-independent elastoplasticity” in Computer Methods in Applied
Mechanics and Engineering, v. 48, p.101-118

Workbook of Applications in VectorSpace C++ Library 577


Chapter 5 Advanced Finite Element Methods
·
s· = 2µ [ I – γ ( n̂ ⊗ n̂ ) ] : e , Eq. 5•231

where plastic consistency parameter γ is defined as

1
γ ≡ d p = -----------------------------
κ' + H’α
Eq. 5•232
1 + 3µ -------------------

µ is the shear modulus. n̂ = ξ TRn + 1 ⁄ ξ nTR+ 1 , where superscript “TR” denotes the trial elastic state. The primes
in κ and Hα denotes the first-derivative with respect to ep. The so-called continuum elastoplastic tangent moduli
·
ae p for the constitutive law σ = a : ε· in rate form is thus obtained as
ep

a ep = K ( 1 ⊗ 1 ) + 2µ I – 1--3- ( 1 ⊗ 1 ) – 2µ γ ( n̂ ⊗ n̂ ) , Eq. 5•233

where K is the bulk modulus.

Stress-Strain Path Integration Algorithm—Closest-Point-Projection


Elastoplastic constitutive equation as manifested in the tangent moduli of Eq. 5•233 is path-dependent. A
stress-strain path can be constructed with an integration scheme for an ordinary differential equation of the form
·
σ = a ep: ε· . Along the course of integration, the closest-point-projection method (Figure 5•15) is used to
enforce the von Mises yield criterion (Eq. 5•229) as an inequality constraint. This is to bring back the stress
state, sn+1TR, to the yield surface at sn+1 when the constraint is violated. In the case of von Mises yield criterion
with associative flow law, the closest-point-projection reduces to the radial return mapping algorithm. We note
by passing that more than one method is possible to accomplish the radial return mapping algorithm. In the
“Implementation 1” of the finite deformation elastoplasticity, in section , the alternative cutting-plane method is
used, which makes the conceptual formulation to satisfy the objectivity principle for the finite deformation prob-
lem a lot easier than the closest-point-projection method, where we need not introduce the covariant operations
in the algorithm.
The closest-point-projection method is consist of two primary phases: elastic-predictor and plastic-correc-
tor.

elastic predictor
sTRn+1
plastic corrector

sn sn+1
Yield Surface

Figure 5•15 Closet-poinst-projection stress correction, when the constraint is violated.

578 Workbook of Applications in VectorSpace C++ Library


Elastoplasticity
Elastic-Predictor: First we assume there is no plastic deformation occurs. All incremental deviatoric strain ∆en+1
is totally allocated to the deviatoric elastic response as (∆en+1elastic)0 = ∆en+1 and (∆en+1plastic)0 = 0 where super-
script “e” denotes the elastic part, and superscript “p” denotes the plastic part, so,

s nTR+ 1 = s n + 2µ ∆ e n + 1 Eq. 5•234

where sn is obtained from stored data of the last time step, and superscript “TR” denotes the trial elastic state. We
also have

ξnTR+ 1 = s n + 1 – α n , and n̂ =
TR
ξ TR n + 1 ⁄ ξnTR+ 1 Eq. 5•235

where back stress αn is also obtained from stored data of the last time step.

Plastic-Corrector: The consistency condition requires the state of stress remains on yield surface during plastic
deformation. This leads to

φ ( γ ∆t ) ≡ – 2-3 κ ( e np + 1 ) + ξnTR+ 1 – 2µ γ ∆t + 2- ∆H α = 0 ,
3
Eq. 5•236

where the last two terms 2µγ∆t is the magnitude of plastic relaxation stress and 2 ⁄ 3 ∆Hα is the magnitude of
translation on “π-plane” of yield surface due to kinematic hardening with the “π-plane” proportional length-fac-
tor 2 ⁄ 3 considered. ep is integrated as

e np (+k 1+ 1 ) = e np (+k )1 + 2- ( γ ∆t ) Eq. 5•237


3

Define λ = γ∆t, the local Newton-Raphson iteration to enforce the consistency condition provides the Newton’s
formula for numerical root-finding on Eq. 5•236 as

δλ = – φ ( λ ) ⁄ d φ ( λ ) , with the solution update as λ += δλ Eq. 5•238

the back stress and deviatoric stress are integrated for updating history data according to

αn + 1 = αn + -2 ∆H α ( enp + 1 ) n̂ , and s n + 1 =
3
αn + 1 + -2 κ ( enp + 1 ) n̂
3
Eq. 5•239

then, the Cauchy stress is updated by

σn+1 = sn+1 + K tr(ε) 1. Eq. 5•240

Workbook of Applications in VectorSpace C++ Library 579


Chapter 5 Advanced Finite Element Methods
Consistent Tangent Moduli
It turns out the convergence rate for the global Newton-Raphson iteration with the continuum tangent moduli
in Eq. 5•233 is unbearably slow. This is because the incremental loading always has a finite increment, accord-
ingly the stress-strain path integration step in the radial return mapping is discrete in nature. If we use the contin-
uum tangent moduli for the stress integration method. The tangent moduli is implicitly taken as constant during
tn to tn+1. Therefore, error is introduced and the iterative method will lose its most desirable quadratic conver-
gent rate from the Newton-Raphson method. Simo and Taylor[1985]1 was able to derive, consistent with the
radial return mapping method, a tangent moduli which retains the quadratic convergent rate. This discrete ver-
sion of tangent moduli is the consistent tangent moduli;

a ep = K ( 1 ⊗ 1 ) + 2 µβ I – 1--3- ( 1 ⊗ 1 ) – 2 µ γ ( n̂ ⊗ n̂ ) Eq. 5•241

where,

[ κ n + 1 + ∆H α ] 1
β ≡ 2-3 -----------------------------------
- , and γ ≡ -------------------------------------------
[ κ' + H’ ]
- – (1 – β) = γ – (1 – β)
ξ TR
n+1 α n+1
Eq. 5•242
1 + ----------------------------------

We can check this formula by investigating that whether in the limit of infinitesimal incremental loading step,
the consistent tangent moduli does degenerate to the continuum tangent moduli; i.e., to have β = 1 , thus
γ = γ – ( 1 – β ) = γ . This follows immediately from the definitions of β and γ in Eq. 5•242.

5.3.3 Object-Oriented Modeling for Stress-Strain Integration Scheme


The incremental stress-strain path is an integration scheme that is consist of updating variables from {sn, αn,
e pn} at tn to {sn+1, αn+1, e pn+1} at tn+1. These data are referred to as “history data”. The deviatoric stress (s), back
stress (α), and plastic strain (ep) are quantities associated with each element, or more specifically with each
quadrature point of the element. Therefore, one can model these quantities as H0 type private data members
associated with an element (see Program Listing 5•20). Therefore, we need to extend the capacity of standard
element class Omega_eh in “fe.lib” to have these quantities. Applying the concept of programming by specifi-
cation, in the mechanisms provided by C++, this amounts to define a public derived class using Omega_eh as
its base class. This way the interface and implementation of the base class Omega_eh are inherited by the public
derived class Non_Linear_Omega_eh. This derived class becomes a more specific use of the more general base
class it is derived from. At the end of each time step, Omega_eh::update_history_data( ) is called by main()
function in Program Listing 5•19.
In addition, several global level variables need to be linked to the element level. These include various dis-
placement variables û , ∆ û , δ û . These links are coded in the private member function __initialization() of the
public derived Element_Formulation class “Elastoplastic_B_bar_Q41”. This member function will be called
in the definition of the constructor “Elastoplastic_B_bar_Q41::Elastoplastic_B_bar_Q41(...)”.

1. “Consistent tangent operators for rate-independent elastoplasticity” in Computer Methods in Applied


Mechanics and Engineering, v. 48, p.101-118

580 Workbook of Applications in VectorSpace C++ Library


Elastoplasticity

Implementation of Radial Return Mapping with Consistent Tangent Moduli


The stress-strain integration is implemented in Program Listing 5•20. The derived class Elastoplastic_B_bar
_Q41 from the base class Element_Formulation is implemented with the elastoplastic computation on top of the
B_bar_Q41. We refer to that section for discussion on the details of the B-method (page 502).
Τhe basic steps of the radial return mapping is an elastic-predictor followed by a plastic-corrector. In the elas-
tic predictor, the trial state of elastic deviatoric stress is

sTRn+1 = sn + 2µ∆e Eq. 5•243

where sn is obtained from history data. In order to get the trial deviatoric stress, we first need to compute the ele-
ment total incremental displacement (∆ û e = ∆ û +∆u, free plus fixed degree of freedom), and then total incre-
mental deviatoric strain (∆e = Bdev ∆ û e). Then, we can get to the 2µ∆e term in the elastic predictor (Eq. 5•243).
Note that Bdev is the deviatoric part of the B matrix. This trial state of elastic deviatoric stress (sTRn+1) may or
may not shoot out of the yield surface. This is inspected by checking the yield radius (R) against the norm of
ξTRn+1, where ξTRn+1 = sTRn+1 - αn. The back stress αn is obtained from the history data. If the norm of ξTRn+1 is
smaller than or equal to R, the state of stress remains inside the yield surface in the elastic state, we have sn+1 =
sTRn+1. Otherwise, the constraint is violated. The state of the stress is outside the yield surface and the plastic-cor-
rector follows.
The plastic-corrector is to return to the state of stress back to the yield surface—the consistency condition;
i.e., to satisfy φ ( λ ) ≡ – 2-3 κ ( e np + 1 ) + ξnTR+ 1 – 2 µλ + 2- ∆H α = 0 (with λ = γ∆t) in Eq. 5•236. With the Vector-
3
Space C++ Library, root-finding is very simple. Just declare C1 type variables and function for this equation. The
Newton iteration is furnished by the formula δ λ = – φ ( λ ) ⁄ d φ ( λ ) , with the solution updated by λ += δλ. The
C++ codes parallel the mathematical expression exactly. After we obtain the converged result form the local
Newton iteration, one can update s, α, and ep according to Eq. 5•237 and Eq. 5•239.

The consistent tangent moduli (Eq. 5•241) is readily obtained with all these parameters available. The
Cauchy stress is computed with averaged volumetric strain εv = Bvol û e = tr(ε)1, and then plug the volumetric
strain into the second term in the right-hand-side of Eq. 5•240. The rest of the Program Listing 5•20is the (tan-
gent) stiffness, and the residual. The stiffness is computed as defined in
T ep
∫B a B dΩ
Ωe
with B in place of the B for the B-formulation. The residual only needed to be computed for the non-linear prob-
lem, where we need to subtract the internal stress divergence term out of the “force” vector as

∫ B σ dΩ
T

Ωe
The implementation of the radial return mapping method is shown is Program Listing 5•20.

Workbook of Applications in VectorSpace C++ Library 581


Chapter 5 Advanced Finite Element Methods

static Quadrature qp(2, 4); class Non_Linear_Omega_eh


class Non_Linear_Omega_eh : public Omega_eh {
H0 the_s, the_s_n,
deviatoric stress {sx, sy, sxy}T
the_ep, the_ep_n, total plastic strain
the_alpha, the_alpha_n; back stress
void __initialization();
public:
Non_Linear_Omega_eh(int en = 0, int etn = 0, int mtn = 0, int nen = 0, int* ena = 0) :
Omega_eh(en, etn, mtn, nen, ena) { __initialization(); }
H0& s_n() { return the_s_n; }
H0& ep_n() { return the_ep_n; }
H0& alpha_n() { return the_alpha_n; }
H0& s() { return the_s; }
H0& ep() { return the_ep; }
H0& alpha() { return the_alpha; }
void update_history_data();
};
void Non_Linear_Omega_eh::__initialization() {
the_s &= H0(3, (double*)0, qp);
the_ep &= H0((double*)0, qp);
the_alpha &= H0(3, (double*)0, qp);
the_s_n &= H0(3, (double*)0, qp);
the_ep_n &= H0((double*)0, qp);
the_alpha_n &= H0(3, (double*)0, qp);
} update function at the end of each time
void Non_Linear_Omega_eh::update_history_data() {
the_s_n = the_s;
step. (.)n = (.)n+1
the_ep_n = the_ep;
the_alpha_n = the_alpha;
}
class Elastoplastic_B_bar_Q41 : public Element_Formulation {
C0 delta_ul;
H0 s, s_old, ep, ep_old, alpha, alpha_old;
void __initialization(int);
public:
Elastoplastic_B_bar_Q41(Element_Type_Register a) : Element_Formulation(a) {}
Element_Formulation *make(int, Global_Discretization&);
Elastoplastic_B_bar_Q41(int, Global_Discretization&);
};
void Elastoplastic_B_bar_Q41::__initialization(int en) {
delta_ul &= gd_delta_uh.element_free_variable(en) + gd_delta_uh.element_fixed_variable(en); ∆ û e = ∆ û + ∆ û
ul += gl;
û e = û + u
gl = gd_d_uh.element_fixed_variable(en);
Omega_eh *elem = &(oh(en)); δ û e = δu
s &= ((Non_Linear_Omega_eh*) elem)->s(); (.)n and (.)n+1
ep &= ((Non_Linear_Omega_eh*) elem)->ep();
alpha &= ((Non_Linear_Omega_eh*) elem)->alpha();
s_old &= ((Non_Linear_Omega_eh*) elem)->s_old();
ep_old &= ((Non_Linear_Omega_eh*) elem)->ep_old();
alpha_old &= ((Non_Linear_Omega_eh*) elem)->alpha_old();
}
Element_Formulation* Elastoplastic_B_bar_Q41::make(int en, Global_Discretization& gd) {
return new Elastoplastic_B_bar_Q41(en,gd);
}

Listing 5•20 History data and implementation of class Non_Linear_Omega_eh.

582 Workbook of Applications in VectorSpace C++ Library


Elastoplasticity

static const double i_dev[3][3] = {


I_dev
{(1.0-1.0/3.0), -1.0/3.0, 0.0},
{-1.0/3.0, (1.0-1.0/3.0),0.0},
{0.0, 0.0, 1.0}
};
C0 I_dev = MATRIX("int, int, const double*", 3, 3, i_dev[0]);
static double one[3] = {1.0, 1.0, 0.0};
One = VECTOR("int, const double*", 3, one);
static double identity_mu[3][3] = { {1.0, 0.0, 0.0} , {0.0, 1.0, 0.0}, {0.0, 0.0, 0.5} };
I_mu
C0 I_mu = MATRIX("int, int, const double*", 3, 3, identity_mu[0]);
Elastoplastic_B_bar_Q41::Elastoplastic_B_bar_Q41(int en, Global_Discretization& gd) : class Elastoplastic_B_bar_Q41
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.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, 2/*nsd*/, Nx), wx, wy, B, B_bar;
wx &= w_x[0][0]; wy &= w_x[0][1]; C0 zero(0.0);
B &= (~wx || zero) &
(zero || ~wy ) &
(~wy || ~wx );
H0 N_vp(qp); N_vp = 1.0;
H0 mN_vp = N_vp & N_vp & zero;
C0 C = ((~mN_vp)*B) | dv, E = (N_vp) | dv, W = C/E; Bvol
H0 B_bar_vol = (1.0/3.0) * (mN_vp % W[0]), Bdev
B_dev = I_dev * B;
B = Bdev + Bvol
B_bar &= B_dev + B_bar_vol;
__initialization(en); retreive displacement variables u, δu,
H0 delta_e = INTEGRABLE_VECTOR("int, Quadrature", 3, qp); delta_e = 0.0; ∆u, and history data s, α, ep
for(int i = 0; i < nen; i++)
Elastic Predictor begins
delta_e += B_dev(i*ndf)*delta_ul[i*ndf] + B_dev(i*ndf+1)*delta_ul[i*ndf+1];
s = s_n + delta_e*(2.0*mu_*I_mu); ∆ û e = ∆ û +∆u
H0 ZAI = s - alpha_n, ZAI_norm(qp); ∆e = Bdev ∆ û e
#define qv quadrature_point_value
sTRn+1 = sn + 2µ∆e
ξTRn+1 = sTRn+1 - αn
for(int i = 0; i < qp.no_of_quadrature_point(); i++) {
C0 ZAI_0_q, ZAI_1_q, ZAI_2_q;
ZAI_0_q &= ZAI[0].qv(i); ZAI_1_q &= ZAI[1].qv(i); ZAI_2_q &= ZAI[2].qv(i);
ZAI_norm.qv(i) = sqrt((double)(ZAI_0_q.pow(2)+ZAI_1_q.pow(2)+2.0*ZAI_2_q.pow(2))); ξ = ξ 2 00 + ξ 2 11 + 2 ξ 2 01
}
H0 KAPA(qp); KAPA = Y_infinity - (Y_infinity - Y_0) * exp(-iota*ep);
H0 radius = sqrt(2.0/3.0)*KAPA, κ =Y ∞ – ( Y ∞ – Y 0 )e ( –ιe p )
yield_ratio = ZAI_norm/radius;
H0 Dt(3, 3, (double*)0, qp), lambda(qp), 2
sigma = INTEGRABLE_VECTOR("int, Quadrature", 3, qp); R = --- κ ( e p )
3
for(int i = 0; i < qp.no_of_quadrature_point(); i++) {
C0 s_q, ep_q, ep_n_q, alpha_q, alpha_n_q, ZAI_q, ZAI_norm_q, sigma_q, Dt_q,
aep; tangent moduli
σ; Cauchy stress
B_bar_q, B_vol_q, radius_q, lambda_q, yield_ratio_q;
s_q &= s.qv(i); ep_q &= ep.qv(i); ep_n_q = ep_n.qv(i);
alpha_q &= alpha.qv(i); alpha_n_q = alpha_n.qv(i); λ = γ∆t
ZAI_q &= ZAI.qv(i); ZAI_norm_q &= ZAI_norm.qv(i);
loop over each quadrature point
sigma_q &= sigma.qv(i); Dt_q &= Dt.qv(i);
B_bar_q = B_bar.qv(i); B_bar_vol_q = B_bar_vol.qv(i); alias; subscript “q” denotes quadrature
radius_q &= radius.qv(i); lambda_q &= lambda.qv(i); point index
yield_ratio_q &= yield_ratio.qv(i);

Workbook of Applications in VectorSpace C++ Library 583


Chapter 5 Advanced Finite Element Methods
if(ZAI_norm_q > radius_q) { chech if the elastic predictor shoot out
ofs << "***Plastic state: element # " << en << ", quadrature point # " << i of the yield surface
<< ", yield ratio: " << yield_ratio_q << endl;
double EPSILON = 1.e-20, d_lambda_norm, d_lambda_norm_0; Plastic Corrector begins
int MAX_ITERATION_NO = 50, count = 0; root-finding with
C0 d_lambda(0.0); C1 phi, LAMBDA(0.0); local Newton iteration on each quadra-
do {
C1 EP = ep_n_q+sqrt(2.0/3.0)*LAMBDA, ture point , λ0 = 0
KAPA = Y_infinity - (Y_infinity - Y_0)*exp(-iota*EP), e np + 1 = e np + -2 λ
3
RADIUS = sqrt(2.0/3.0)*KAPA,
delta_H_alpha = sqrt(2.0/3.0)*d_H_alpha*LAMBDA;
phi &= -RADIUS + = Y ∞ – ( Y ∞ – Y 0 )e ( –ιe
ZAI_norm_q - (2.0*mu_*(LAMBDA) + sqrt(2.0/3.0)*delta_H_alpha);
d_lambda = - ((C0)phi)/d(phi); φ ( λ ) ≡ – 2-3 κ ( e np + 1 ) + ξnTR+ 1
((C0) LAMBDA) += d_lambda;
d_lambda_norm = norm(d_lambda);
if(count == 0) d_lambda_norm_0 = d_lambda_norm; – 2µγ∆t + 2- ∆H α = 0
3
} while(d_lambda_norm>(d_lambda_norm_0*EPSILON) &&
count++<MAX_ITERATION_NO);
lambda_q = (C0) LAMBDA; δ λ = – φ ( λ ) ⁄ d φ ( λ ) ; root finding by
if(count == MAX_ITERATION_NO) { local Newton iteration with update λ +=
ofs << "Warning: local Newton iteration failed to converge!" << endl << δλ
" element # " << en << ", quadrature point # " << i << endl;
} update history data
ep_q = ep_n_q + sqrt(2.0/3.0)*lambda_q;
C0 n = ZAI_q/ZAI_norm_q; e np (+i 1+ 1 ) = e np (+i )1 + 2- ( λ )
double delta_H_alpha = sqrt(2.0/3.0)*d_H_alpha*(double)lambda_q; 3
alpha_q = alpha_n_q + sqrt(2.0/3.0)*delta_H_alpha*n;
C1 EP((double)ep_q), KAPA = Y_infinity - (Y_infinity - Y_0)*exp(-iota*EP); n̂ = ξ TRn + 1 ⁄ ξnTR+ 1
double factor = 1.0+1.e-8;
s_q = alpha_q + (sqrt(2.0/3.0)*((double)((C0)KAPA))*factor)*n; // (3.3); (iv-2),
double gamma = 1.0/(1.0+((((double)d(KAPA))+d_H_alpha)/3.0/mu_)); αn + 1 = αn + -2 ∆H α ( e np + 1 ) n̂
3
#if defined(__TEST_CONSISTENT_TANGENT_OPERATOR)
double beta=sqrt(2.0/3.0)*((double)((C0)KAPA)+delta_H_alpha)/(double)ZAI_norm_q, sn + 1 = αn + 1 + -2 κ ( e np + 1 ) n̂
3
gamma_bar = gamma - (1.0-beta);
Dt_q = lambda_*(One%One) +
2.0 * mu_ * (beta * I_mu - gamma_bar * (n%n)); 1
#else γ ≡ d p = ----------------------------
1 + κ’ + H’α
-
Dt_q = lambda_*(One%One) + 2.0 * mu_ * (I_mu - gamma * (n%n)); -------------------
#endif 3µ
} else {
[ κ n + 1 + ∆H α ] .
Dt_q = lambda_*(One%One) + 2.0*mu_*I_mu;
ofs << " Elastic state: element # " << en << ", quadrature point # " << i
β ≡ 2-3 -----------------------------------
- , γ = γ −(1−β)
<< ", yield ratio: " << yield_ratio_q << endl; ξ nTR+ 1
}
C0 eps_v(3, (double*)0);

εv = Bvol û e = tr(ε)1
for(int i = 0; i < nen; i++)
eps_v += B_bar_vol_q(i*ndf)*ul[i*ndf] + B_bar_vol_q(i*ndf+1)*ul[i*ndf+1];
eps_v *= 3.0; σn+1 = sn+1 + K Ttr(ε) 1
stiffness += ∫ B a B dΩ
sigma_q = s_q + K_*eps_v; ep
}
stiff &= ((~B_bar)*Dt*B_bar)|dv; Ωe

∫ B σ dΩ
T
force &= -((~B_bar)*sigma)|dv; force -=
} Ωe

Listing 5•21 Radial return mapping alogrithm for the elastoplastic element.

584 Workbook of Applications in VectorSpace C++ Library


Elastoplasticity

δu = 0.01m

18 m

5m

5m
Figure 5•16 Perforated strip under uniaxial extension.
5.3.4 Perforated Strip under Uni-axial Extension
A benchmark test problem for elastoplasticity is shown in Figure 5•16. For the experimental results see Theo-
caris and Marketos[1964]1. The geometry and its boundary conditions are described below. Only a quadrant of
the problem is modeled due to the symmetry of the problem. Plain strain is assumed. The material properties are
Young’s Modulus E = 70 MPa, Poisson ratio ν = 0.2. The isotropic hardening law is given specifically as

κ = Y ∞ – ( Y ∞ – Y 0 )e ( –ιep ) Eq. 5•244

where, Y0 = Y∞ = 0.243 MPa, ι = 0.1. No kinematic hardening is assumed, for simplicity. This test problem will
also be used in the two implementations of the finite deformation elastoplasticity in Section 5.4 for comparison.
The project “elastoplasticity” in project workspace file “fe.dsw” implements the example problem in this sec-
tion and the algorithm discussed in the last section. The yield ratio contours of the perforated strip after 10 incre-
mental loading steps are shown in Figure 5•17. The plastic zone is developed in area where the highest intensity
of the maximum shear values are expected. This zone runs about 45 o upwards from point A, forming a plastic
enclave. This direction is mostly parallel to the point-wise maximum shear directions inside this zone.
For the non-linear problem at hand, the iterative process requires the “matrix assembly” and “solution phase”
been processed many times, comparing to a linear problem where this process is only go thourgh once. There-
fore, the computing time becomes a concern for us. However, the computing time is very sensitive to the size of
the problem. We use “n” to represent the size of a problem, which may stand for total node/element number of a
problem. The element formulation in the assembly process is linearly proportional to “n”. We denote this linear
dependency as O(n). The memory space required for storing global stiffness matrix is proportional to n2 or

1. J. Mech. Phys. Solids, v. 12, p. 377.

Workbook of Applications in VectorSpace C++ Library 585


Chapter 5 Advanced Finite Element Methods

3.50
3.00
2.50
2.00
1.50
1.00
0.75
0.50
0.25
A 0.00

Figure 5•17 Contours of yield ratio (normalized to initial yield value) after
accumulating 10 incremental steps (δu = 0.01 m; total streching ~0.56%).

denote as O(n2), while the matrix solution problem is known to be a process of O(n3). As the size of the problem
increases, obviously the matrix solution process will become very costly. Therefore, optimization methods,
introduced in Chapter 2, that alleviates matrix solution process will be most desirable when the problem size
becomes large. We discuss (1) the conjugate gradient method that completely avoids the need for inverting a
matrix, and (2) the quasi-Newton BFGS method that only inverting matrix once in a while. A word of caution is
as you have seen in Chapter 2, the classical Newton method is the most powerful one that has quadratic conver-
gence rate. Use of these other methods may slow down the convergence rate substantially. The trade-off is then
many more iterations are needed to get to the same level convergence, when the second-order information (the
inverse of Hessian in this case is the decomposition of the global stiffness matrix) is to be avoid either partially
or completely.

Conjugate Gradient Method


The finite element implementation for the conjugate gradient method has use the fact that we are dealing
with a quadratic programming problem with minimization of energy functional (quadratic) of the type

1
Π ( u ) = --- u T Ku – u T f Eq. 5•245
2

where K is the global stiffness matrix and f is the global force vector and we drop the “hat” sign for the nodal
solution as u for simplicity. For the conjugate gradient method the initial search direction is taken as the negative
gradient as

586 Workbook of Applications in VectorSpace C++ Library


Elastoplasticity
p 0 ≡ – g 0 = – D u Π ( u 0 ) = f0 – Ku 0 ≡ r 0 Eq. 5•246

where Du is the directional derivatives, and r0 is the residual. The the solution u, for the next iteration, is updated
along the search direction p as

uk+1 = uk + αk pk Eq. 5•247

The that minimized the objective functional for this quadratic programming case can be shown as1

g kT p k
α k = – -----------------
- Eq. 5•248
p kT Kp k

The next search direction is so chosen that it is orthogonal to the set of previously chosen search directions as

g kT + 1 Kp k
p k + 1 = – g k + 1 + β k p k, where β k = -----------------------
- Eq. 5•249
p kT Kp k

and gk+1 is g evaluated at uk+1. In Chapter 2, the conjugate gradient method is introduced with line search
method that no Hessian (global stiffness matrix) is required. In that case, the convergence will be extremely slow.
For finite element method, not assembling the global stiffness matrix gives some advantage that the required
memory space grow as O(n2) as discussed earlier. However, if the memory is not of very much concern, the vital
second-order information (the Hessian), will help convergence by a lot. The decision to assemble the global stiff-
ness matrix also depends on the fact that computation to form element stiffness matrix is un-avoidable. In order
to compute the residual according to Eq. 5•246, the element stiffness ke must be computed anyway. Since ele-
ment stiffness matrices are computed, assembly of the global stiffness matrix provides vital second-order infor-
mation which facilitates the convergence of the conjugate gradient method. Eq. 5•246 to Eq. 5•249 are
implemented for each incremental loading step, with its index “i”, as

1 const int MAX_ITERATION_NO = 20;


2 const double EPSILON = 1.e-9;
3 if(i != 0) { // initial state of a incremental loading step
4 delta_u = 0.0; // initial free total increment of displacement set to zero
5 delta_uh = delta_u;
6 }
7 for(int j = 86; j <= 92; j++) {
8 d_gh[j][1] = 0.01; // displacement increment on top
9 delta_gh[j][1] = 0.0;
10 }
11 d_uh = d_gh;
12 delta_uh = delta_gh;

1. see p.244 in Luenberger, D.G., 1984, “Linear and nonlinear programming”, 2nd eds., Addison-Wesley Publishing Com-
pany, Inc., Reading, Massachusetts.

Workbook of Applications in VectorSpace C++ Library 587


Chapter 5 Advanced Finite Element Methods
13 uh = gh;
14 double norm_p_0; C0 p;
15 do {
16 cout << (i+1) << "-time step, " << (count) << "-iteration, " << endl;
17 if(count == 1) { // first iteration only
18 for(int j = 86; j <= 92; j++) {
19 gh[j][1] += d_gh[j][1]; // u += du
20 delta_gh[j][1] = d_gh[j][1]; //
21 d_gh[j][1] = 0.0; // after first iteration reset du = 0
22 }
23 uh = gh;
24 delta_uh = delta_gh;
25 d_uh = d_gh;
26 }
27 C0 g, K;
28 for(int k = 0; k < MAX_ITERATION_NO; k++) { // “partial” conjugate gradient loop
29 if(k == 0) {
30 Matrix_Representation::Assembly_Switch = Matrix_Representation::ALL;
31 mr.assembly();
32 K = ((C0)(mr.lhs())); // Kk
33 p = ((C0)(mr.rhs())); // pk = - gk; (gk = rk)
34 g = -p;
35 (C0)(mr.rhs()) = 0.0;
36 (C0)(mr.lhs()) = 0.0;
37 } g kT p k
38 double alpha = (-g*p)/(p*(K*p)); α
// k = – -----------------
-
p kT Kp k
39 du = alpha*p;
40 if(i == 0 && count == 1 && k == 0) delta_u &= C0(p.length(), (double*)0);
41 delta_uh = delta_u += du; // update increment
42 uh += du;
43 if(k != 29) { // compute next search direction pk+1
44 Matrix_Representation::Assembly_Switch = Matrix_Representation::ALL;
45 mr.assembly();
46 C0 g1 = -(C0)(mr.rhs()); // gk+1
47 K = ((C0)(mr.lhs())); // Kk+1
48 (C0)(mr.rhs()) = 0.0;
49 (C0)(mr.lhs()) = 0.0; // g kT + 1 Kp k
pk + 1 = – g k + 1 + β k p k, where β k = ----------------------- -
50 double beta = g1*(K*p)/(p*(K*p)); p kT Kp k
51 p = -g1 + beta * p;
52 g = g1; // update search direction p and gradient g
53 }
54 if(count == 1 && k == 0) norm_p_0 = (double)norm(p); // save initial || p0||
55 if(((double)norm(p)) < (norm_p_0*EPSILON)) break; // convergence if || pk+1|| > ε || p0||
56 cout << k << " p norm: " << ((double)norm(p)) << endl;

588 Workbook of Applications in VectorSpace C++ Library


Elastoplasticity
57 }
58 } while(((double)norm(p)) > (EPSILON*norm_p_0) && (count < MAX_ITERATION_NO) );

The global stiffness matrix is assembled in this algorithm, but no matrix solution, by direct method, is performed.
The convergence rate compared to the classical Newton-Raphson method is still very very slow. The implemen-
tation of the conjugate gradient method for quadratic programming case can be activated by setting the macro
definition “__TEST_CONJUGATE_GRADIENT_METHOD” in project “elastoplasticity” of project workspace
file “fe.dsw”.

Quasi-Newton BFGS Method


In the conjugate gradient method for the quadratic programming problem in the above, we have completely
avoid the use of matrix solver (direct method) for the global stiffness matrix. The convergence rate can be
improved if matrix solver is used. By assuming that this matrix solver is still costly for a large size problem, we
want to use it sparingly. The BFGS method, introduced in Chapter 2, require matrix solver “only once in a
while”. When the inverse of the Hessian, Bi, is formed, the subsequent Bi+1 to Bi+n are obtained by an updating
formula which is relative inexpensive to compute as (see Eq. 2•39 in page 134 of Chapter 2)

 q i • ( B i q i ) p i ⊗ p i p i ⊗ ( B i q i ) + ( B i q i ) ⊗ p i
B iBFGS = B i +  1 + --------------------------- ------------------ – ---------------------------------------------------------------- Eq. 5•250
+1
 qi • pi  pi • pi qi • pi

The basic algorithm is described as follows

Step 1: search direction—compute di = - Bi gi , where Bi = K-1(xi), and gi = ∇Π(xi).


Step 2: loop—partial quasi-Newton method, loop over “n” dimension
a: line search by taking α = 1; i.e., p i = x i+1 - x i = αd i = d i ,
b: gradient difference—qi = gi+1 - gi , where gi+1 = ∇Π(xi+1),
c: update inverse of Hessian for B i + 1 from Eq. 5•250 .
Step 3: restart—repeat Step 1 and 2, and reset B .

For size of B as “n”, step 1 to step 2 can be repeated every “m” times (m < n). This m-iteration loop can be
repeated many times until all the convergence criteria are met. This additional outer loop is known as the partial
quasi-newton method, which aids the convergent rate of a numerical iterative process as opposed to theoretical
one that the convergence is guaranteed in “n” steps. The implementation of the BFGS method is as the follow-
ings

1 const int MAX_ITERATION_NO = 10;


2 const double EPSILON = 1.e-9;
... // incremental loading update
3 double energy, energy_0;
4 do {
5 cout << (i+1) << "-time step, " << (++count) << "-partial quasi-newton loop, " << endl;
... // incremental loading update
6 C0 g, H, B, d_;

Workbook of Applications in VectorSpace C++ Library 589


Chapter 5 Advanced Finite Element Methods
7 for(int k = 0; k < MAX_ITERATION_NO; k++) {
8 if(k == 0) {
9 Matrix_Representation::Assembly_Switch = Matrix_Representation::ALL;
10 mr.assembly();
11 H = ((C0)(mr.lhs())); // Hessian
12 g = -((C0)(mr.rhs())); // gradient
13 Cholesky dH(H, 1.e-12); // modified cholesky method
14 B = dH.inverse(); // inverse of Hessian
15 d_ = -B*g; // search direction
16 (C0)(mr.rhs()) = 0.0;
17 (C0)(mr.lhs()) = 0.0;
18 }
19 if(i == 0 && count == 1 && k == 0) delta_u &= C0(d_.length(), (double*)0);
20 delta_uh = delta_u += du; // update total increment of displacement
21 uh += du; // upeate total displacement
22 double residual_norm = norm(g);
23 energy = fabs( (double)(du *g );
24 if(count == 1 && k == 0) energy_0 = energy;
25 cout << k << " bfgs iteration energy: " << energy << " residual: " << residual_norm << endl;
26 if(k != (MAX_ITERATION_NO-1)) {
27 Matrix_Representation::Assembly_Switch = Matrix_Representation::RHS;
28 mr.assembly(); // form residual vector (rhs)
29 C0 g1 = -(C0)(mr.rhs()); // next gradient
30 (C0)(mr.rhs()) = 0.0;
31 C0 p, q, Bq; p &= du; q = g1 -g; Bq = B*q;
32 B += (1.0+(q*Bq)/(q*p))*((p%p)/(p*q)) - (p%Bq+Bq%p)/(q*p);
33 g = g1; // update gradient
34 d_ = -B*g; // update search direction
35 }
36 if(energy < (energy_0*EPSILON)) break;
37 } // end partial quasi-newton for loop
38 } while(energy > (energy_0*EPSILON) && count < MAX_ITERATION_NO);

With the aids of the inverse of Hessian, B, the convergence rate shows dramatic improvement. When the size of
the matrix is increased, saving time in inverting the global stiffness matrix, a O(n3) process, will become more
and more critical. This implementation can be activated by setting marco definition “__TEST_QUASI_
NEWTON_BFGS_METHOD” in project “elastoplasticity” of workspace file “fe.dsw”.

590 Workbook of Applications in VectorSpace C++ Library


Finite Deformation Elastoplasticity
5.4 Finite Deformation Elastoplasticity
Finite deformation can be an obscure subject, because it requires extremely high-flown mathematics to do it
right. We have always conducted in a manner that only minimum necessary mathematics is used. To convince
you that all your investment of time and effort is worthwhile, we provide some motivation to justify a deeper
mathematical engagement. Heuristic approaches can be made to show the contrary: with the finite rotation, we
analyze how much error is happening for the infinitesimal strain formulation. If there are two observers
using different frames of reference, will the stress rate and material moduli be different for the two observers?
Any difference in the stress rates violates the principle of determinism for stress, and any difference in material
moduli contradicts the principle of material frame indifference.1 These two principles are fundamental postulates
of a purely mechanical theory. These two principles are intuitively comprehensible, and the contrary to them is
just absurd! On top of the material frame indifference, we will also introduce the more advanced concept of
covariance in this section.
A heuristic discussion on the material frame indifference in section 5.4.1 is to complement a more formal
introduction followed in section 5.4.2. Some counter examples are introduced first. 2 These examples are helpful
in clearing out many basic issues regrading finite deformation.

5.4.1 Counter Examples of Infinitesimal Assumption

Strain Measures
We first check the effect of finite rotation on the infinitesimal strain. A rigid body rotation can be expressed
as an orthogonal transformation, with θ as the rotation angle,

R = cos θ – sin θ . Eq. 5•251


sin θ cos θ

An initial position X is rotated to the current position x, as x = R X. The displacement u = x - X = (R - I) X. The


infinitesimal strain (ε) is (with the derivatives with respect to X),

1 2
--- θ + O ( θ 4 ) 0
cos θ – 1
ε ≡ 1--2- ( ∇ u + ∇ T u ) = 1--2- ( R + R T – 2 I ) = 0

2
Eq. 5•252
0 cos θ – 1 1 2
0 --- θ + O ( θ 4 )
2

The last approximation step is the Taylor expansion of the cos θ. Therefore, finite rotation, a rigid body motion,
would excite unwanted strain in the order of θ2 (in radian). In many practical engineering applications we are

1. Malvern, 1969, “Introduction to the Mechanics of a Continuous Medium”, Prentice-Hall, Inc.


2. Belytschko, T, 1986, “An Overview of Semi-discretization and Time Integration Procedures”, p.1, in “Com-
putational Methods for Transient Analysis”, edited by T. Belytschko and T.J.R. Hughes, Elsevier Science Pub-
lishers B.V.

Workbook of Applications in VectorSpace C++ Library 591


Chapter 5 Advanced Finite Element Methods
interested in strain in the order of 10-4 to 10-3. We consider that one degree rotation angle is about in the order of
10-2 in radian. The error in strain caused by this one degree of rotation will be in the order of 10-4. One can
expect that the resultant strain value to be seriously contaminated by the unwanted error. Therefore, the infinites-
imal strain assumption can be rather restrictive.

Stress Rates
In small deformation problems, the Cauchy stress is used in the formulation. Unfortunately, the Cauchy
stress rate is not objective. Consider again applying a rigid body rotation to a body, where no stress should be
excited. Since the stress is a second order tensor, it transforms with the rotation tensor according to the transfor-
mation rule

σ’ = R σ RT Eq. 5•253

Denote the spin tensor (W) as

· 1 T
W ≡ R = --- ( ∇ u· – ∇ u· ) Eq. 5•254
2

Note that W is skew symmetric, so WT = -W. For two coordinates with x = X initially, and therefore R = I initially,
taking time derivative with respect to Eq. 5•253, (then evaluated at x = X, and R = I)

σ· ’ = W σ + W σ = Wσ – σW
T
Eq. 5•255

If σ ≠ 0 , σ· will not vanish. However, no stress rate is expected to be excited. Therefore, σ· is the spurious
’ ’
stress rate that needs to be corrected. The Jaumann stress rate takes out of this spurious stress rate and is defined
as

σ̃ = σ· – W σ + σ W Eq. 5•256

σ̃ is also known as the co-rotational stress rate. For a more formal treatment of the objective stress rate see stan-
dard text.1
The Jaumann stress rate is said to be objective only with respect to isomorphism. Isomorphism means that
the transformation is a one-to-one mapping with inverse of the transformation defined. We are also interested in
diffeomorphism. Diffeomorphism is a transformation that further requires the derivatives of the transformation is
also one-to-one mapping with inverse. There are many other objective stress rates historically. They are either
objective with respect to isomorphism or diffeomorphism.2 Rates which are objective with respect to diffeomor-
phism are called covariance. Therefore, the covariance requirement demands that a physical quantity invariant
under arbitrary Cn transformation. The covariance let us leap forward to the essence of modern Einstein’s rela-

1. Malvern, 1969, “Introduction to the Mechanics of a Continuous Medium” Prentice-Hall, Inc.


2. p.99-102 in Marsden, J.E. and T.J.R. Hughes,1983,“Mathematical foundations of elasticity”, Prentice-Hall,
Inc.

592 Workbook of Applications in VectorSpace C++ Library


Finite Deformation Elastoplasticity
tivity, and by-pass even the quantum mechanics which is yet to be made covariant. A covariant stress-strain path
integration of elastoplastic constitutive equation is the central theme in “implementation 2” in section 5.4.4.

Material Moduli
We restrict our discussion on the constitutive law to what is relevant to the strain measure and the stress rate
issues developed above. First of all, lets note the material moduli is a fourth-order tensor. A transformation apply
to a fourth-order tensor is similar to that of Eq. 5•253 for the second-order tensor, only the spurious term gener-
ated will be twice as ugly as what is in Eq. 5•255. Disregarding what those ugly spurious terms really are, it is
suffice to say that the material moduli so derived is not invariance with respect to a rigid body motion. This has
certain impact on material moduli of anisotropic materials. For isotropic materials, the constitutive law are
required to be written, by choosing its independent parameters, to be invariant with respect to rotation. It is not so
fortunately for the anisotropic materials. It becomes our responsibility to make it objective by subtracting corre-
sponding spurious terms similar to what was done in Eq. 5•256. The argument above for anisotropic material
moduli is therefore completely parallel that for stress rate. Secondly, we already know the Cauchy stress rate is
not objective (the spurious stress σ · ≠ 0 in Eq. 5•255 occurs). When we construct the constitutive equation in

stress-strain rate form, we could expect that the constitutive equation should be defined using the objective stress
rates only; i.e., not to use the Cauchy stress rate. Then, if it is more convenient for computation, the objective
stress rates can then be expressed in terms of the Cauchy stress rate with its corresponding spurious terms. When
the objective stress rate is used, the problem occurred in the material moduli here resolves automatically.

5.4.2 Basic Theory of Finite Deformation Elastoplasticity


We proceed with the theory on finite deformation. We follow notations and definitions primarily from J.E.
Marsden and T.J.R. Hughes[1983]1. Our intention in this workbook is to implement the endeavor of Simo et
al.[1985]2, and Simo[1988]3. The perspectives covered below is limited to the subject that is most relevant to
computational alogrithms that we are going to impelement in VectorSpace C++ Library. For scientists and engi-
neers, we recommend introduction in finite element context with less mathematical by Crisfield[1997]4 or Bonet
and Wood[1997]5. For a minimum exposure for the mathematical-impatient readers, Crisfield[1997] is recom-
manded. However, if you are not interested in the subject of finite deformation elastoplasticity itself, you just
want to assess how much VectorSpace C++ Library can make programming of a high-flown mathematical sub-
ject easier. This section should be self-contained and be able to assist you dig into all the programming details.
You can learn by comparing the mathematical expression herein and the actual codes of implementation in Vec-
torSpace C++ Library.

1. Marsden, J.E. and T.J.R. Hughes,1983,“Mathematical foundations of elasticity”, Prentice-Hall, Inc.


2. J.C. Simo, R.L. Taylor, and K.S. Pister, 1985, “Variational and projection methods for the volume constraint in finite
deformation elasto-plasticity”, Computer Methods in Applied Mechanics and Engineering, vol. 51, p. 177-208.
3. J.C. Simo, 1988, “A framework for finite strain elastoplasticity based on maximum plastic dissipation and the multiplica-
tive decompositions: part I. continuum formulation.” Computer Methods in Applied Mechanics and Engineering, vol. 66, p.
199-219. “Part II. Computational Aspects.”, vol. 68, p.1-31.
4. Crisfield, M.A., 1997, “Non-linear finite element analysis of solid and structures”, vol. 2, John Wiley & Son Ltd. , UK.
5. Bonet, J. and R.D. Wood, 1997, “Nonlinear continuum mechancis for finite element analysis”, Cambridge University
Press, Cambridge, UK.

Workbook of Applications in VectorSpace C++ Library 593


Chapter 5 Advanced Finite Element Methods

φ
φ(X) = x
WX
X Tφ WX

Figure 5•18 “Push-forward” of a tangent vector WX.

Kinematics
A simple body is an open set in n. Define its containing space as = n. A C1 configuration is repre-
sented by a mapping φ: → , see Figure 5•18. A tangent space to the set at point X is denoted as TX . The
tangent space is the vector space n considered as vectors with base point X. Similarly the tangent space to is
Tx . A tangent vector WX = (X, W) ∈ T X, with X denoting its base point. A tangent map of φ is defined as T
φ: TX → Tx . T φ(X, W) = (φ(X), Dφ(X) • W) where “Dφ(X) • W” denotes the derivative of φ(X) at X in the
direction of W. Then, T φ • WX is called “push-forward” of W by φ. The push-forward is also denoted as φ*.

Deformation Gradient
The tangent of φ is denoted F, the deformation gradient of φ; i.e., F = T φ. Let {XA} and {xa} denote coordi-
nate system on and , respectively. The matrix F with respect to the coordinate bases EA(X) and ea(x) is given
by F = FaA ea ⊗ EA (= GRAD φ(X) ≡ ∇ ⊗ φ(X), where the operator ⊗ denotes the tensor product)

a ∂φ a ∂x a
F A ( X ) = ---------- ( X ) , note that d x a = ---------- dX A Eq. 5•257
∂X A ∂X A

where dxa and dXA are the infinitesimal position vectors, which are the tangent vectors in and . From Eq.
5•257, dxa = F • dXA (= T φ • dXA = φ* dXA) is the “push-forward” of dXA by φ. The Jacobian is, J = det F.
The restriction that J > 0 is the impenetrability of matter, in that the local invertibility of F is required. Therefore,
the relative orientation of the line elements is preserved under deformation.
Metric tensor on is defined as gab(x) = <ea , eb>x , and metric tensor on is defined as GAB(X) = <EA ,
EB>X , where < , > is the inner product in n. The transpose of F is defined as

A
( F T ( x ) )a = g ab ( x ) F Bb ( X ) G AB ( X ) Eq. 5•258

Note that the deformation gradient F is a two-point tensor which can be considered as a tensor with “two legs”;
one on and the other on . Therefore, the question of whether F is symmetric is irrelevant!
Since F with two different “legs” can not be symmetrical, we want to find ways to define “symmetrized”
quantities from it; i.e., somehow, get the off-diagonal components to have same value and “leg”. Therefore, the

594 Workbook of Applications in VectorSpace C++ Library


Finite Deformation Elastoplasticity
Green deformation tensor (or the right Cauchy-Green tensor) C is defined as CAB(X) = F(X)TF(X). Note that
the “legs” of C is now all in upper-case indices. If F is one-to-one, C is symmetric and positive definite. Simi-
larly the left Cauchy-Green tensor b is defined as bab(x) = F(X) F(X)T. b is also symmetric and positive definite,
and its inverse c (= b-1) is called the Finger deformation tensor.

Lagrangian Strain Tensor and Eulerian Strain Tensor


The difference of the square of infinitesimal position vectors in and is

d x 2 – d X 2 = d X • ( FT F – G ) • d X = d X • ( C – G ) • d X
Eq. 5•259
2E

The Lagrangian (material, or Green) strain tensor E is defined as 2E = (C - G), (where G = I, in the Cartesian
Coordinates, where I is the identity on TX ). Similarly,

d x 2 – d X 2 = d X • ( g – ( FF T ) –1 ) • d X = d X • ( g – b –1 ) • d X Eq. 5•260
2e

The Eulerian (spatial) strain tensor e is defined as 2e = (g - b-1), (where g = i, where i is the identity on Tx ).

Symmetric Positive Definitive Tensor and Orthogonal Tensor


Before we get to the polar decomposition theorem, let’s first look at the properties of two second-order ten-
sors: (1) a symmetric positive definite tensor, and (2) an orthogonal tensor. A symmetric positive definite tensor
S in 3 admits spectral representation with three positive eigenvalues λ i , and three eigenvectors pi as follows:

3
S = ∑ λi ( pi ⊗ pi )
i=1
Eq. 5•261

An orthogonal tensor Q satisfies a necessary and sufficient condition by definition

QTQ = I, and det Q = 1 Eq. 5•262

This leads to det(Q - I) = 0, indicating there exists a unit vector p such that

Q p = p = QT p Eq. 5•263

The orthogonal tensor Q can be expressed as the following equation (for derivation see P. Chadwick[1976]1)

Qx = pp + (q cosθ-r sinθ)q+(q sinθ+r cosθ)r Eq. 5•264

1. see p. 37 in Chadwick, P., 1976, “Continuum mechanics, concise theory and problems”, John Wiley & Sons,
New York.

Workbook of Applications in VectorSpace C++ Library 595


Chapter 5 Advanced Finite Element Methods
p3

λ1 p x x’
λ3
q
p2
p1 λ2

r
Figure 5•19 Stretches and Rotation for the Polar Decomposition
One recognizes, in Eq. 5•264, (q cosθ-r sinθ)q+(q sinθ+r cosθ)r is a rotation about p-axis on (q, r)-plane. Fig-
ure 5•19 illustrates both the interpretation of S as stretches λ1, λ2, λ3, and Q as rotation around p-axis.
Recall C and b are symmetric and positive definite. Define U = C , and V = b , where U and V are the
right stretch tensor and the left stretch tensor, respectively. U and V have common eigenvalues λ i. The eigen-
vectors pi of U and the eigenvectors qi of V are called the referential stretch axis and the current stretch axis,
respectively.

Polar Decomposition Theorem


Polar Decomposition Theorem: For a regular φ, there exists an orthogonal transformation R(X): TX
→ Tx , such that, F = R U—the right polar decomposition, and F = V R—the left polar decomposition are
both unique.

Step 0 Step 1 Step 2


Decompsition

pi
Right Polar

λi
qi

Stretch by U Rotate by R
Decomposition

pi
Left Polar

qi λi

Rotate by R Stretch by V

Figure 5•20 Two Steps of Right and Left Polar Decomposition.


See P. Chadwick[1976, p. 33] for proof. The geometrical interpretations of the right polar decomposition,
and the left polar decomposition follows. The deformation on a given particle, is proceeded as either first apply-
ing stretches λ i in the directions pi, and then rotating from directions pi to directions qi (the right polar decom-
position), or the operations can be performed in a reverse order (the left polar decomposition). These two
operations in 2-D are shown in Figure 5•20

596 Workbook of Applications in VectorSpace C++ Library


Finite Deformation Elastoplasticity
Deviatoric-Spherical Kinematic Split
A kinematic split of infinitesimal strain tensor into its deviatoric and spherical parts is

ε 1
= e + --- Θ g
3
Eq. 5•265

where infinitesimal strain tensor ε ≡ ( ∇ u + ∇T u ) ⁄ 2 , and the mean dilatation Θ ≅ J ≡ ∇•u . This kinematic split
is important for kinematic constraint for the incompressible materials, and is useful in the context of J2 plasticity.
In finite deformation this additive split is substituted by a multiplicative split.

F = J 1 / 3 F̂ Eq. 5•266

where “^”, denotes the volume-preserving part. Therefore, the corresponding right Cauchy-Green tensor is

T
Ĉ = J – 2 / 3 C = F̂ F̂ Eq. 5•267

The infinitesimal version of the additive split (Eq. 5•265) can be understood as linearization of the multiplicative
split (Eq. 5•267) about φ = I as the followings. Let denotes the set of all configuration φ. A tangent vector to
at φ0 ∈ is a vector field u covering φ0. The variation εu, where ε ∈ , is the infinitesimal deformation imposed
on the finite deformation φ0, or writes εu = δ φ0, the variation of the configuration. Considering an incremental
motion φ0ε = φ0 + εu • φ0, the deformation gradient with infinitesimal increment Fε is

Fε = [F + ε ∇uF]. Eq. 5•268

The linearization of the volume-preserving right Cauchy Green tensor Ĉ in Eq. 5•267 gives “2e”, , where e,
unfortunately an overloaded symbol, is the infinitesimal deviatoric strain tensor. The derivation is explained in
the followings. The derivatives of the Jacobian, and the right Cauchy-Green tensor evaluated at ε = 0 are:

d d
det ( F ε ) = det ( F ) tr ( ∇u ) , and Cε = F T ( ∇ u + ∇T u ) F Eq. 5•269
dε ε=0
dε ε=0

εu
φ0

εu

φ0( )
Figure 5•21 Infinitesimal deformation imposed on finite deformation

Workbook of Applications in VectorSpace C++ Library 597


Chapter 5 Advanced Finite Element Methods
On specifying φ0 = I, and substituting two results in Eq. 5•269 into the derivatives of Eq. 5•267 gives 2e = - 2/3
tr( ∇u) g + ( ∇ u + ∇ T u ) . By definition ( ∇ u + ∇ T u ) = 2 ε . Therefore, the linearization of C in Eq. 5•267 gives
ˆ
the infinitesimal deviatoric strain e, as the additive version, e = ε - 1/3 tr( ∇u) g.

Lie Derivatives and Objective Rates


The Lie derivatives of a spatial tensor field t relative to the deformation φ associated with the spatial velocity
field vt is defined as


L v t = φ ∂ t φ∗t Eq. 5•270
*

where φ* is the “pull-back” by φ, defined as action taken by tangent map T φ-1. In words, the procedure of taking
the Lie derivatives is:

(1) “pull-back” the spatial object to the reference configuration with F,

(2) taking the time derivative at the reference configuration, and

(3) “push-forward” the result of the time derivative to the current configuration using F.

This objective spatial rate is sometimes called a convected time derivative. It is the change of a spatial object rel-
ative to the flow of the spatial velocity field. Therefore, it is naturally objective. As we have mention earlier, the
objective flux (rate) defined by the Lie derivatives is objective with respect to diffeomorphisms and is called
covariant. A covariant rate transforms tensorially independent of any preferred coordinate system.
Strain rates are essential in defining constitutive laws in rate-form, for example, viscosity and plasticity. The
covariant nature derived from the Lie derivatives plays an important role. Following results of the Lie deriva-
tives on spatial tensors, g and b, are important.

Lv g = 2 d, and Lv b = 0 Eq. 5•271

We discuss the derivation in words, with two additional pull-back identities φ*(g) = C and φ*(b) = G [see Mars-
den and Hughes]1. In the first part of this equation, the Lie derivative of spatial metric tensor g gives two times
T
the spatial rate of deformation tensor d ( ≡ ( ∇ u· + ∇ u· ) ⁄ 2 ). The result follows immediately from the definition
of the Lie derivative per se. The first step is: “pull-back” of g is C the right Cauchy-Green tensor; i.e., φ*(g) = C.
Then the second step is: taking the time derivative on C yields 2D by definition; i.e., 2D ≡ ∂C ⁄ ∂t , where D is
the material rate of deformation tensor. The third step is: “push-forward” of D is the spatial rate of deformation
tensor d; i.e., φ*(D) = d. The second part of Eq. 5•271 shows the left Cauchy-Green tensor b is “dragged” by the
flow v. Again, following the procedure defined in the Lie derivatives, in the first step, the “pull-back” of b is the
material metric tensor G; i.e., φ*(b) = G. In the second step, the material metric tensor G is constant with respect
to time, and will be zero upon taking time derivative. In the third step, “push-forward” of zero gives zero. Eq.
5•271 is useful for developing the constitutive law in rate form.

1. “Mathematical Foundations of Elasticity”, Prentice-Hall, Inc.

598 Workbook of Applications in VectorSpace C++ Library


Finite Deformation Elastoplasticity
Piola Transformation, Stresses, and Stress Rates
The Piola Transformation is of primary importance in relating material and spatial descriptions of a continu-
ous medium. This transformation is similar to the “pull-back”, with additional presence of the Jacobian factor.
The presence of the Jacobian indicates that volume or area are being transformed. Definition of the Piola trans-
formation is as the followings.

Piola Transformation: Let y be a vector field on , and Y a vector field on . The Piola Transform of y to Y is
given by

Y = J φ*y Eq. 5•272

This transformation will be used frequently in the following developments.


n
Ν
y
Y da
dA φ0( )

Figure 5•22 Piola tranformation


The Cauchy stress, σ, is defined on spatial configuration. In infinitesimal theory no distinction of spatial and
material configurations is necessary. Therefore, Cauchy stress definition is sufficient for the analysis under the
infinitesimal assumption. In the context of finite deformation, we have distinct spatial and material configura-
tions. Although one can use the Cauchy stress to formulate the equation of motion in terms of the Eulerian for-
mulation, the Lagrangian formulation is often preferred in elasticity, particularly for the Green elastic material
(see also the hyperelasticity in the next section on Constitutive Equations). For the Green elastic material, it is
assumed that there exists a natural state to which the body would return to the natural state when it is unloaded.
With spatial and material configurations in mind, many stress definitions, based on different combinations of
configurations, are possible. The idea is to choose the one that appears to be natural to the constitutive postulate.
Considering the stress is force per unit area. It will be natural to measure the force in the spatial configuration
where the force is currently balanced, and measure the per unit area in the material configuration to which the
natural unloaded state is defined. Therefore, the first Piola-Kirchhoff stress tensor P is a two-point tensor given
by the Piola transformation on the second index of the Cauchy stress tensor σ. That is,

PaA = J (F-1)Ab σab Eq. 5•273

This stress definition gives simple form of equations of motion. However, the first Piola-Kirchhoff stress tensor
is not symmetrical. Recall mixed upper-lower case indices means that the first Piola-Kirchhoff stress is a two-
point tensor with “two legs”. This disadvantage of first Piola-Kirchhoff stress tensor leads to the definition of
second Piola-Kirchhoff stress tensor. The second Piola-Kirchhoff stress tensor S is defined as pulling the “first
leg” of P back by φ as

SAB = (F-1)Aa PaB = J (F-1)Aa (F-1)Bbσab Eq. 5•274

Workbook of Applications in VectorSpace C++ Library 599


Chapter 5 Advanced Finite Element Methods
Both σ and S are symmetrical tensors. The symmetrical property of S can be directly derived from the moment
of momentum balance principle. It is similar to the standard argument in continuum mechanics that leads to the
proof of the symmetry of σ. Similarly, the Kirchhoff stress tensor τ is the “push-forward” of the second leg of
the first Piola-Kirchhoff stress tensor; i.e.,

τab = PaB FbB = J σab Eq. 5•275

Both τ and σ are defined only in spatial configuration. We observe the difference of them is a scalar factor of J,
the Jacobian. The reason for this proliferation of stresses (τ and σ) in the same configuration is whether one is
performing the Piola transformation with J factor considered, or merely pull-back and push-forward the tensor
objects between spatial and material configurations.
In this section, we mentioned one of the disadvantage of the Cauchy stress in describing the Green elastic
material. The other dismay of the Cauchy stress is the rate of the Cauchy stress is not objective (while the rate of
another symmetric stress tensor, the second Piola-Kirchhoff stress tensor S is objective). Therefore, the Cauchy
stress rate will not be suitable for defining constitutive laws formulated in rate form, e.g., viscosity and plasticity.
The subject of objective rates has been very controversial. All the “objective rates” of second-order tensors, for
examples the Oldroyd rate, Truesdell rate, and Jaumann rate are in fact either (1) the Lie derivatives of the
Cauchy stress tensor σab, or (2) the Lie derivative of the Cauchy stress’s associate tensors (σab, σab, σab), or
(3) certain linear combinations of the Lie derivatives of the associate Cauchy stresses.

Constitutive Equations
In the context of elastoplasticity for this workbook, we restrict ourselves on isotropic material in hyperelas-
ticity and associative flow law in the classical J2 plasticity (see e.g., Fung [1965]1, or Malvern [1969]2 for intro-
duction). We focus on the consequence of finite deformation on such a constitutive law.
For pure elasticity (isothermal), Ψ—the free energy, and E—the internal energy coincide (Ψ = E). We also
postulate that the constitutive equation can be expressed in differential operator which is defined in “local”
points to represent the whole material; i.e., the axiom of locality. Taking covariance of energy balance as an
other axiom, one can deduce

∂E ∂F
ρRef  ------- = P: ------- Eq. 5•276
∂t ∂t

This is, the change in internal energy (the left-hand side) equals the “mechanical power” (the right-hand side).
Therefore, the constitutive equation, in material configuration expressed by the first Piola-Kirchhoff stress ten-
sor, should have the form of

∂Ψ
P = ρRef g# -------- Eq. 5•277
∂F

1. Fung, Y.C., 1965, “Foundations of solid mechanics” Prentice-Hall, Inc., Englewood Cliffs, New Jersey.
2. Malvern, L.E., 1969, “Introduction to the mechanics of a continuous medium” Prentice-Hall, Inc., Englewood
Cliffs, New Jersey.

600 Workbook of Applications in VectorSpace C++ Library


Finite Deformation Elastoplasticity
where g# is the Riemannian spatial metric tensor with the sharp sign, “#”, indicating all of its indices raised. This
is to say the free energy Ψ depends on point value of X, and depends on φ only through F. We observe that Ψ
doesn’t require to depend on φ itself, or higher derivatives of F.
From principle of material frame indifference (in 3), as an axiom, Eq. 5•277 has to be objective under
orthogonal transformation (rotation). So, the free energy Ψ depends on F only through C, which makes intuitive
sense since C = FTF = U2, which is free of the orthogonal component R—the rotational part of the polar decom-
position of F. In other words, the free energy function Ψ(X, F) reduces to Ψ(X, C). The “mechanical power” in
material configuration can also be expressed in S and C by writing P : ∂F ⁄ ∂t = S : D = 1/2 S : ∂C ⁄ ∂t . The same
argument to derive Eq. 5•277 leads to

∂Ψ
S = 2 ρRef -------- Eq. 5•278
∂C

Recall the “push-forward” of C is g. Having the Piola transformation in mind, Eq. 5•278 is equivalent to consti-
tutive equations written in the Cauchy stress σ, and the Kirchhoff stress τ defined in spatial configuration as the
follows

Ψ ∂Ψ
σ (= J-1 τ) = 2 ρ ∂-------
- ⇒ τ = 2 ρRef -------- Eq. 5•279
∂g ∂g

This is the Doyle-Erickson formula. For the dependence of free energy Ψ on g, one can feel more comfortable by
directly considering spatial setting as the followings. Changes of spatial metric tensor on from g to say g'
affect the accelerations of particles. Thus, the internal energy E must depend on the metric tensor g; i.e., for the
free energy Ψ to depend on g.
The elasticity tensor or the elasticities A is a fourth-order tensor on , and is defined as

∂S ABCD ∂S AB ∂2 Ψ
A = ------- , i.e., A = -------------- = 2 ρ Ref ----------------------------- Eq. 5•280
∂C ∂C CD ∂C AB ∂C CD

the symmetry of the elasticity tensor AABCD = ACDAB implies the cross partial differentiation of Ψ are equal

∂2 Ψ ∂2Ψ
----------------------------- = ----------------------------- Eq. 5•281
∂C AB ∂C CD ∂CCD ∂C AB

This is the condition for the existence of the free energy Ψ as a potential function from calculus; i.e., also the
condition to define hyperelasticity.
We have concluded that the free energy Ψ depends on φ and F only through C. Recall that C is a symmetric
tensor, which can be brought to diagonal form by orthogonal transformation. For regular φ we have seen, a posi-
tive definite symmetric tensor in this case C, admits spectral representation (Eq. 5•261). The free energy Ψ must
be a function only of the eigenvalues of C; that is, Ψ depends only on the principles stretches of U. However,
instead of using eigenvalues, the three invariants of C, where I(C) = tr (C), II(C) = det C tr C-1, and III(C) = det
C = J2, is also convenient to use. The same is true for the free energy Ψ expressed in spatial configuration with
the left Cauchy-Green tensor b, used in the next section.

Workbook of Applications in VectorSpace C++ Library 601


Chapter 5 Advanced Finite Element Methods
5.4.3 An Isotropic Material with Uncoupled Volumetric and Deviatoric Response
A specific case of material property, isotropic with uncoupled volumetric and deviatoric response, is devel-
oped in Simo[1988]1. This hyperelastic material is used for the numerical computation adopted in this manual.

Elasticity
Recall the kinematic split into deviatoric and spheric parts, the volume-preserving right Cauchy-Green ten-
T
sor is, therefore, defined as Ĉ = J –2 / 3 C = F̂ F̂ . The volume-preserving left Cauchy-Green tensor is defined
similarly as b̂ = J –2 / 3 b = F̂F̂ . The idea is to express the free energy Ψ as function of three invariant of b; i.e.,
T

Ib, IIb, and IIIb. For an isotropic material with uncoupled volumetric and deviatoric responses, the stored energy
ˆ
function has the form of Ψ = Ψ ( b̂ ) + U ( J ) . A special case in invariants of b is

1
Ψ = --- µ ( Îb – 3 ) + U ( J ) Eq. 5•282
2

where Î b = J-2/3 Ib, (where Ib ≡ b : g), and that J = IIIb1/2. Two identities are useful for the following deriva-
tions: Lv(IIIb) = Lv(J2) = 2 IIIb g# : d, and Lv(Ib) = Lv(b# : g) = 2 b# : d. Recall the Doyle-Erickson formula
(Eq. 5•279) as the Kirchhoff stress in relation to the free energy Ψ is

Ψ
τ = 2 ρRef ∂-------
- ⇒ τ = J p g# + µ dev b̂ , Eq. 5•283
∂g
∂τ
where p = dU/dJ. The spatial elasticity tensor a ≡ 2 ------ becomes,
∂g

2  1 
a = J 2 U’’( J )( g ⊗ g ) + Jp ( g ⊗ g – 2I )+ --- µ  Î b I – --- ( g ⊗ g ) – ( devb̂ ⊗ g – g ⊗ devb̂ )  Eq. 5•284
3  3 

Define the deviatoric part of the elasticity tensor a as

2  1 
a dev ≡ --- µ  ˆI b I – --- ( g ⊗ g ) – ( devb̂ ⊗ g – g ⊗ devb̂ )  Eq. 5•285
3  3 

For example, a possible case of U, satisfying polyconvex condition, is U(J) = K/2 (J2-1). U’ = dU/dJ = p = KJ =
K (1+ ∫ dV/V) = K (1+logV), U’’ = d2U/d2J = K = V ( ∂p ⁄ ∂V ) T , with the last identity derived from p =
K(1+logV), where K is then identified as the bulk modulus. Eq. 5•284 is symmetrical as required in hyperelas-
ticity shown in Eq. 5•281.
We observe that in the above extremely simplified case of finite elasticity, the elasticity tensor a is a strong
non-linear function of deformation (expressed in J and b) and metric tensor (g), even though the bulk modulus K
and the shear modulus µ have been naively assumed as constant with respect to deformation and thermal effect.
This non-linearity is required if the constitutive equation is to be covariant, and is to be consistent with the sim-

1. J.C. Simo, 1988, “A Framework for Finite Strain Elastoplasticity Based on Maximum Plastic Dissipation and the Multi-
plicative Decompositions: Part I. Continuum Formulation.” Computer Methods in Applied Mechanics and Engineering, vol.
66, p. 199-219. “Part II. Computational Aspects.”, vol. 68, p.1-31.

602 Workbook of Applications in VectorSpace C++ Library


Finite Deformation Elastoplasticity
ple notion of hyperelasticity, where a stored energy function must exist. Covariance is also required in the plas-
ticity discussed below, in which we use the Lie derivative to measure the stress rate in spatial configuration.

We have come to a long way to define the elastoplasticity in the finite deformation range. However, the com-
plicated mathematical expression in Eq. 5•284 is only to implement the simplest ideas of “objectivity” and
“restorable elastic energy”. The algebraic structures in the infinitesimal deformation simply does not carry over
to the finite deformation. The idiosyncrasy in finite deformation range, if not respected, will lead only to absur-
dity!

Multiplicative Decomposition of Elastic and Plastic Deformations


The formal additive decomposition (the Green-Naghdi decomposition) is

E = Ee + Ep => D = De + Dp Eq. 5•286

where E is the finite Lagrangian strain tensor, and D is the strain rate tensor. The constitutive equation for such
additive decomposition is usually cast in a rate-form. However, the notion of hyperelasticity is not consistent
with a rate expression. The incremental objective algorithm, historically, is developed to ensure the frame-indif-
ference nature of the path-dependent stress-strain integration scheme, and hyperelasticity is achieved through
algorithmic approximation. The semantics of the Eq. 5•286 implies that the elastic and plastic deformations
occurred simultaneously. De and Dp are unknown to be solved simultaneously. An additional (stronger) assump-
tion, on top of the formal additive decomposition, will help us resolve the elastic and plastic parts of deformation
separately. This additional assumption is that the elastic and plastic deformation occur in succession. The defor-
mation gradient, occurring in two consecutive steps, is naturally expressed as a multiplicative decomposition
(Lee decomposition).

F = F e Fp Eq. 5•287

The superscript “e” indicates elastic part, and “p” indicates the plastic part. The tensor (Fe)-1, therefore, is the
deformation gradient which responses to the elastically released stress. The released intermediate configuration
is thus introduced. In multiplicative decomposition, one is able to compute elastic response exactly (by mere
function evaluation as opposed to algorithmic approximation in the incremental objective algorithm). This addi-
tional assumption of multiplicative decomposition is also justifiable from scientific point of view. It has been
argued from two perspectives. Firstly, from theoretical development of how elastic deformation of a crystalline
material is lead to dislocation (i.e., plasticity as the macroscopic manifestation of crystal structural dislocation).
Secondly, electron-microscope study that demonstrates how this micro-mechanism can occur.

Plastic Metric Tensor and the Form of Elastoplastic Free Energy


From the definition of the Eulerian strain tensor e, we see that e = 1/2 (g - b-1), and similarly one can defined
e = 1/2 (g - (be)-1
e ). Since the total strain is the summation of the elastic and plastic strain e ≡ ee + ep, we have ep
= e - e = 1/2 ((b ) - b-1 ). Comparing this result to the definitions of the Eulerian strain (e) and the plastic Eule-
e e -1

rian strain (ep), we observe that (be)-1 plays the role of the plastic metric tensor gp. That is, for the elastic Eule-
rian strain ee, the elastic Finger deformation tensor (be)-1 is the deformation from the “identity” g, while for the

Workbook of Applications in VectorSpace C++ Library 603


Chapter 5 Advanced Finite Element Methods
plastic Eulerian strain ep, the Finger deformation tensor b-1 is the deformation from the “identity” gp =(be)-1. We
have the transition from the initial to the intermediate and then to the final configurations by: g → (be)-1 → b-1.

Principle of Maximum Plastic Dissipation and Plastic Flow Law


Keeping the plastic metric tensor gp = (be)-1 in mind, we first discuss the form of the elastoplastic free
energy, and then we derive the constitutive equation in rate form as follows. The most general form of a pure
mechanical elastoplastic free energy is Ψ = Ψ(g, Fe, F). That is to be dependent on the spatial metric tensor g
and deformations. Since there are two parts—elastic and plastic, the free energy also depend on the intermediate
deformation gradient Fe and the final deformation gradient F (= FeFp). (taking {Fe, F} is equivalent to taking
{Fe, Fp}). The first restriction is the intermediate configuration represented by Fe should be invariant under
proper orthogonal transformation. This reduce Fe to (be)-1, recall this is the elastic Finger deformation tensor.
That is Ψ(g, Fe, F) → Ψ(g, (be)-1, F). An alternative view of this is Ψ(g, gp, F), if we consider gp = (be)-1. The
second restriction is covariance under arbitrary diffeomorphisms. If we allow the transformation ξ* = φ*, that is
pull-back, we obtain, in material configuration, Ψ(C, Cp) (Note that φ*g = C, φ*gp = Cp, and φ*F = I).
Recall we have pointed out that the Cauchy stress rate is not objective (Note that the spatial tensor quantities
are, in general, not covariant with respect to diffeomorphism). The covariant tensor quantities are those spatial
quantities taken as the Lie derivative. Recall the Kirchhoff stress τ = J σ = 2 ρRef ∂Ψ ⁄ ∂g , and the Lie deriva-
tive of g is Lv g = 2 d. We take the Lie derivative of τp, the plastic relaxation stress, and the Lie derivative of
∂Ψ ⁄ ∂g with respect to the last two variables, the metric gp (=(be)-1) and F to constitute the plastic law. That is

e –1
∂ Ψ ( g, ( b ) , F ) ∂2Ψ ∂2Ψ
τp = 2 ρRef Lv( ------------------------------------------ ) = - 2 ρ Ref  ------------------------- 
e –1
Lv e – 1- L v ( ( b ) ) + -------------- L v ( F ) Eq. 5•288
∂g ∂g∂(b ) ∂ g ∂F

such that
e –1
∂ 2 Ψ ( g, ( b ) , F )
Lv τ p
= - 4 ρ Ref --------------------------------------------
e –1 - : dp Eq. 5•289
∂g∂(b )
e –1
Recall Lv(g) = 2d, and Lv(F) is zero, since φ*F = I. Note that L v ( ( b ) ) = L v ( g p ) = 2dp, the spatial rate of
plastic deformation tensor, where d is the symmetrical part of the velocity gradient.

Volume-Preserving Plastic Flow Rule


For an uncoupled hyperelastic case, with elastic volumetric response, the plastic relaxation stress is

e –1
∂ 2 Ψ ( g, ( b ) , F )
Lv τ = - J
p -2 / 3 e –1
e –1 - : L v ( ( b ) ) ],
dev [ 2ρRef-------------------------------------------- Eq. 5•290
∂g∂(b )

where g = J –2 / 3 g . The yield function for the associative flow rule is

e –1
φ ( g, ( b ) , q, F ) ≤ 0 Eq. 5•291

604 Workbook of Applications in VectorSpace C++ Library


Finite Deformation Elastoplasticity
where q is the plastic internal variable. The Lagrangian functional with the inequality constraint φ ≤ 0 is

e –1
∂ Ψ ( g, ( b ) , F )
γ φ ( g, ( b ) , q, F )
p e –1 · e –1
≡ ------------------------------------------
e –1 : Lv ( ( b ) ) + Eq. 5•292
∂( b )

· ·
where γ is the plastic consistency parameter. γ is also the Lagrangian multiplier in constrained optimization.
The minimization condition of the Lagrangian functional is the Euler-Lagrange equations (with virtual variation
of g )


------ p = 0, Eq. 5•293
∂g

and the Kuhn-Tucker condition, the celebrated trio in inequality constrained optimization is

· e –1 · e –1
γ ≥ 0 , φ ( g, ( b ) , q, F ) ≤ 0 , and γ φ ( g, ( b ) , q, F ) = 0 Eq. 5•294

the Euler-Lagrange equation Eq. 5•292 gives the definition of the plastic relaxation stress as

∂φ
Lv τp = γ· 2 J dev ------
–2 / 3
Eq. 5•295
∂g

This is the plastic flow law basing on Hill’s principle of maximum plastic dissipation as derived in Simo[1988]1.
For a specific case of isotropic-kinematic hardening J2 plasticity with uncoupled hyperelasticity, the problem
is defined as

τ≡K log J g + J –2 / 3 µdev [ b e ] Eq. 5•296

ξ ≡ dev [ τ ] – J –2 / 3 dev [ α ] Eq. 5•297

φ ≡ ξ – 2--- κ ( e p ) Eq. 5•298


3

The corresponding flow rule from maximum plastic dissipation principle is

· ξ 1
µ J –2 / 3 dev [ L v b e ] = – 2µγ n̂ , where n̂ ≡ -------- , µ ≡ --- µ J –2 / 3 tr [ b e ] , and tr [ L v b e ] = 0 Eq. 5•299
ξ 3

The hardening law, with isotropic-to-kinematic proportion ratio β ∈ [ 0, 1 ] is

1. J.C. Simo, 1988, “A Framework for Finite Strain Elastoplasticity Based on Maximum Plastic Dissipation and the Multipli-
cative Decompositions: Part I. Continuum Formulation.” Computer Methods in Applied Mechanics and Engineering, vol. 66,
p. 199-219. “Part II. Computational Aspects.”, vol. 68, p.1-31.

Workbook of Applications in VectorSpace C++ Library 605


Chapter 5 Advanced Finite Element Methods

· 2·
κ ( e p ) = κ0 + β h’e p , where e p = --- γ Eq. 5•300
3

· h'
J – 2 / 3 dev [ L v α ] ≡ 2 µγ ------- ( 1 – β ) n̂ , where µ ≡ µ – J –2 / 3 tr [ α ] , and tr [ L v α ] = 0 Eq. 5•301

Consistent Tangent Moduli


The finite deformation counterpart of the consistent tangent moduli is derived in Simo[1988]1.

µ ≡ µ – --- tr [ F u α ( F u ) ]
1 1 ˆ ˆ T
µ ≡ --- µ tr [ b̂ ne +TR
1 ], Eq. 5•302
3 3

1 2
TR
a dev n+1 ≡ 2 µ I – --- ( g ⊗ g ) – --- ( s TR ⊗ g + g ⊗ s TR ) Eq. 5•303
3 3

≡ 2 µ I – --- ( g ⊗ g ) – --- ( ξ TR ⊗ g + g ⊗ ξ TR )
TR 1 2
h dev n+1 Eq. 5•304
3 3

with scaling factors as

2 µγ n + 1 1 h’ κ’ , h’
- + ------- δ ≡ f 2µ – -----  1 + ------ – 1 --- γ n + 1 , δ ≡ 2
f0 ≡ ------------------- 1 4
ξn + 1
TR
-, f 1 ≡ ----- – f 0
δ0
, δ ≡ 1 + ------
0 3µ 3µ 1 1 δ0  3µ 3 2 ξnTR+ 1 f 1 Eq. 5•305

The deviatoric part of the consistent tangent moduli Eq. 5•285 is modified accordingly with superscript “s”
denote symmetrized

ep
a dev n+1 ≡ a dev
TR
n+1
TR
– f 0 h dev n+1 – δ1 ( n̂ ⊗ n̂ ) – δ 2 ( n̂ ⊗ dev [ n̂ 2 ] ) s Eq. 5•306

Three-Field Hu-Washizu Variational Principle for Finite Deformation Elastoplasticity


The three-field Hu-Washizu variational principle leads to the B-formulation in infinitesimal deformation
case. However, it has been found by Simo et. al.[1985]2, that B-formulation does not carry over to the finite
deformation case. Many “garbage” terms generated because of the non-linearity, if one insists to cast the formu-
lation in B-matrix. Retaining standard formulation Simo[1988]3 advocates the standard strain-displacement
matrix (B-matrix) formulation. The Lagrangian functional for the three-field Hu-Washizu variational principle is

1. J.C. Simo, 1988, “A Framework for Finite Strain Elastoplasticity Based on Maximum Plastic Dissipation and the Multi-
plicative Decompositions: Part I. Continuum Formulation.” Computer Methods in Applied Mechanics and Engineering, vol.
66, p. 199-219. “Part II. Computational Aspects.”, vol. 68, p.1-31.
2. J.C. Simo, R.L. Taylor, and K.S. Pister, 1985, “Variational and Projection Methods for the Volume Constraint in Finite
Deformation Elasto-Plasticity”, Computer Methods in Applied Mechanics and Engineering, vol. 51, p. 177-208.

606 Workbook of Applications in VectorSpace C++ Library


Finite Deformation Elastoplasticity

Π ( φ, Θ, p ) ≡ ∫ [ U ( Θ ) + Ψ ( E, E p ) + p ( J – Θ ) ] dΩ +Πext Eq. 5•307


Proceed as in the linear case for the Hu-Washizu variational principle, the Euler-Lagrange equations are a set of
simultaneous equations and is reduced to displacement field only formulation. Linearization with respect to vari-
ations at tn+1, we obtain the tangent stiffness as (η is the variation about φn+1)

1 T
--- ∇ η :a: ∇ u = ∇ T η : [ σ ∇ u ] + ∇ T η : [ p ( 1 ⊗ 1 – 2I ) + a dev
ep ] :∇ u Eq. 5•308
J

The first term in the right-hand-side is the geometrical stiffness. The second term, which depends on the material
moduli, can be cast in the B-matrix formulation with an averaged volumetric term as

∫ B T [ adev
ep
n+1 ( Θ ) N’ N’]vol ( φ n + 1 )
– 2pI ] B dV + [ U’’
T
Eq. 5•309
Ωe

U(Θ) is the volumetric part of the free energy function, where Θ is the element mean dilatation, and N’ is the
averaged derivatives of shape function over the element.

5.4.4 Implementations of Stress-Strain Path Integration for Finite Deformation


In the stress-strain path integration scheme for the small deformation case, we introduce the closest-point
projection method to implement the radial return mapping algorithm. We mentioned that the cutting-plane
method will be used in this section. The point of departure of these two methods is the cutting-plane method bas-
ing on its integration at φn+1, while the closest-point projection method basing on its integration at φn.

sTRn+1
elastic predictor
plastic corrector
sn+1 elastic predictor sTRn+1
plastic corrector

sn sn sn+1
Yield Surface Yield Surface

cutting-plane closest-point projection

Figure 5•23 Cutting-plane method and closest-point projection method.

3. J.C. Simo, 1988, “A Framework for Finite Strain Elastoplasticity Based on Maximum Plastic Dissipation and the Multipli-
cative Decompositions: Part I. Continuum Formulation.” Computer Methods in Applied Mechanics and Engineering, vol. 66,
p. 199-219. “Part II. Computational Aspects.”, vol. 68, p.1-31.

Workbook of Applications in VectorSpace C++ Library 607


Chapter 5 Advanced Finite Element Methods
In the cutting-plane method, the spatial metric g is taken to coincide with identity of a Cartesian coordinate
system. The intermediate configuration needed to be inverted from stress through the constitutive law at the end
of each time step. With this stress inversion, the covariance of the cutting-plane method follows directly from the
covariance of the constitutive law. The secret of the trade is, at the trial elastic step, by definition, no plastic flow
is assumed. Plastic internal variables remain unchanged. So, push-forward of nothing is nothing, therefore,
push-forward operation can be skipped. At the plastic step, the plastic flow occurs at fixed current configuration
φn+1. Again, adroitly avoid the need for push-forward. However, the stress inversion step for the intermediate
configuration turns out to be very complicated.

In the closest-point projection method, the integration is done at φn then push-forward by Fu (the incremental
deformation gradient) to the current configuration φn+1. The push-forward of the intermediate configuration and
its internal plastic variables at the finite deformation range makes the algorithm not only incremental objective,
but covariant.

Computationally, the stress inversion step of cutting-plane method is the trade-off for the push-forward step
in the closest-point projection method. The algorithm for the closest-point-projection is aesthetically more satis-
fying but requires the introduction of the advanced covariant concept.

Implementation 1: Cutting-Plane Method


The project file for the implementation 1 is “test_finite_deformation.exe” in project workspace file “fe.dsw”.

Deformation Gradient from Shape Derivatives at φn+1 and the Mean Dilatational Approximation : The shape
derivative code (see Program Listing 5•22) is different from the infinitesimal case that it is evaluated at x = φn+1,
in place of X = φ0. The spatial metric tensor g = i (spatial identity), instantaneously coincide with a Cartesian
coordinates system. Recall x = X + u,

F = dx / dX = d(X+u) / dX = I + (du / dx) (dx / dX) = I + (grad u) F Eq. 5•310

The lower case “grad”, in convention, denotes derivatives with respect to spatial coordinates x. Post-multiply by
F-1, we have

F -1 = I - grad u Eq. 5•311

We first compute F-1 from grad u. The inverse of F-1 gives the deformation gradient, F. And evaluate the Jaco-
bian from the determinant of the deformation gradient as J = det(F). Remember for the following computation
all the available quantities are center around x = φn+1.
The mean dilatation Θ is

Θ=v/V Eq. 5•312

where the current volume is v = ∫ d v , and the initial volume is V = ∫ ------ , both integration are done at the cur-
dv
φ ( Ωe ) φ ( Ωe ) J
rent configuration φn+1.

The volumetric store energy function U(Θ)

608 Workbook of Applications in VectorSpace C++ Library


Finite Deformation Elastoplasticity

C0 u(nen, ndf, (double*)0);


for(int i = 0; i < nen; i ++)
for(int j = 0; j < ndf; j++) u[i][j] = ul[i*ndf+j];
H1 x = N*(xl+u); xn+1 = X + u
H0 Nx = d(N) * d(x).inverse();
J dv(d(x).det());
. . .
. . .
{ F -1 = I - grad u
H0 F_inv = I_22 - (~u)*Nx;
F = F_inv.inverse();
F = (F -1)-1
j = F.det(); J = det(F)
}
H0 unit(qp); unit = 1.0; V = ∫ dv
------ , and v = ∫φ( Ω )dv
C0 VOL = (1.0/j) | dv, φ( Ω e ) J e
vol = unit | dv,
theta = vol / VOL; Θ=v/V
C2 THETA(theta), K
U = K_*((log(THETA)).pow(2))/2.0; U ( Θ ) = ---- ( log Θ ) 2
p = d(U);
2
H0 J_two_third = exp(log(j)*2.0/3.0); p = U' = dU / dΘ, and J 2 / 3

Listing 5•22 Deformation gradient and mean dilatation approximation evaluated from configuration at x
= φn+1.

K
U ( Θ ) = ---- ( log Θ ) 2 Eq. 5•313
2

where K is the bulk modulus. p = U' = dU / dΘ, and U'' will be used in the calculation of tangent stiffness.
Again with VectorSpace C++ Library the evaluation of these derivatives is quite simple. We just need to declare
U and Θ to be a function and an independent variable, respectively, of C2 class. Alternatively, with such simple
equation, you might want to do the differentiation by hand, and code the result directly. In addition, because
pow(int) function only takes integer exponent, J2/3 is expressible as functions of exp() and log() by the equation
J2/3 = elog(J)2/3.
The update procedure for implementation 1 is to map the history data {Θn, pn, Jn, Fn, σn, (Fp)-1n, epn, αn} at
tn to {Θn+1, pn+1, Jn+1, Fn+1, σn+1, (Fp)-1n+1, epn+1, αn+1} at tn+1. We omit the programming details, since the
implementation to construct object-oriented private data members is completely parallel to that of the infinitesi-
mal case.

Elastic-Predictor and Plastic-Corrector: The elastic predictor (see Program Listing 5•23) assumes that the trial
plastic deformation gradient at tn+1 is Fnp +TR 1 ≡ F n ; i.e., no new plastic flow occurs. Therefore, the internal plas-
p

tic variables remain unchanged. Although the configuration has changed from φn to φn+1, push forward of zero
(no change of internal plastic variables) is zero. Then, the trial elastic deformation gradient at the current config-
p –1
1 ≡ F n + 1 ( F n ) . The deviatoric part of the Kirchhoff stress is calculated from the constitutive
uration is F ne +TR
equation, which has already been chosen to be covariant, as

s TR ≡ dev ( τ nTR+ 1 ) = µ dev ( b̂ ne +TR


1 ) Eq. 5•314

Workbook of Applications in VectorSpace C++ Library 609


Chapter 5 Advanced Finite Element Methods
where b̂ ne +TR –2 / 3 e
1 = Jn + 1 bn + 1
is the trial elastic volume-preserving left-Cauchy-Green tensor and s = {sxx, syy, szz,
sxy} is expressed in engineering convention as a vector. The covariance of the constitutive equation leads
directly to the covariant nature of the stress-strain path integration algorithm.
The formal additive decomposition de + dp = d must always hold no matter plastic flow occurs or not. The
additive decomposition is not contradictory to the multiplicative decomposition when plastic flow occurs. The
multiplicative decomposition is viewed as an additional (stronger) assumption, or as an additional equation, on
top of the additive decomposition. If the yield condition is violated, the plastic flow occurs at the fixed current
configuration φn+1 (with d remains unchanged). dp gains from de as a retribution for violating the consistency
condition, and the very value of dp is also determined from the consistency condition with the plastic-corrector
per se. With this interpretation, since φn+1 is fixed during the plastic deformation, no push-forward is necessary.
The stress-strain path integration algorithm is naturally covariant as long as the elastic-predictor step is covari-
ant.
The plastic-corrector phase therefore reduces to an infinitesimal case around φn+1. It’s algorithm is exactly
the same as in the infinitesimal case. Therefore the implementation aspect is skipped here. The beauty of the
Implementation 1 is that in both the elastic-predictor and the plastic-corrector the push-forward is tacitly
avoided.

H0 s(4, (double*)0, qp); s = {sxx, syy, szz, sxy} is dev(τTRn+1)


H0 mu_bar;
{
1 ≡ F n , and F n + 1 ≡ F n + 1 ( F n )– 1
Fnp +TR p e TR p
F_p_inv = F_p_inv_n;
H0 TR_F_e = F * F_p_inv,
TR_b_e = INTEGRABLE_MATRIX("int, int, Quadrature", 3, 3, qp),
ref_TR_b_e =INTEGRABLE_MATRIX("int, int, H0&, int, int, Quadrature",
be = Fe (Fe)T
2, 2, TR_b_e, 0, 0, qp);
ref_TR_b_e = TR_F_e *(~TR_F_e); b̂ ne +TR –2 / 3 e
1 = Jn + 1 bn + 1
TR_b_e[2][2] = 1.0;

dev ( τ TR n + 1 ) = µ dev ( b̂ ne +TR


H0 TR_b_hat_e = TR_b_e / J_two_third,
dev_TR_b_hat_e = TR_b_hat_e - (1.0/3.0) * tr(TR_b_hat_e) * I_33, 1 )
dev_TR_tau = mu_ * dev_TR_b_hat_e;
mu_bar &= (mu_/3.0) * tr(TR_b_hat_e);
s[0] = dev_TR_tau[0][0]; s[1] = dev_TR_tau[1][1]; 1
s[2] = dev_TR_tau[2][2]; s[3] = dev_TR_tau[0][1]; µ ≡ --- µ tr [ b̂ ne +TR
1 ]
3

Listing 5•23 Elsatic predictor assume no plastic flow occurs. The deviatoric Kirchhoff stress s, in engi-
neering convention, is evaluated from the consititutive law

Intermediate Configuration: We mentioned that the covariance of this stress-strain path integration algorithm
follows directly from the covariance of the constitutive law. We did not mention how do we get the (Fpn)-1 in the
above algorithm to furnish the covariance requirement.

We first describe a tactic change on stress inversion algorithm, from Simo et al[1985]1, adopted in the imple-
mentation of this workbook. At the initial step of the incremental loading step (Fp0)-1 can be assumed to be an
identity matrix, for no deformation has occurred. The tensor (Fp0)-1 remains unchanged during the same time

1. J.C. Simo, R.L. Taylor, and K.S. Pister, 1985, “Variational and Projection Methods for the Volume Constraint in Finite
Deformation Elasto-Plasticity”, Computer Methods in Applied Mechanics and Engineering, vol. 51, p. 177-208.

610 Workbook of Applications in VectorSpace C++ Library


Finite Deformation Elastoplasticity
p
step. When we get a converged result, we will like to end the first time step, (F 1)-1 will be computed from the
deviatoric part of the Kirchhoff stress tensor through constitutive equation (where the covariance nature of the
stress-strain path integration algorithm is relied on). We defer the description of how to compute this step later.
However, the value of (Fpn+1)-1 is most likely to be computed at the element level. In the element level, we do not
p
know beforehand that if the result is going to converge. One way to do it is to compute (F n+1)-1 at every itera-
p
tion, so it will always be ready. This is certainly very costly, especially when the computation of (F n+1)-1 is very
complicated. The intermediate configuration computation step is labelled “Step 3” follows the “Step 1 and 2” of
the elastic-predictor / plastic-corrector steps in Simo et al.[1985, p. 199]. The other way around is to compute
(Fpn)-1 at the beginning of every time step tn+1, except at the first time step (when it is assumed to be identity).
This also has the advantage that at the last time step we do not need to compute (Fpn)-1, since there is no next time
step which needs to use this value. Therefore, we code the stress inversion of the intermediate configuration step
before the elastic-predictor / plastic-corrector steps. It is then more appropriate to think of this step as “Step 0” to
get (Fpn)-1.
The step to compute (Fpn)-1 is very complicated (see Program Listing 5•24). This is the price to pay for avoid-
ing the push-forward operations (see Implementation 2) demanded by covariance requirement. “Step 0” is subdi-
vided into following sub-steps:

(a) get the Kirchhoff stress from the Jacobian and the Cauchy stress— τ n = J σ n from Eq. 5•275.

(b) compute dev ( b̂ ne ) from the deviatoric part of the constitutive law— dev ( b̂ ne ) = --- dev ( τn ) from Eq. 5•296.
1
µ

(c) compute b̂ ne from its deviatoric part dev ( b̂ne ) and requiring det ( b̂ne ) = 1.

(d) compute the elastic left-Cauchy-Green tensor from the elastic volume-preserving left-Cauchy-Green tensor—
b ne = J – 2 / 3 b̂ ne by definition.

(e) compute the left-stretch tensor ( Vne ) from the left-Cauchy-Green tensor ( bne )— Vne = b ne .

(f) set Fne = V ne , this is valid for isotropic case, and compute F pn = F n (F en)-1 from the multiplicative decompo-
sition (Eq. 5•287). Then, (F pn)-1 can be obtained.
Except for sub-steps (c) and (e), the other sub-steps are straight forward without explanation. The essence of
sub-step (c), for those who familiar with solid mechanics, is similar to the calculation of principle deviatoric
stress from a deviatoric stress. See C.Y. Fung[1965, p. 80]1, or L.E. Malvern[1969, p. 91]2.
Kinematic split of the elastic volume-preserving left-Cauchy-Green tensor b̂ ne is

1
b̂ ne = dev ( b̂ ne ) + Ξ , where Ξ = --- tr ( b̂ ne ) . Eq. 5•315
3

The objective of the problem at hand is to compute the spherical part Ξ , then adds to the given deviatoric part,
requiring that det ( b̂ ne ) = 1 —add trace to a unimodular deviatoric tensor. Assuming plain strain, we define

1. “Foundations of Solid Mechanics”, Prentice-Hall, Inc.


2. “Introduction to the Mechanics of a Continuous Medium” Prentice-Hall, Inc.

Workbook of Applications in VectorSpace C++ Library 611


Chapter 5 Advanced Finite Element Methods

if(new_time_flag) { τn = Jσn
H0 dev_b_hat_n_e = INTEGRABLE_MATRIX("int, int, Quadrature", 3, 3, qp),

dev ( τ n ) = τ n – 1--3- tr ( τn )
dev_tau_n = INTEGRABLE_MATRIX("int, int, Quadrature", 3, 3, qp),
tau_n = J_n*sigma_n, tr_tau_n = tau_n[0]+tau_n[1]+tau_n[2];
dev_tau_n[0][0] = tau_n[0] - (1.0/3.0)*tr_tau_n; dev_tau_n[1][1] = tau_n[1]-(1.0/3.0)*tr_tau_n;
dev ( b̂ ne ) = --- dev ( τ n )
dev_tau_n[2][2] = tau_n[2] - (1.0/3.0)*tr_tau_n; dev_tau_n[0][1] = dev_tau_n[1][0] = tau_n[3]; 1
dev_b_hat_n_e = dev_tau_n / mu_; µ
H0 bp = dev_b_hat_n_e, theta_0(qp),
J2 = (bp[0][0].pow(2)+bp[1][1].pow(2)+bp[2][2].pow(2))/2.0 + bp[0][1].pow(2);
#if defined(__NUMERICAL_ROOT_FINDING)
for(int i = 0; i < qp.no_of_quadrature_point(); i++) { 1
C0 J2_q = J2.quadrature_point_value(i), theta_0_q = theta_0.quadrature_point_value(i), J 2 ≡ --- [ ( b’11 ) 2 + ( b’22 ) 2 + ( b’33 ) 2 ]
2
bp_q=bp.quadrature_point_value(i), J3_prime = (J2_q-bp_q[2][2].pow(2))*bp_q[2][2] + 1.0;
int MAX_ITERATION_NO = 50, count = 0; double EPSILON = 1.e-6;
+ ( b’12 ) 2 + ( b’13 ) 2 + ( b’23 ) 2
C1 X(1.0), f; C0 d_X(0.0);
do { J’3 = J 3 – I 3
f &= X.pow(3) - J2_q*X - J3_prime; d_X = -((C0)f)/d(f); ((C0)X) += d_X;
} while((double)norm(d_X) > EPSILON && ++count < MAX_ITERATION_NO);
if(count == MAX_ITERATION_NO) ofs << f ( Ξ ) = Ξ 3 – J 2 Ξ – J’3
"Warning: No convergence achieved for solution of a cubic algebraic equation!" << endl;
f
theta_0_q = ((C0)X); d Ξ = – ----- , and updating by Ξ += d Ξ
} df
#else
for(int i = 0; i < qp.no_of_quadrature_point(); i++) { Closed-form solution
C0 J2_q = J2.quadrature_point_value(i), theta_0_q = theta_0.quadrature_point_value(i);
if((double)J2_q < 1.e-6) theta_0_q = 1.0;
else {
C0 bp_q=bp.quadrature_point_value(i), Octahedral deviatoric tensor
J3_prime=(J2_q-bp_q[2][2].pow(2))*bp_q[2][2]+1.0,
2J
bp_oct = sqrt((2.0/3.0)*J2_q), temp = (-J2_q/3.0).pow(3)+(J3_prime/2.0).pow(2);
b oct = -------2-
if((double)temp > 0.0) { 3
double a1, a2,
arg1=(double)(J3_prime/2.0+sqrt(temp)),arg2=(double)(J3_prime/2.0-sqrt(temp)); marginal conditions
if(fabs(arg1) < 1.e-6)a1=0.0; else a1=((arg1>=0.0) ? 1.0 : -1.0) * exp(logl(fabs(arg1))/3.0);
if(fabs(arg2) < 1.e-6)a2=0.0; else a2=((arg2>=0.0) ? 1.0 : -1.0) * exp(logl(fabs(arg2))/3.0);
theta_0_q = a1 + a2; J’3 3 3 / 2 2 J’3
} else { cos 3α = ------  ---- = ----------------
-
C0 cos_3alpha=J3_prime * sqrt(2.0) / bp_oct.pow(3), alpha = acos(cos_3alpha)/3.0; 2  J 2 3
b oct
theta_0_q = sqrt(2.0) * bp_oct * cos(alpha);
}
} J
} Ξ = 2 ----2- cos α
#endif
3
{
H0 b_hat_n_e = dev_b_hat_n_e + theta_0 * I_33; b̂ ne = dev ( b̂ ne ) + Ξ
H0 b_n_e = J_two_third * b_hat_n_e;
H0 V_n_e = INTEGRABLE_MATRIX("int, int, Quadrature", 2, 2, qp);
H0 I_b = b_n_e[0][0]+b_n_e[1][1],
1
I_b =b_n_e[0][0]*b_n_e[1][1]-b_n_e[0][1]*b_n_e[1][0]; V = ----------------------------------- ( b + IIb I )
V_n_e[0][0] = (b_n_e[0][0]+sqrt(II_b)) / sqrt(I_b+2.0*sqrt(II_b)); ( I b + 2 II b )
V_n_e[1][1] = (b_n_e[1][1]+sqrt(II_b)) / sqrt(I_b+2.0*sqrt(II_b));
V_n_e[0][1] = b_n_e[0][1] / sqrt(I_b+2.0*sqrt(II_b));
V_n_e[1][0] = b_n_e[1][0] / sqrt(I_b+2.0*sqrt(II_b));
H0 F_n_e = V_n_e;
F_p_inv_n = F_n_e * F_n.inverse();
F ne = V ne , F pn = F n (F en)-1, (F pn)-1
}

Listing 5•24 Computation of the intermediate configuration (F pn)-1.

612 Workbook of Applications in VectorSpace C++ Library


Finite Deformation Elastoplasticity
b’11 b’12 0
dev ( b̂ ne ) ≡ b’21 b’22 0 , where b’33 = 1 Eq. 5•316
0 0 b’33

Following standard procedure for finding principle values, it can be shown that the principle deviations b’ of Eq.
5•316 satisfy

b’3 – J 2 b’ – J 3 = 0 , and also Ξ 3 – J 2 Ξ – ( J 3 – I 3 ) = 0 Eq. 5•317

Numerical root-finding procedure can be applied to second part of Eq. 5•317. This is turned on by a macro defi-
nition “__NUMERICAL_ROOT_FINDING”. With the aid of the VectorSpace C++ Library this numerical pro-
cedure is straight forward without any explanation (see Chapter 2 for introduction). However, a closed-form
expression for this cubic algebraic equation is even more desirable. Considering that the last two equations are
almost identical. The solution for b’ can be use for solution of Ξ if one defines J' 3 = J 3 – I3 ; i.e., J'3 is used in
place of J3 in the formula for solving b’ . According to Malvern p. 922., the solution of the cubic algebraic equa-
tion is obtained by substituting

J Eq. 5•318
Ξ = 2 ----2- cos α
3

into Eq. 5•317. We get,

J 3
2  ----- [ 4 cos3 α – 3 cos α ] = J’3 ,
2
3 Eq. 5•319

With the trigonometrical identity

cos 3α = 4 cos3 α – 3 cos α Eq. 5•320

the value of cos 3α from Eq. 5•321 is

J' 3 3 3/2 2 J' 3 2J


cos 3α = ------  ----- = ----------------
- , where b oct = -------2- Eq. 5•321
2  J 2 3
b oct 3

The octahedral b (boct, the counterpart of the octahedral shear stress τo in C.Y. Fung1) does not really need to be
introduced, if one use the first identity only (as in L.E. Malvern, p.922). We can first invert for α from Eq. 5•321
e
then get to Ξ by Eq. 5•318. After solving for the value of Ξ , b̂ n is obtained from Eq. 5•315.
The sub-step (e) is the steps that one usually uses to compute numerical values of polar decomposition from a
deformation gradient tensor. We only need the middle step, for b = FFT is already available, and after we get V

1. “Foundations of Solid Mechanics”, Prentice-Hall, Inc.


2. “Introduction to the Mechanics of a Continuous Medium” Prentice-Hall, Inc.

Workbook of Applications in VectorSpace C++ Library 613


Chapter 5 Advanced Finite Element Methods
we do not proceed to get R. We refer to J.E. Marsden and T.J.R. Hughes[1983, p. 55]1. With the Cayley-Hamil-
ton theorem from linear algebra, for a two-dimensional case,

V 2 – Iv V + IIv I = 0 Eq. 5•322

where Iv = tr V, and IIv = det V. The solution of this equation can be shown as

1
V = ----------------------------------- ( b + II b I ) Eq. 5•323
( I b + 2 II b )

Since we have assumed plain strain, this two-dimensional formula is sufficient.

Consistent Tangent Moduli, Tangent Stiffness and Residual: The consistent tangent moduli (Eq. 5•284 with
deviatoric part Eq. 5•285 modified by Eq. 5•302-Eq. 5•306) is having without the push-forward and set g = i
(since the current configuration is described in an instantaneous Cartesian coordinates). The implementation is
transparent (see Program Listing 5•25). Some nuisance in the code is caused by the use of engineering conven-
tion of the deviatoric Kirchhoff stress (s) as a vector instead of its counterpart dev τ as a matrix. Naturally, Vec-
torSpace C++ Library, designed for general purpose numerical computation, does not have specific operators
defined for s. Therefore, we throw the VectorSpace C++ Library out of the window, s need to be computed com-
ponent by component just as you would do in C or Fortran. A better implementation will be to use object-ori-
ented method to extend a new derived class for this “engineering 1-D matrix”.

Implementation 2: Closest-point-projection Method


The project file for the implementation 2 is “test_finite_deformation_covariant.exe” in project workspace
file “fe.dsw”.

We mentioned that the beauty of implementation 1 is that no push-forward is necessary. Covariance is


achieved by choosing wisely the configuration for the stress-strain path integration algorithm without having to
do operations regarding to the covariant issue. However, “Step 0” (or “Step 3” in Simo et al.[1985]2) in imple-
mentation 1 is lengthy and costly. This is the price we pay for outsmarting the covariance requirement. This step
is, at least, untidy comparing to the rest of the implementation 1. In implementation 2 (based on Simo[1988]3),
we face the covariance directly and get rid of the above sloppy details. This implementation not only leads to a
neat algorithm, but its simplicity also reflects the theoretical satisfaction. The algorithm is more general and
applicable to other problems. We do not base the algorithm on maneuvers that are relevant only to the specific
problem at hand. We emphasize that the numerical results of the two implementation are almost the same, differ

1. “Mathematical Foundations of Elasticity”, Prentice-Hall, Inc.


2. J.C. Simo, R.L. Taylor, and K.S. Pister, 1985, “Variational and Projection Methods for the Volume Constraint in Finite
Deformation Elasto-Plasticity”, Computer Methods in Applied Mechanics and Engineering, vol. 51, p. 177-208.
3. J.C. Simo, 1988, “A Framework for Finite Strain Elastoplasticity Based on Maximum Plastic Dissipation and the Multi-
plicative Decompositions: Part I. Continuum Formulation.” Computer Methods in Applied Mechanics and Engineering, vol.
66, p. 199-219. “Part II. Computational Aspects.”, vol. 68, p.1-31.

614 Workbook of Applications in VectorSpace C++ Library


Finite Deformation Elastoplasticity

...
C0 n2(4, (double*)0);
n̂ 2
n2[0] = n[0]*n[0]+n[3]*n[3]; n2[1] = n[3]*n[3]+n[1]*n[1];
n2[2] = n[2]*n[2]; n2[3] = n[3]*(n[0]+n[1]); tr( n̂ 2 )
C0 trn2 = n2[0]+n2[1]+n2[2];
C0 dev_n2(4, (double*)0); dev_n2 = n2;
for(int i = 0; i < 3; i++) dev_n2[i] -= trn2/3.0;
dev( n̂ 2 ) = n̂ 2 - 1/3 tr( n̂ 2 )
C0 mu_2bar = mu_bar_q - (alpha_n_q[0]+alpha_n_q[1]+alpha_n_q[2])/3.0,
2µγ n + 1
µ ≡ µ – --- tr ( α )
f_0 = 2.0*mu_bar_q*lambda_q / ZAI_norm_q, 1
f0 ≡ ------------------
-
delta_0 = (1.0+d_H_alpha/3.0/mu_+d(KAPA)/3.0/mu_2bar),
f_1 = (1.0/delta_0-f_0),
3 , ξn + 1 ,
TR

delta_1 = f_1*2.0*mu_2bar -
(1.0/delta_0*(1.0+d_H_alpha/3.0/mu_)-1.0)*(4.0/3.0)*lambda_q, 1 h’ κ’
f 1 ≡ ----- – f 0 δ0 ≡ 1 + ------ + ------
delta_2 = 2.0*ZAI_norm_q*f_1; δ0 , 3µ
C0 TR_h_dev = 2.0 * mu_2bar * (I_mu - (One%One)/3.0) - 3µ
(2.0/3.0)*(ZAI_q%One+One%ZAI_q);
a_dev_q = TR_a_dev_q - f_0 * TR_h_dev - delta_1*(n%n) -
δ 1 ≡ f1 2µ – -----  1 + ------ – 1 --- γ n + 1
(delta_2/2.0)*(n%dev_n2+dev_n2%n); 1 h’ 4
} else { δ0 3µ 3
a_dev_q = TR_a_dev_q;
ofs << " Elastic state: element # " << en << ", quadrature point # " << i

}
<< ", yield ratio: " << yield_ratio_q << endl;
δ2 ≡ 2 ξnTR+ 1 f1
sigma_q = s_q/J_q + p*One;
1
} TR
a dev n+1 ≡ 2µ I – --- ( g ⊗ g ) –
. . . 3
2 TR
– --- ( s ⊗ g + g ⊗ s TR )
3
1
TR
h dev n+1 ≡ 2µ I – --- ( g ⊗ g )
. . . 3

– --- ( ξ ⊗ g + g ⊗ ξ TR )
2 TR
3

. . .
ep
a dev n+1 ≡ a nTR
+ 1 – f 0 h n + 1 – δ 1 ( n̂ ⊗ n̂ )
TR

– δ 2 ( n̂ ⊗ dev [ n̂ 2 ] ) s

C0 stiff_geometrical, stiff_material; σ = J-1 τ = s / J + p 1


{

∇Tη : [ σ ∇u ]
C0 e = BASIS("int", ndf),
E = BASIS("int", nen),
U = (e%e)*(E%E);
H0 fact = wx*(sigma[0]*~wx+sigma[3]*~wy) + wy*(sigma[3]*~wx+sigma[1]*~wy);
stiff_geometrical &= (+(fact*U[0][0]+ fact*U[1][1])) |dv; ∫ B T [ a dev
ep – 2pI ]B dv
} φ ( Ωe ) n+1
{
C0 dN_bar = (( (~wx) || (~wy) ) | dv) / vol; T
stiff_material &= ( ((~B) * (a_dev/j + p*((One%One) - 2.0*I_mu)) * B) | dv ) + [ U’’( Θ ) N’ N’]vol [ φ n + 1 ]
+ dd(U) * theta * ((~dN_bar)*dN_bar) * vol;
}
stiff &= stiff_geometrical + stiff_material; ∫ B T σ dv
force &= -((~B)*sigma)|dv; φ ( Ωe )

Listing 5•25 Consistent tangent moduli, tangent stiffness and residual.

Workbook of Applications in VectorSpace C++ Library 615


Chapter 5 Advanced Finite Element Methods
only up to round-off error, let alone the qualitative results of the problem. In the following only the differences in
the two implementations are discussed.
The stress-strain path integration method is the closest-point-projection. The over-all algorithm is therefore
closer to the infinitesimal case, comparing to the Implementation 1. Conceptually, Implementation 2 only differs
from infinitesimal case in that now the finite deformation kinematics is used. With the closest-point-projection
method the integration step is to update the history data {Θn, pn, Jn, Fn, σn, b̂ e n, e pn, αn} at tn to {Θn+1, pn+1,
Jn+1, Fn+1, σn+1, b̂ en+1,e pn+1,αn+1} at tn+1. For updating intermediate configuration, b̂ is used in place of (F p)-1
e
p -1
in the history data of the implementation 1. In implementation 1, (F ) relies on covariance of constitutive equa-
tion to keep the algorithm covariant (the infamous “Step 0” in Implementation 1). In the present implementation,
b̂ is covariant by simply push-forward to φn+1 with the volume-preserving incremental deformation gradient
e
ˆ
Fu .
Simo[1988]1 listed six steps for closest-point-projection method described in the following two sections.
ˆ
Volume-Preserving Incremental Deformation Gradient ( F u ) from Shape Derivatives at φn or φ0 : Step 1:
ˆ
geometric update with volume-preserving incremental deformation gradient— F u . Note a given incremental dis-
placement at the current and referential configuration is ∆u(φn(X)) = ∆U(X). The current configuration is update
through φn+1 = φn + ∆U. The deformation gradient tensor is updated by Fn+1 = Fn + GRAD ∆U = [I + ∇n∆u] Fn =
ˆ
Fu Fn, where Fu = [I + ∇n∆u], is the incremental deformation gradient. Fu = det(Fu) -1 / 3 Fu, is the volume-pre-
serving incremental deformation gradient tensor. There are two ways to do this. The macro definition
__REFERENCE_DERIVATIVES turn on code segment that implements shape derivatives base on φo.
The first way computes the shape derivatives from X = φo. Then the deformation gradient F is obtained first
through F = Fn + GRAD ∆U. Then the incremental deformation gradient is computed from Fu = F Fn-1. The sec-
ond way computes the shape derivatives from xn = φn. Then reversing the process, first get the incremental
deformation gradient by Fu = I + grad ∆u. Then the deformation gradient F is obtained from F = Fu Fn.The com-
ˆ
putation of J, Ju and F u are simply obtained from the definitions.

Incremental Covariant Algorithm: Step 2-Step 5 These steps include computing the elastic-predictor, the plas-
tic-corrector, the radial return mapping algorithm, and the consistent tangent moduli. They are the same as in the
ˆ
Implementation 1, except we need to push-forward b̂ n and αn by F u in the elastic-predictor and the radial
e

return mapping algorithm. In the elastic predictor step,

αnTR+ 1 ≡ dev [ Fˆ u αn ( Fˆ u )
T ˆ ˆ T
] , and b̂ nTR
+ 1 ≡ F u b̂ n ( F u ) Eq. 5•324

In the radial return mapping step,

ˆ T
µ ≡ µ – --- tr [ F u α n ( F u ) ]
1 ˆ
Eq. 5•325
3

1. J.C. Simo, 1988, “A Framework for Finite Strain Elastoplasticity Based on Maximum Plastic Dissipation and the Multi-
plicative Decompositions: Part I. Continuum Formulation.” Computer Methods in Applied Mechanics and Engineering, vol.
66, p. 199-219. “Part II. Computational Aspects.”, vol. 68, p.1-31.

616 Workbook of Applications in VectorSpace C++ Library


Finite Deformation Elastoplasticity

H0 F_u_bar = INTEGRABLE_MATRIX("int, int, Quadrature", 3, 3, qp);


{
C0 du(nen, ndf, (double*)0);
∆u(φ(X)) = ∆U(X)
for(int i = 0; i < nen; i ++) Method 1: based on φo
for(int j = 0; j < ndf; j++) du[i][j] = delta_ul[i*ndf+j]; X = φo
#if defined(__REFERENCE_DERIVATIVES)
H1 X = N*(xl);
d N / d X = (d N / dξ) (d X / dξ)-1
H0 NX = d(N) * d(X).inverse(); F = Fn + GRAD ∆U
= Fn + ∆U (d N / dX)
F = F_n + (~du)*NX; T
H0 F_u = F * F_n.inverse(); -1
#else
Fu = F Fn
H1 x_n = N*(xl+u-du); Method 2: based on φn
H0 Nx_n = d(N) * d(x_n).inverse(); x = φn
H0 F_u = I_22 + (~du)*Nx_n;
F = F_u * F_n;
d N / dx = (d N / dξ) (d x / dξ)-1
#endif Fu = I + grad ∆u = I + ∆uT (d N / dx)
j = F.det(); F = Fu Fn
H0 J_u = F_u.det();
H0 F_u_bar_22 = 1.0/exp(log(J_u)/3.0) * F_u;
J = det F
F_u_bar[0][0] = F_u_bar_22[0][0]; Ju = det Fu
F_u_bar[1][1] = F_u_bar_22[1][1]; Fu = Ju-1/3 Fu
F_u_bar[0][1] = F_u_bar_22[0][1];
F_u_bar[1][0] = F_u_bar_22[1][0];
F_u_bar[2][2] = 1.0/exp(log(J_u)/3.0);
} J2/3
H0 J_two_third = exp(log(j)*2.0/3.0);
H0 g(4, (double*)0, qp);
{
∂Z r ∂Z s
H0 Zx = (~xl)* Nx; - --------- δ
g mn = ---------
g[0] = Zx(0)*Zx(0); ∂x m ∂x n rs
g[1] = Zx(1)*Zx(1);
g[2] = 1.0;
∂Z 1 ∂Z 1 ∂Z 2 ∂Z 2 ∂Z 3 ∂Z 3
g[3] = Zx(0)*Zx(1); = ---------
- --------- + ---------- --------- + ---------- ---------
} ∂x m ∂x n ∂x m ∂x n ∂x m ∂x n

Listing 5•26 Incremental deformation gradient and spatial metric tensor.

This definition of µ is also used in the consistent tangent moduli. The radial return mapping also include updat-
ing history data

2
e np + 1 = e np + --- γ n + 1 Eq. 5•326
3

αn + 1 = α TR n + 1 + -----
h’

- 2µγ n + 1 n̂ n + 1 Eq. 5•327

s n + 1 = s TRn + 1 – 2µγn + 1 n̂ n + 1 Eq. 5•328

The spatial metric tensor g is now in place of 1 in “Implementation 1” used in implementation 1. The present
implementation is straight forward except, again, minor nuisance caused by engineering convention that is not
supported by VectorSpace C++ Library.

Workbook of Applications in VectorSpace C++ Library 617


Chapter 5 Advanced Finite Element Methods
Update intermediate configuration: Step 6: Update intermediate configuration is brand new but neat com-
pared to implementation 1. Thus, we can afford to compute this value in each iteration no matter the result will
attain convergence or not.

2µγ
1 – --------- tr [ b̂ n + 1 ] n̂ n + 1
b̂ ne + 1 = b̂ ne +TR e TR
Eq. 5•329

The finite deformation formulation for the implementation 1 for cutting-plane method is implemented in
project “finite_elastoplasticity” and the implementation 2 for closet-point-projection method is implemented in
project “covariant_finite_elasticity”. Both projects are in project file “fe.dsw”. Figure 5•24 shows the strip with
circular whole under 0.33 % (6 incremental steps) and 0.56 % (10 incremental steps) vertical stretching Figure
5•24a and b, respectively. At 0.33% stretching the deformation mode is similar to the infinitesimal case up to
0.56 % stretching (Figure 5•24c). At 0.56 % stretching the high yield ratio area is confined to the bottom edge of
the model. This is the “necking” process developed. After 1/10 (180 incremental steps) vertical stretching is
shown in Figure 5•25a. Necking is developed into a clear stage.

Line Search: The time consuming nonlinear iterative algorithm becomes clearly a problem. 180 incremental
step, say with average of five iteration for each incremental step means that we will have computation time that
is about a thousand times that of a linear problem. The problem is even more serious if we consider to stretch the
strip with circular hole problem up to 1/3 as shown in Figure 5•25b. That will need about four thousand times
that of a linear problem. A lot of computing time will be required. One immediate solution is to increase incre-
mental loading 10 times by using δu = 0.1 instead of δu = 0.01 for each incremental loading step. However, with
such magnitude of incremental loading, the nonlinear iteration fails to converge even for the first time step. As
we recalled from Chapter 2 the classic Newton method is powerful that its convergent rate is quadratic. How-
ever, the Newton method often leads to wild search path and may easily fail to converge. Therefore, a line search
method with golden section can be use to regulate the search path of the Newton method (see page 125 in Chap-
ter 2). This is implemented as the follows

1 Matrix_Representation::Assembly_Switch = Matrix_Representation::ALL;
2 mr.assembly();
3 new_time_flag = FALSE; // flag to turn off Step 3 of the finite deformation procedure, after first iteration
4 C0 p = ((C0)(mr.rhs())) / ((C0)(mr.lhs()));
5 energy = fabs( (double)(p * ((C0)(mr.rhs())) ) );
6 if(count == 1) { // first iteration only
7 for(int j = 86; j <= 92; j++) {
8 gh[j][1] += d_gh[j][1];

b_e_hat_q[0][0] = TR_b_e_hat_q[0][0]-mu_2bar_gamma/(3.0*mu_)*tr(TR_b_e_hat_q)*n[0];
b_e_hat_q[1][1] = TR_b_e_hat_q[1][1] - mu_2bar_gamma/(3.0*mu_)*tr(TR_b_e_hat_q)*n[1];
b_e_hat_q[2][2] = TR_b_e_hat_q[2][2] - mu_2bar_gamma/(3.0*mu_)*tr(TR_b_e_hat_q)*n[2];
2µγ
1 – --------- tr [ b̂ n + 1 ] n̂ n + 1
b̂ ne + 1 = b̂ ne +TR
b_e_hat_q[0][1] = b_e_hat_q[1][0] = e TR
TR_b_e_hat_q[0][1] - mu_2bar_gamma/(3.0*mu_)*tr(TR_b_e_hat_q)*n[3]; 3µ

Listing 5•27 Update intermediate configuration.

618 Workbook of Applications in VectorSpace C++ Library


Finite Deformation Elastoplasticity

3.50 & up
3.00
2.50
2.00
1.50
1.00
0.75
0.50

(a) finite formulation (c) infinitesimal formulation 0.25


(b) finite formulation
0.33 % stretching 0.56 % stretching 0.56 % stretching 0.00
Figure 5•24 (a) and (b) yield ratios of finite formulation computation after 0.33 % and
0.56 % stretching. At 0.56 % streching the plastic zone has developed into a “necking”
process. (c) comparing to the yield ratio from previous infinitesimal formulation at 0.56
% stretching, which is still under the same deformation mode as that in (a).

(a) stretching 1/10


(b) stretching 1/3
vertically vertically

Figure 5•25 The further development of the “necking” of the strip with (a) 1/10
vertical stretching, and (b) 1/3 vertical stretching.

Workbook of Applications in VectorSpace C++ Library 619


Chapter 5 Advanced Finite Element Methods
9 delta_gh[j][1] = d_gh[j][1];
10 d_gh[j][1] = 0.0;
11 }
12 uh = gh;
13 delta_uh = delta_gh;
14 d_uh = d_gh;
15 }
16 if(count != 1 && energy > energy_old) { // abandant the current solution and activate line search
17 cout << " energy: " << energy << endl;
18 U_h uh_temp(ndf, oh); U_h delta_uh_temp(ndf, oh); // save current total displacement
19 for(int j = 0; j < uh_temp.total_node_no(); j++) { // and total increment of displacement
20 uh_temp[j] = uh[j]; delta_uh_temp[j] = delta_uh[j];
21 }
22 double alpha = 0.0, // line search parameter
23 left = 0.0, right = 1.0, length = right-left; // initial bracket
24 int line_search_counter = 0;
25 do {
26 (C0)(mr.rhs()) = 0.0;
27 double golden_alpha; alpha = golden_alpha = (left + 0.618 * length);
28 for(int j = 0; j < uh_temp.total_node_no(); j++) { // restore
29 uh[j] = uh_temp[j]; delta_uh[j] = delta_uh_temp[j];
30 }
31 delta_uh += alpha*p; // update total increment of displacement
32 uh += alpha*p; // update total displacement
33 Matrix_Representation::Assembly_Switch = Matrix_Representation::RHS;
34 mr.assembly(); // form residual vector (rhs)
35 double golden_residual = norm( (C0)(mr.rhs()) );
36 C0 mr_rhs_temp = (C0)(mr.rhs());
37 (C0)(mr.rhs()) = 0.0;
38 double left_alpha; alpha = left_alpha = (left + 0.382 * length);
39 for(int j = 0; j < uh_temp.total_node_no(); j++) { // restore
40 uh[j] = uh_temp[j]; delta_uh[j] = delta_uh_temp[j];
41 }
42 delta_uh += alpha*p; // update total increment of displacement
43 uh += alpha*p; // update total displacement
44 Matrix_Representation::Assembly_Switch = Matrix_Representation::RHS;
45 mr.assembly(); // form residual vector (rhs)
46 double left_residual = norm( (C0)(mr.rhs()) );
47 if(golden_residual < left_residual) {
48 left = left + 0.382 * length;
49 alpha = golden_alpha;
50 (C0)(mr.rhs()) = mr_rhs_temp;
51 } else {
52 right = left + 0.618 * length;
53 alpha = left_alpha;

620 Workbook of Applications in VectorSpace C++ Library


Finite Deformation Elastoplasticity
54 }
55 length = right-left;
56 cout << line_search_counter << " alpha: " << alpha <<
57 " current energy: " << fabs( (double)((alpha*p)*((C0)(mr.rhs()))) ) << endl;
58 } while (line_search_counter++ < 10);
59 du = alpha * p;
60 for(int j = 0; j < uh_temp.total_node_no(); j++) { // restore
61 uh[j] = uh_temp[j]; delta_uh[j] = delta_uh_temp[j];
62 }
63 } else du = p;
64 residual_norm = norm((C0)(mr.rhs()));
65 energy_old = energy;
66 if(count == 1) energy_0 = energy;
67 cout << " energy: " << fabs( (double)(du*((C0)(mr.rhs()))) ) << endl;
68 (C0)(mr.lhs()) = 0.0;
69 (C0)(mr.rhs()) = 0.0;

The line search method is activated by setting macro definition “__GOLDEN_SECTION_LINE_SEARCH” at


compile time in project “finite_elastoplasticity” in project workspace file “fe.dsws”. With this line search method
the incremental loading can be set at δu = 0.1. The result in Figure 5•25b is obtained with only 60 incremental
loading steps.

Workbook of Applications in VectorSpace C++ Library 621


Chapter 5 Advanced Finite Element Methods

622 Workbook of Applications in VectorSpace C++ Library

Das könnte Ihnen auch gefallen