The Stiffness Matrix from First Principles
Where does the element stiffness matrix actually come from? A full derivation from virtual work, through shape functions, to the 12×12 frame element matrix you use every day.
Every finite element analysis software — ETABS, SAP2000, ANSYS, your own Python solver — is built on the same foundation: the element stiffness matrix.
You’ve probably used it. But where does it actually come from?
This post derives it from scratch — starting from virtual work, moving through shape functions, ending at the 12×12 matrix for a 3D frame element. No black boxes.
The core idea
A structural element resists deformation. The stiffness matrix captures how much — it maps nodal displacements to nodal forces :
For a single element with degrees of freedom, is an matrix. For a full structure, we assemble all element matrices into a global .
Virtual work principle
The derivation starts with the principle of virtual work:
For a body in equilibrium, the virtual work done by external forces through any virtual displacement equals the virtual strain energy stored internally.
For a bar element under virtual nodal displacement :
This is the foundation. Now we need to connect strains to displacements.
Shape functions
A 2-node bar element has 2 DOF: and (axial displacements). We assume the displacement varies linearly along the element:
and are the shape functions — they define how the interior displacement field is interpolated from the nodal values.
In matrix form:
Strain-displacement relationship
For a bar in axial loading, strain is:
where is the strain-displacement matrix, found by differentiating :
Assembling the stiffness matrix
With linear elastic material, .
Substituting into the virtual work equation:
Since is arbitrary, we can factor it out:
For a bar with constant , :
That’s the 2×2 stiffness matrix for an axial bar element — derived entirely from first principles.
Extending to a 2D beam element
A 2D Euler–Bernoulli beam element has 4 DOF: two transverse displacements , and two rotations , .
The shape functions are cubic (Hermite polynomials):
where:
The curvature (analogous to strain for bending) is . The stiffness matrix follows the same integral:
This is the Euler–Bernoulli beam stiffness matrix — you’ll find this exact matrix in every structural analysis textbook.
The 12×12 3D frame element
Combining axial, bending (two planes), torsion, and shear DOF gives the full 12×12 matrix used in ETABS, SAP2000, and every general-purpose FEM solver.
The assembly process is always the same:
- Define shape functions for each DOF
- Compute by differentiation
- Integrate
- Transform from local to global coordinates via
Implementing it in Python
import numpy as np
def bar_stiffness(E: float, A: float, L: float) -> np.ndarray:
"""2x2 stiffness matrix for an axial bar element."""
k = (E * A / L)
return k * np.array([[1, -1], [-1, 1]])
def beam_stiffness(E: float, I: float, L: float) -> np.ndarray:
"""4x4 Euler-Bernoulli beam element stiffness matrix.
DOF order: [v1, θ1, v2, θ2]
"""
k = E * I / L**3
return k * np.array([
[ 12, 6*L, -12, 6*L],
[6*L, 4*L**2, -6*L, 2*L**2],
[-12, -6*L, 12, -6*L],
[6*L, 2*L**2, -6*L, 4*L**2],
])
In the next post in this series, we’ll assemble these element matrices into a global stiffness matrix, apply boundary conditions, and solve for displacements — building a complete 2D frame solver from scratch.
What this tells you
Every result ETABS gives you flows from this integral. The “stiffness matrix” isn’t a magic number lookup — it’s a compact representation of how a physical element resists deformation, derived from strain energy and virtual work.
Understanding the derivation doesn’t change your day-to-day workflow. But when a model gives you a surprising result, it tells you where to look.
Discover more
Talking to ETABS in Natural Language: ETABSSharp + MCP
I built an MCP server on top of ETABSSharp that lets AI assistants query, modify, and run analysis on ETABS models through plain English. Here's how it works.
Building ETABSSharp: A C# Wrapper for the ETABS API
The raw ETABS API is verbose and weakly typed. Here's how I designed a fluent C# wrapper that makes structural automation actually enjoyable to write.