Class documentation of Concepts

Loading...
Searching...
No Matches
elasticity2D_tutorial.cc

Contents

  1. Introduction
  2. Reduction to 2-dimensional elasticity
  3. Variational Formulation
  4. Commented Program
  5. Results
  6. Complete Source Code

Introduction

In this tutorial the implementation of linear elasticity problems in 2D is shown. We consider a thin plate with a hole at its centre. The plate is fixed at its left face and and there is some applied force on its right face. This is shown in the following figure. Sketch of problem First we will give a short overview of the equations of linear elasticity in three dimensions and then reduce the problem to two dimensions.

The equation to be solved is

\[
      - {\rm div} \:\sigma(u) = f  \quad \text{in } \Omega,
\]

for the unknown displacement $ u: \Omega \rightarrow \mathbb{R}^3 $ where $ \Omega \subseteq \mathbb{R}^3 $.
The applied body forces are described by the mapping $ f: \Omega \rightarrow \mathbb{R}^3 $, called the density of the applied body force per unit volume. The tensor $ \sigma(u) $ is the stress tensor, which will be described later.

On a subset $ \Gamma_0 \subseteq \partial\Omega $ the body is fixed and we get the homogenous Dirichlet boundary condition

\[
      u = 0 \quad \text{on } \Gamma_0.
\]

On the subset $ \Gamma_1 \subseteq \partial\Omega $ we have applied surface forces, which are given by a mapping $ g: \Gamma_1 \rightarrow \mathbb{R}^3 $, called the density of the applied surface force per unit area. From that we get the boundary condition

\[ 
      \sigma(u) n = g \quad \text{on } \Gamma_1,  
\]

where $ n $ is the unit outer normal vector along $ \Gamma_1 $.

The linear stress tensor is given by (also known as Hooke's Law)

\[
      \sigma(u) = \lambda\; {\rm trace} (\varepsilon(u)) + 2\mu \varepsilon(u).
\]

with the linear strain tensor

\[
      \varepsilon_{ij}(u) = \frac{1}{2} (\partial_j u_i + \partial_i u_j ).
\]

Reduction to 2-dimensional elasticity

For the reduction to a 2-dimensional problem there are two kinds of reduction techniques: the plane stress state and the plane strain state. The plane stress state is a good approximation for very thin bodies. In this case one can assume that all components of $ \sigma(u) $ connected to the $ x_3 $-direction vanish, i.e.

\[
      \sigma_{i3}(u) = \sigma_{3i}(u) = 0 \quad i=1,2,3.
\]

By throwing away the component of $ u $ in the $ x_3 $-direction and expressing $ \varepsilon_{33}(u) $ by $ \varepsilon_{11}(u) $ and $ \varepsilon_{22}(u) $, we can write the material law from the first section in the form

\[
      \sigma^{\rm 2D} (u) = 2\mu \frac{\lambda}{\lambda+2\mu}\; {\rm trace}\left(\varepsilon^{\rm 2D}(u)\right) + 2\mu \varepsilon^{\rm 2D}(u).
\]

For the plane strain state one can assume that all components of $ \varepsilon(u) $ connected to the $ x_3 $-direction vanish. Again by throwing away the component of $ u $ in the $ x_3 $-direction one can then write the material law in the form

\[
       \sigma^{\rm 2D} (u) = \lambda\; {\rm trace}\left(\varepsilon^{\rm 2D}(u)\right) + 2\mu \varepsilon^{\rm 2D}(u).
\]

Here we will use the plane stress state.

Variational Formulation

We choose

\[
      V := \{ v \in H^1(\Omega)^2\,|\,\text{with } v = 0 \text{ on } \Gamma_0 \}
\]

where now $ \Omega \subseteq \mathbb{R}^2 $. Then we have the bilinearform

\[
      B(u,v) = \int_\Omega \sigma^{\rm 2D}(u):\varepsilon^{\rm 2D}(v) \;{\rm d}x
\]

with $ u,v \in V $. Here we use for matrices $ A:B := \sum_{i,j=1}^n a_{ij}b_{ij} = {\rm trace}(A^T B) $.

Finally, the variational formulation is: Find $ u \in V $ such that

\[
      2\mu \frac{\lambda}{\lambda+2\mu} \int_\Omega {\rm div}(u){\rm div}(v) \;{\rm d}x + 2\mu \int_\Omega \varepsilon^{\rm 2D}(u):  \varepsilon^{\rm 2D}(v) \;{\rm d}x = \int_\Omega f \cdot v \;{\rm d}x + \int_{\Gamma_1} g \cdot v \;{\rm d}s \quad \forall v \in V
\]

in the case of the plane stress state and

\[
      \lambda \int_\Omega {\rm div}(u){\rm div}(v) \;{\rm d}x + 2\mu \int_\Omega \varepsilon^{\rm 2D}(u):  \varepsilon^{\rm 2D}(v) \;{\rm d}x = \int_\Omega f \cdot v \;{\rm d}x + \int_{\Gamma_1} g \cdot v \;{\rm d}s \quad \forall v \in V 
\]

in the case of the plane strain state. We will use this splitting of the bilinear form for computing the system matrix later.

Commented Program

First, systemfiles

#include <iostream>

and conceptfiles

#include "basics.hh"
#include "formula.hh"
#include "function.hh"
#include "geometry.hh"
#include "graphics.hh"
#include "hp2D.hh"
#include "hp1D.hh"
#include "operator.hh"
#include "space.hh"
#include "toolbox.hh"
#include "vectorial.hh"

are included. In this example we use a cig-file as input. This file is create with a Python script and contains geometry data, the densities of the applied forces, material constants and values for the h- and p-refinement. For using that cig-file as input, we write a method that first sets some default values and then reads the file.

void processInput(int argc, char** argv, concepts::InOutParameters& input)
{
input.addString("projname", "project");
input.addBool("plain_stress", 1);
input.addString("f1", "(0)");
input.addString("f2", "(0)");
input.addString("g1", "(1000000)");
input.addString("g2", "(0)");
input.addInt("dirichlet", 1);
input.addInt("neumann", 2);
input.addInt("delete", 11);
input.addInt("h", 3);
input.addInt("p", 2);
input.addInt("graphicpoints", 4);
void addInt(const char *name, const int value)
Adds an int to the hash of ints.
void addBool(const char *name, const int value)
Adds a bool to the hash of bools.
void addString(const char *name, const char *value)
Adds a string to the hash of strings.
concepts::ProcessParameter parameter(input);
if (!parameter.apply(argc, argv))
exit(1);
}

We first read the input data from the cig-file

int main(int argc, char *argv[]){
try {
processInput(argc, argv, input);

and save the material constants and the applied forces (each component is one function) for better access in some variables.

const concepts::Real lambda = input.getDouble("lambda");
const concepts::Real mu = input.getDouble("mu");
double getDouble(const char *name) const
Returns a double from the hash of doubles.
double Real
Definition typedefs.hh:39
std::string getString(const char *name, const char *value=0) const

Then the mesh is generated and we write the mesh to an eps-file.

std::unique_ptr<concepts::Mesh2>
std::cout << "Mesh: " << *msh << std::endl;
graphics::drawMeshEPS( *msh, input.getString("projname") + "_mesh.eps", 100, 1.0, 100);
Import2dMeshGeneral * import2dMeshGeneralFromInput(const InOutParameters input, bool verbose=false)
MeshEPS< Real > drawMeshEPS(concepts::Mesh &msh, std::string filename, const Real scale=100, const Real greyscale=1.0, const unsigned int nPoints=2)

We load the attribute of the edges with the Dirichlet boundary condition from the input file and set the boundary condition for all edges with this attribute.

bc.add(concepts::Attribute(input.getInt("dirichlet")), concepts::Boundary(concepts::Boundary::DIRICHLET));
void add(const Set< Attribute > &attrib, const Boundary &bcObject)
Adds boundary condition for a set of attributes.
int getInt(const char *name, const int value=INT_MAX) const

Now we first create a scalar space for the several components of the solution and save it again in an eps-file.

hp2D::hpAdaptiveSpaceH1 spc(*msh, input.getInt("h"), input.getInt("p"), &bc);
spc.rebuild();
std::cout << "spc = " << spc << std::endl;
graphics::drawMeshEPS(spc, input.getString("projname") + "_space.eps", 100, 1.0, 4);

We can then put this space into the components of the vectorial space.

const uint vdim = 2;
vspc.put(spc);
vspc.put(spc);
vspc.rebuild();

We create a trace space for the Neumann boundary condition.

hp2D::TraceSpace tspc(spc, concepts::makeSet<uint>({attrib_neumann}));
Set< F > makeSet(uint n, const F &first,...)
Definition set.hh:320

Now, we can compute the system matrix, where we use the splitted form of the bilinear form as stated in the variational formulation of the problem.

concepts::SparseMatrix<concepts::Real> S(vspc.dim(), vspc.dim());
hp2D::setupDivDiv(divdiv_bf);
A.addInto(S, 2*mu*lambda / (lambda + 2*mu));
hp2D::setupLinStrainStrain(strain_bf);
B.addInto(S,2*mu);
void setupDivDiv(vectorial::BilinearForm< F, typename concepts::Realtype< F >::type > &bf, const concepts::ElementFormulaContainer< F, typename concepts::Realtype< F >::type > frm=concepts::ElementFormulaContainer< F, typename concepts::Realtype< F >::type >())

The right hand side vectors are computed from the applied volume and surface forces. We compute these vectors for each component of the forces and concatenate them to one vector for the vectorial system.

rhs.add(rhs1);
rhs.add(rhs2,1.0,spc.dim());
hp1D::Riesz<concepts::Real> traceLForm1(g1);
hp1D::Riesz<concepts::Real> traceLForm2(g2);
concepts::Vector<concepts::Real> rhsNeumann1(tspc, traceLForm1);
concepts::Vector<concepts::Real> rhsNeumann2(tspc, traceLForm2);
rhs.add(rhsNeumann1);
rhs.add(rhsNeumann2,1.0,tspc.dim());

We solve the system with a direct method provided by SuperLU.

For better access and post-processing we split the solution into the two parts $ u_1 $ and $ u_2 $.

Now we write the solution (the displacement) and the resulting strains and stresses to a matlab binary file. Before doing that we recompute the shape functions on equidistant points.

hp2D::IntegrableQuad::factory().get()->setTensor(concepts::TRAPEZE, true, input.getInt("graphicpoints"));
spc.recomputeShapefunctions();
graphics::MatlabElasticityGraphics(spc,input.getString("projname")+"_stress",
sol_u1, sol_u2, lambda, mu, true);
static std::unique_ptr< concepts::QuadRuleFactoryTensor2d > & factory()
Definition quad.hh:144

We compute the energy norm of the solution.

concepts::Real energy_norm = sol*rhs;
std::cout << "Energy norm: " << std::setprecision(16) << energy_norm << std::endl;

Finally, exceptions are caught and a return value is given back.

} catch (concepts::ExceptionBase& e) {
std::cout << e << std::endl;
return 1;
}

Results

For running this project we first have to create the cig file with the python script

python elasticity2D.py

With a symbolic link named bin to the directory containing the binaries, we can start the program with

bin/elasticity2D -f elasticity2D/elasticity2D.cig

The output of the program on the screen is then

Mesh: Import2dMeshGeneral(ncell = 4)
spc = hpAdaptiveSpaceH1(dim = 9359 (V:1071, E:4192, I:4096), nelm = 1024)
Assembling system matrix ...
done.
Systemmatrix:
SparseMatrix(18718x18718, HashedSparseMatrix: 918392 (0.262125%) entries bound.)
Assembling RHS ...
done.
solve system ...
done.
Energy norm: 7.480971032756906

The output of the solutions of the program is saved in a file called elasticity2D_stress.mat. For regarding the output (for example the stress $ \sigma_{11} $) in MATLAB we can type the following in the MATLAB command window

load elasticity2D_stress
patch(x(msh),y(msh),stress11(msh),'edgecolor','none')
axis image
colorbar

This creates the following image. Matlab output

Complete Source Code

Author
Philipp Kliewe, 2013
#include <iostream>
#include "basics.hh"
#include "formula.hh"
#include "function.hh"
#include "geometry.hh"
#include "graphics.hh"
#include "hp2D.hh"
#include "hp1D.hh"
#include "operator.hh"
#include "space.hh"
#include "toolbox.hh"
#include "vectorial.hh"
void processInput(int argc, char** argv, concepts::InOutParameters& input)
{
//set default values
input.addString("projname", "project");
input.addBool("plain_stress", 1);
input.addString("f1", "(0)");
input.addString("f2", "(0)");
input.addString("g1", "(1000000)");
input.addString("g2", "(0)");
input.addInt("dirichlet", 1);
input.addInt("neumann", 2);
input.addInt("delete", 11);
input.addInt("h", 3);
input.addInt("p", 2);
input.addInt("graphicpoints", 4);
// read arguments and Inputfiles
concepts::ProcessParameter parameter(input);
if (!parameter.apply(argc, argv))
exit(1);
}
int main(int argc, char *argv[]){
try {
#ifdef HAS_MPI
// Initialisation is necessary if parallel version of Mumps is compiled
MPI_Init(&argc, &argv);
#endif
// read CIG-file
processInput(argc, argv, input);
// read Lame-constants
const concepts::Real lambda = input.getDouble("lambda");
const concepts::Real mu = input.getDouble("mu");
// read formulas for the applied forces
// read and generate the mesh
std::unique_ptr<concepts::Mesh2>
std::cout << "Mesh: " << *msh << std::endl;
graphics::drawMeshEPS( *msh, input.getString("projname") + "_mesh.eps", 100, 1.0, 100);
// set Dirchlet boundary conditions
bc.add(concepts::Attribute(input.getInt("dirichlet")), concepts::Boundary(concepts::Boundary::DIRICHLET));
// Space
hp2D::hpAdaptiveSpaceH1 spc(*msh, input.getInt("h"), input.getInt("p"), &bc);
spc.rebuild();
std::cout << "spc = " << spc << std::endl;
graphics::drawMeshEPS(spc, input.getString("projname") + "_space.eps", 100, 1.0, 4);
// Vectorial Space
const uint vdim = 2;
vspc.put(spc);
vspc.put(spc);
vspc.rebuild();
// Trace Space for Neumann boundary condition
uint attrib_neumann = input.getInt("neumann");
hp2D::TraceSpace tspc(spc, concepts::makeSet<uint>({attrib_neumann}));
// Build system matrix
std::cout << "Assembling system matrix ..." << std::endl;
concepts::SparseMatrix<concepts::Real> S(vspc.dim(), vspc.dim());
hp2D::setupDivDiv(divdiv_bf);
A.addInto(S, 2*mu*lambda / (lambda + 2*mu));
hp2D::setupLinStrainStrain(strain_bf);
B.addInto(S,2*mu);
std::cout << "done." << std::endl;
std::cout << "Systemmatrix:" << std::endl << S << std::endl;
// Build RHS vectors
std::cout << "Assembling RHS ..." << std::endl;
rhs.add(rhs1);
rhs.add(rhs2,1.0,spc.dim());
hp1D::Riesz<concepts::Real> traceLForm1(g1);
hp1D::Riesz<concepts::Real> traceLForm2(g2);
concepts::Vector<concepts::Real> rhsNeumann1(tspc, traceLForm1);
concepts::Vector<concepts::Real> rhsNeumann2(tspc, traceLForm2);
rhs.add(rhsNeumann1);
rhs.add(rhsNeumann2,1.0,tspc.dim());
std::cout << "done." << std::endl;
// Solve system
std::cout << "solve system ..." << std::endl;
#ifdef HAS_MUMPS
#else
#endif
solver(rhs, sol);
// split the solution into the components u1 and u2
concepts::Vector<concepts::Real> sol_u2(spc, (concepts::Real*)sol + spc.dim());
// plot the solution
hp2D::IntegrableQuad::factory().get()->setTensor(concepts::TRAPEZE, true, input.getInt("graphicpoints"));
spc.recomputeShapefunctions();
graphics::MatlabElasticityGraphics(spc,input.getString("projname")+"_stress",
sol_u1, sol_u2, lambda, mu, true);
std::cout << "done." << std::endl;
// compute energy norm
concepts::Real energy_norm = sol*rhs;
std::cout << "Energy norm: " << std::setprecision(16) << energy_norm << std::endl;
} catch (concepts::ExceptionBase& e) {
std::cout << e << std::endl;
return 1;
}
#ifdef HAS_MPI
MPI_Finalize();
#endif
return 0;
}
bool apply(int argc, char **argv)