This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License
The corresponding files can be obtained from:
- Jupyter Notebook:
buckling_3d_solid.ipynb
- Python script:
buckling_3d_solid.py
Linear Buckling Analysis of a 3D solid¶
This demo has been written in collaboration with Eder Medina (Harvard University).
In this numerical tour, we will demonstrate how to compute the buckling modes of a three-dimensional elastic solid under a conservative loading. The critical buckling loads and buckling modes are computed by solving a generalized non-hermitian eigenvalue problem using the SLEPcEigensolver
. This example is closely related to the Modal analysis of an elastic structure and the Eulerian
buckling of a beam demos. For more details on the theoretical formulation, the reader can refer to [NGU00].
Geometrically Nonlinear Elasticity¶
To formulate the buckling problem, we must consider a general geometrically nonlinear framework. More precisely, we will consider the case of small-strain/large displacements that are expected in typical buckling analysis.
We consider the Green-Lagrange strain tensor \(\be\) defined as:
Note that this can be rewritten in tensor format as
where we isolate the linear \(\beps(\bu)\) and nonlinear quadratic part \(\bq(\bu, \bu)=\nabla\bu\T\nabla\bu\).
Since we restrict to a small strain assumption, we adopt a standard linear Hookean behaviour for the material material model which can be described by the following quadratic elastic energy density:
where \(\mathbb{C}\) is the elastic moduli tensor.
We seek to find the underlying equilibrium displacement field \(\bu\) as a function of a scalar loading parameter \(\lambda\) under a set of prescribed external surface forces \(\bt(\lambda)\).
Equilibrium and stability conditions¶
Let us consider the total potential energy functional:
The equilibrium equations are obtained from the first variation which must vanish:
where \(\delta\bu\) is any kinematically admissible perturbation direction and where:
The stability conditions of an equilibrium are obtained from the Lejeune-Dirichlet theorem stating that the energy second variation must be positive. Here the second variation bilinear form is given by:
where:
Bifurcation point¶
A given equilibrium solution \(\lambda^c, \bu^c=\bu(\lambda^c)\) is a bifurcation point if the second variation vanishes for some \(\delta\bu\). \(\lambda^c\) defines the bifurcation load and \(\delta\bu\) the bifurcation mode. Hence, we have:
Linear buckling analysis¶
When performing a linear buckling analysis, we look for bifurcation points on the fundamental equilibrium branch obtained by a small displacement assumption. As a result, in the above bifurcation equation the quadratic contribution in the first term can be neglected and the Green-Lagrange strain \(\be(\bu^c)\) can be replaced by the linearized strain \(\beps(\bu^c)\). Besides, in the small-displacement assumption, if the loading depends linearly on the load parameter \(\lambda\), the small displacement solution also depends linearly on it. We therefore have \(\bu^c= \lambda^c\bu_0\) and the bifurcation condition becomes:
After, introducing the inital pre-stress \(\bsig_0 = \mathbb{C}:\beps(\bu_0)\), the last term can be rewritten as \(\bsig_0:\bq(\delta\bv,\delta\bu) = (\nabla \delta \bv) \bsig_0 (\nabla \delta\bu)\T\) so that :
We recognize here a linear eigenvalue problem where the first term corresponds to the classical linear elasticity bilinear form and the second term is an additional contribution depending on the pre-stressed state \(\bu_0\). Transforming this variational problem into matrix notations yields:
where \([\mathbf{K}]\) is the classical linear elastic stiffness matrix and \([\mathbf{K}_G(\bu_0)]\) the so-called geometrical stiffness matrix.
FEniCS implementation¶
Defining the Geometry¶
Here we will consider a fully three dimensional beam-like structure with length \(L = 1\) and of rectangular cross-section with height \(h = 0.01\), and width \(b = 0.03\).
[1]:
from dolfin import *
import matplotlib.pyplot as plt
import numpy as np
L, b, h = 1, 0.01, 0.03
Nx, Ny, Nz = 51, 5, 5
mesh = BoxMesh(Point(0, 0, 0), Point(L, b, h), Nx, Ny, Nz)
Solving for the pre-stressed state \(\bu_0\)¶
We first need to compute the pre-stressed linear state \(\bu_0\). It is obtained by solving a simple linear elasticity problem. The material is assumed here isotropic and we consider a pre-stressed state obtained from the application of a unit compression applied at the beam right end in the \(X\) direction while the \(Y\) and \(Z\) displacement are fixed, mimicking a simple support condition. The beam is fully clamped on its left end.
[2]:
E , nu = 1e3, 0.
mu = Constant(E/2/(1+nu))
lmbda = Constant(E*nu/(1+nu)/(1-2*nu))
def eps(v):
return sym(grad(v))
def sigma(v):
return lmbda*tr(eps(v))*Identity(v.geometric_dimension())+2*mu*eps(v)
# Compute the linearized unit preload
N0 = 1
T = Constant((-N0, 0, 0))
V = VectorFunctionSpace(mesh, 'Lagrange', degree = 2)
v = TestFunction(V)
du = TrialFunction(V)
# Two different ways to define how to impose boundary conditions
def left(x,on_boundary):
return near(x[0], 0.)
def right(x, on_boundary):
return near(x[0], L)
boundary_markers = MeshFunction('size_t', mesh, mesh.topology().dim()-1)
ds = Measure('ds', domain=mesh, subdomain_data=boundary_markers)
AutoSubDomain(right).mark(boundary_markers, 1)
# Clamped Boundary and Simply Supported Boundary Conditions
bcs = [DirichletBC(V, Constant((0, 0, 0)), left),
DirichletBC(V.sub(1), Constant(0), right),
DirichletBC(V.sub(2), Constant(0), right)]
# Linear Elasticity Bilinear Form
a = inner(sigma(du), eps(v))*dx
# External loading on the right end
l = dot(T, v)*ds(1)
We will reuse our stiffness matrix in the linear buckling computation and hence we supply a PETScMatrix
and PETScVector
to the system assembler. We then use the solve
function on these objects and specify the linear solver method.
[3]:
K = PETScMatrix()
f = PETScVector()
assemble_system(a, l, bcs, A_tensor=K, b_tensor=f)
u = Function(V,name = "Displacement")
solve(K, u.vector(), f, "mumps")
# Output the trivial solution
ffile = XDMFFile("output/solution.xdmf")
ffile.parameters["functions_share_mesh"] = True
ffile.parameters["flush_output"] = True
ffile.write(u, 0)
Forming the geometric stiffness matrix and the buckling eigenvalue problem¶
From the previous solution, the prestressed state \(\bsig_0\) entering the geometric stiffness matrix expression is simply given by sigma(u)
. After having formed the geometric stiffness matrix, we will call the SLEPcEigenSolver
for solving a generalized eigenvalue problem \(\mathbf{Ax}=\lambda \mathbf{Bx}\) with here \(\mathbf{A}=\mathbf{K}\) and \(\mathbf{B}=-\mathbf{K}_G\). We therefore include directly the negative sign in the definition of the geometric stiffness
form:
[4]:
kgform = -inner(sigma(u), grad(du).T*grad(v))*dx
KG = PETScMatrix()
assemble(kgform, KG)
# Zero out the rows to enforce boundary condition
for bc in bcs:
bc.zero(KG)
The buckling points of interest are the smallest critical loads, i.e. the points corresponding to the smallest \(\lambda^c\).
We now pass some parameters to the SLEPc solver. In particular, we peform a shift-invert transform as discussed in the Eulerian buckling of an elastic beam tour.
[5]:
# What Solver Configurations exist?
eigensolver = SLEPcEigenSolver(K, KG)
print(eigensolver.parameters.str(True))
<Parameter set "slepc_eigenvalue_solver" containing 8 parameter(s) and parameter set(s)>
slepc_eigenvalue_solver | type value range access change
--------------------------------------------------------------------
maximum_iterations | int <unset> Not set 0 0
problem_type | string <unset> Not set 0 0
solver | string <unset> Not set 0 0
spectral_shift | double <unset> Not set 0 0
spectral_transform | string <unset> Not set 0 0
spectrum | string <unset> Not set 0 0
tolerance | double <unset> Not set 0 0
verbose | bool <unset> Not set 0 0
[6]:
eigensolver.parameters['problem_type'] = 'gen_hermitian'
eigensolver.parameters['solver'] = "krylov-schur"
eigensolver.parameters['tolerance'] = 1.e-12
eigensolver.parameters['spectral_transform'] = 'shift-and-invert'
eigensolver.parameters['spectral_shift'] = 1e-2
# Request the smallest 3 eigenvalues
N_eig = 3
print("Computing {} first eigenvalues...".format(N_eig))
eigensolver.solve(N_eig)
print("Number of converged eigenvalues:", eigensolver.get_number_converged())
eigenmode = Function(V)
# Analytical beam theory solution
# F_cr,n = alpha_n^2*EI/L^2
# where alpha_n are solutions to tan(alpha) = alpha
alpha = np.array([1.4303, 2.4509, 3.4709] + [(n+1/2) for n in range(4, 10)])*pi
I = b**3*h/12
S = b*h
for i in range(eigensolver.get_number_converged()):
# Extract eigenpair
r, c, rx, cx = eigensolver.get_eigenpair(i)
critical_load_an = alpha[i]**2*float(E*I/N0/S)/L**2
print("Critical load factor {}: {:.5f} FE | {:.5f} Beam".format(i+1, r, critical_load_an))
# Initialize function and assign eigenvector
eigenmode.vector()[:] = rx
eigenmode.rename("Eigenmode {}".format(i+1), "")
ffile.write(eigenmode, 0)
Computing 3 first eigenvalues...
Number of converged eigenvalues: 3
Critical load factor 1: 0.16821 FE | 0.16826 Beam
Critical load factor 2: 0.49691 FE | 0.49405 Beam
Critical load factor 3: 0.98918 FE | 0.99084 Beam
Note that the analysis above is not limited to problems subject to Neumann boundary conditions. One can equivalenty compute the prestress resulting from a displacement control simulation and the critical buckling load will correspond to the critical buckling displacement of the structure.
References¶
[NGU00]: Nguyen, Q. S. (2000). Stability and nonlinear solid mechanics. Wiley.