A C++ implementation of CALFEM, the finite element toolbox from the Division of Structural Mechanics, Lund University. The library is built on Eigen and is used as the solver core of ObjectiveFrame.
Currently available:
-
Springs:
spring1e/s -
Bars:
bar1e/s,bar1we/ws(elastic foundation),bar2e/s,bar2ge/gs(second order),bar3e/s -
Beams:
beam1e/s,beam1we/ws,beam2e/s,beam2we/ws,beam2te/ts(Timoshenko),beam2ge/gsandbeam2gxe/gxs(second order),beam2de/ds(dynamics),beam3e/s -
Field problems (heat flow etc.):
flw2te/ts,flw2qe/qs,flw2i4e/s,flw2i8e/s,flw3i8e/s -
Plane stress/strain:
plante/s/f,planqe/s,plani4e/s; solids:soli8e/s; plates:platre -
Materials:
hooke,effmises,stress2nodal -
Solvers:
solveq,spsolveq,SparseSolver,red,statcon,eigen -
Time integration:
step1(first order systems),step2(Newmark),gfunc(time functions) -
Boundary conditions and loads:
apply_bc,apply_bc_3d,apply_bc_node,apply_force(_3d,_node,_total,_total_3d) -
DOF utilities:
create_dofs,coord_extract,extract_edof,extract_eldisp,convert_to_node_topo,beam2crd,scalfact2 -
Dense and sparse assembly
-
Geometry and mesh generation:
Geometry(points, splines, B-splines, circle and ellipse arcs, surfaces with holes, structured surfaces and volumes, markers),create_mesh/MeshGeneratorwith a built-in 2D mesher and an optional Gmsh backend
All element and solver routines of calfem-python's core.py are available.
- CMake 3.24 or later
- A C++17 compiler (MSVC 2022+, GCC 9+, Clang 10+)
- vcpkg (dependencies: Eigen 3.4, Catch2 for tests)
The presets use Ninja. vcpkg is located automatically (cmake/vcpkg_detect.cmake): from the VCPKG_ROOT
environment variable, the vcpkg executable on PATH, VCPKG_INSTALLATION_ROOT, or a common location such as
~/vcpkg or C:/vcpkg. An explicit -DCMAKE_TOOLCHAIN_FILE=... takes precedence, and
-DCALFEM_VCPKG_DETECT=OFF disables the search. All presets share a
single vcpkg tree, vcpkg_installed/, so the dependencies are installed once for all build trees. Since vcpkg
removes packages a configuration does not request, every preset requests the same manifest features (including
qt); the Qt presets differ only in enabling CALFEM_WITH_QT.
cmake --preset release
cmake --build --preset release
ctest --preset releaseOn Windows, run these from a Developer Command Prompt/PowerShell (or let VS Code's CMake Tools select the MSVC kit) so that Ninja finds the compiler.
To get a Visual Studio solution instead, use the vs preset (Windows only, Visual Studio 2026). It generates a
multi-config solution in build-vs/calfem.slnx that does not need a developer environment:
cmake --preset vs
cmake --build --preset vs-release # or vs-debug
ctest --preset vs-release # or vs-debugOpen build-vs/calfem.slnx in Visual Studio to build and debug from the IDE. To use another Visual Studio
version, override the generator in a CMakeUserPresets.json, e.g. "generator": "Visual Studio 17 2022".
To use a different vcpkg installation than the one found automatically, create a CMakeUserPresets.json (ignored by
git):
{
"version": 6,
"configurePresets": [
{ "name": "my-debug", "inherits": "debug", "environment": { "VCPKG_ROOT": "E:/vcpkg" } }
],
"buildPresets": [ { "name": "my-debug", "configurePreset": "my-debug" } ],
"testPresets": [ { "name": "my-debug", "configurePreset": "my-debug" } ]
}| Option | Default (top-level / subproject) | Description |
|---|---|---|
CALFEM_BUILD_TESTS |
ON / OFF |
Build the Catch2 unit tests |
CALFEM_BUILD_EXAMPLES |
ON / OFF |
Build the examples in src/examples |
CALFEM_INSTALL |
ON / OFF |
Generate install/export rules |
CALFEM_WITH_MESH |
ON / OFF |
Geometry and mesh generation (CDT) |
CALFEM_WITH_GMSH |
OFF |
Gmsh mesh backend (GPL, see below) |
CALFEM_WITH_QT |
OFF |
Qt windows for calfem::vis (LGPL) |
#include <calfem/calfem.h> includes the whole library. Individual modules can also be included on their own:
| Header | Contents |
|---|---|
calfem/types.h |
Matrix, RowVec, ColVec, SpMatrix, IntMatrix, ... |
calfem/elements.h |
all elements (elements/spring.h, bar.h, beam.h, flow.h, plane.h, solid.h, plate.h) |
calfem/material.h |
hooke, effmises |
calfem/dofs.h |
create_dofs, coord_extract, extract_edof, extract_eldisp, stress2nodal, ... |
calfem/assembly.h |
assem, spassem |
calfem/boundary.h |
BoundaryDofs, apply_bc, apply_force, ... |
calfem/solvers.h |
solveq, spsolveq, SparseSolver, red, statcon, eigen, SolverError |
calfem/dynamics.h |
step1, step2, gfunc, TimeHistory |
calfem/geometry.h |
Geometry (with CALFEM_WITH_MESH) |
calfem/mesh.h |
create_mesh, MeshGenerator, Mesh, geo_string, apply_traction_linear_element |
calfem/log.h |
setLogFunction |
calfem/utils.h |
print, spy, scalfact2 |
calfem/vis.h |
Figure, figure, eldraw2, eldisp2, dispbeam2, secforce2, scalgraph2, save_figure, show |
calfem/vis_qt.h |
Qt backend: figure_widget (target calfem::vis_qt, with CALFEM_WITH_QT) |
Examples live in src/examples and are built to bin/debug or bin/release:
truss3d- three-bar 3D truss: topology, assembly, sparse solve and normal forces.frame2d- 2D portal frame with beam elements, distributed load, dense solve and section forces.plane2d- plane stress cantilever with 4-node elements: tip deflection versus beam theory, von Mises stress.plate2d- simply supported plate (platre,apply_bc_3d) compared with Navier's solution.heat2d- steady heat conduction in a plate with 4-node isoparametric elements, compared with the exact solution.mesh2d- plate with a hole: geometry, mesh generation, plane stress analysis and the stress concentration compared with Kirsch/Heywood.mesh2d mesh.svg 2writes the mesh as SVG and uses triangles.vibration2d- natural frequencies of a cantilever (beam2de,eigen) and its response to a load pulse (gfunc,step2).
Each example has a visualisation version <example>_vis that solves the same problem and plots the results
with calfem::vis. The figures are shown in Qt windows when built with CALFEM_WITH_QT, otherwise (or with
--svg) written as <example>_<figure>.svg:
truss3d_vis- undeformed and deformed truss in a 3D projection, supports, load and normal forces.frame2d_vis- deformed frame and normal force, shear force and moment diagrams.plane2d_vis- deformed mesh, the centre line deflection versus beam theory, and the effective stress (draw_element_values).plate2d_vis- deflected plate as a 3D wireframe, the centre line deflection versus Navier's solution, and deflection contours (draw_nodal_values_contourf/_contour).heat2d_vis- temperature contours with heat flux arrows (draw_element_flux), and the temperature versus the exact solution.mesh2d_vis- mesh, deformed mesh,sigma_xxabove the hole versus Kirsch's solution, andsigma_xxcontours (mesh2d_vis 2uses triangles).vibration2d_vis- the first three bending modes, and the tip displacement history.
calfem/vis.h ports calfem-python's drawing functions (calfem.vis_mpl). They draw into figures, which are
written to SVG without further dependencies, or shown in interactive windows with the Qt backend:
#include <calfem/calfem.h>
#include <calfem/vis_qt.h> // optional: Qt windows (link calfem::vis_qt)
vis::figure(1);
vis::eldraw2(ex, ey, {1, 2, 1}); // undeformed mesh
double sfac = vis::eldisp2(ex, ey, ed); // deformed mesh, returns the scale factor
vis::scalgraph2(sfac, {1e-3, 0.5, 0.0});
vis::title("Displacements");
vis::figure(2);
for (Eigen::Index i = 0; i < ex.rows(); i++)
vis::secforce2(ex.row(i), ey.row(i), es[i].col(2), {2, 1}, scale);
vis::save_figure("moment.svg"); // .png/.jpg/.pdf need the Qt backend
vis::show(); // Qt windows; without Qt: SVG opened in the default viewerMesh and field drawings follow calfem-python's draw_* functions and use matplotlib's colour maps (viridis by
default, vis::colormap() selects jet or gray) and contour levels:
vis::figure(3);
vis::draw_nodal_values_contourf(a, coords, edof, 12, "Temperature", 1, 3, true); // filled contours + mesh
vis::colorbar();
vis::draw_element_flux(q, coords, edof, 1, 3); // flux arrows
vis::figure(4);
vis::draw_element_values(eseff, coords, edof, 2, 3, a, false, true, 10.0); // on the displaced mesh
vis::colorbar();Also available: draw_mesh, draw_elements, draw_node_circles, draw_displacements,
draw_nodal_values_contour, draw_nodal_values_shaded, draw_nodal_values and the element-coordinate forms
eliso2, elval2 and elflux2. Deviations from calfem-python (e.g. Gouraud shading approximated by 64
contour intervals) are listed in vis.h.
The Qt windows pan with the mouse, zoom with the wheel, fit with Home or a double-click, toggle the grid with G
and save the current view as SVG, PNG or PDF. show() blocks until the windows are closed (set
CALFEM_NO_BLOCK to return immediately); figure_widget(fig, parent) embeds a figure in a Qt application.
The Qt backend is built with -DCALFEM_WITH_QT=ON and the vcpkg feature qt (presets vs-qt, debug-qt,
release-qt). vcpkg builds Qt from source the first time, which takes a while; the binary cache makes later
configures fast. Qt is LGPL licensed and linked dynamically.
If your user profile path contains a space (e.g. C:\Users\First Last), vcpkg's Qt build fails with
Feature "pkg_config": Forcing to "ON" breaks its condition. Point PKG_CONFIG to vcpkg's pkg-config.exe
through a path without spaces (e.g. its 8.3 short name) for the first configure:
$env:PKG_CONFIG = "C:/Users/FIRSTL~1/AppData/Local/vcpkg/downloads/tools/msys2/<hash>/mingw64/bin/pkg-config.exe"
$env:VCPKG_KEEP_ENV_VARS = "PKG_CONFIG"
cmake --preset vs-qtGeometries are described as in calfem-python and meshed with create_mesh or MeshGenerator:
Geometry g;
g.point({0.0, 0.0}); // points 0..3
g.point({2.0, 0.0});
g.point({2.0, 1.0});
g.point({0.0, 1.0});
g.spline({0, 1}, -1, 10); // curve 0, marker 10
g.spline({1, 2}, -1, 20);
g.spline({2, 3}, -1, 30);
g.spline({3, 0}, -1, 40);
g.surface({0, 1, 2, 3});
auto [coords, edof, dofs, bdofs, element_markers] = create_mesh(g, 3, 0.1, 2); // quads, size, dofs/node
MeshGenerator mesher(g); // more options, boundary elements
mesher.el_type = 2;
mesher.el_size_factor = 0.05;
Mesh m = mesher.create();Two mesh generators are available (MeshGenerator::backend):
- Built-in (always available, MIT/MPL): 2D geometries with triangles (
el_type2) or quadrilaterals (el_type3). Curves are divided according toel_on_curve(with progression/bump distributions) or the point element sizes, structured surfaces get a mapped mesh, other surfaces a constrained Delaunay triangulation (CDT) with graded interior points and smoothing. Quadrilaterals on unstructured surfaces are made by splitting triangles (needs an even number of elements on the curves of such surfaces). - Gmsh (optional): 3D, higher order elements, Gmsh's algorithms and options (
min_size,max_size,meshing_algorithm,gmsh_options). The geometry is written as a.geodescription in the same way as calfem-python and meshed with the Gmsh library in-process (no executable), giving the same mesh as calfem-python.
MeshBackend::Auto (default) uses Gmsh when calfem is built with it, otherwise the built-in mesher.
License: Gmsh is licensed under the GPL (version 2 or later). calfem-cpp itself stays MIT, but programs
that link calfem built with CALFEM_WITH_GMSH must comply with the GPL when they are distributed. The option
is therefore off by default.
Gmsh support uses the Gmsh SDK (C API with the gmsh.h_cwrap C++ wrapper, which works with any compiler).
The vcpkg gmsh port is built without the mesh module and cannot be used. Download the SDK from
gmsh.info (or pip install gmsh, which contains the same files) and point GMSH_ROOT to it:
cmake --preset release -DCALFEM_WITH_GMSH=ON -DGMSH_ROOT=/path/to/gmsh-sdkThe Gmsh shared library is copied next to the test and example executables.
The element routines are checked against results computed with calfem-python.
tests/reference_data/generate_calfem_python.py calls the
Python functions with fixed inputs and stores inputs and results in calfem_python_reference.json, which
test_calfem_python.cpp compares with the C++ results. To regenerate it (requires numpy and scipy):
python tests/reference_data/generate_calfem_python.py <path to calfem-python>/src
python tests/reference_data/generate_calfem_python_mesh.py <path to calfem-python>/src # also needs gmshThe mesh reference stores calfem-python's .geo text (compared with geo_string in every build) and its Gmsh
mesh (compared with the Gmsh backend when available).
As an installed package:
find_package(calfem CONFIG REQUIRED)
target_link_libraries(myapp PRIVATE calfem::calfem)As a subproject (git submodule or FetchContent):
add_subdirectory(external/calfem-cpp)
target_link_libraries(myapp PRIVATE calfem::calfem)#include <calfem/calfem.h>
using namespace calfem;
RowVec ex(2), ey(2), ez(2), ep(2);
ex << 0.0, 2.0;
ey << 0.0, 0.0;
ez << 0.0, 0.0;
ep << 210e9, 0.01; // E, A
double eq = 1e3; // distributed axial load
Matrix Ke = bar3e(ex, ey, ez, ep); // Python: Ke = bar3e(ex, ey, ez, ep)
auto [Ke2, fe] = bar3e(ex, ey, ez, ep, eq); // Python: Ke, fe = bar3e(ex, ey, ez, ep, eq)The element routines follow calfem-python: results are returned, as a single value or as a std::tuple for
use with structured bindings. As in Python, the load vector is only returned when eq is given, and the *s
routines only return edi and eci when the number of evaluation points nep is given:
ColVec es = bar3s(ex, ey, ez, ep, ed); // es at the element ends
auto [es, edi, eci] = bar3s(ex, ey, ez, ep, ed, eq, nep); // es, edi, eci at nep pointsThe same style applies to the rest of the API:
auto [edof, ex, ey, ez] = extract_edof(dofs, coords, topo);
Matrix ed = extract_eldisp(edof, a); // Python: ed = extract_eldisp(edof, a)
assem(edof.row(i), K, Ke); // Python: assem(edof, K, Ke)
assem(edof.row(i), K, Ke, f, fe); // Python: assem(edof, K, Ke, f, fe)
auto [a, r] = solveq(K, f, bcDofs); // Python: a, r = solveq(K, f, bc)
auto [a, r] = spsolveq(K, f, bcDofs, bcVals); // Python: a, r = spsolveq(K, f, bc, bcVal)
SparseSolver solver;
solver.setup(K, bcDofs, bcVals);
auto [a, r] = solver.solve(f);extract_edof and extract_eldisp are aliases for extractEdof and extractElDisp, so either naming
style can be used. assem/spassem update K in place (copying the global matrix for every element would be far too slow).
The value-returning solvers throw calfem::SolverError (a std::runtime_error) on invalid input or a failed
factorization.
The original output-argument form, used by ObjectiveFrame, is still available for all of these. There the
solvers return false on failure instead of throwing:
Matrix Ke;
ColVec fe;
bar3e(ex, ey, ez, ep, eq, Ke, fe);
ColVec a, r;
bool ok = spsolveq(K, f, bcDofs, bcVals, a, r);Degrees of freedom, topology and boundary condition arrays use 1-based numbering, as in CALFEM for MATLAB and Python. Boundary condition dofs can be given in any order.
The solvers return false on invalid input or a failed factorization. Diagnostic messages (problem size,
the reason for a failure, a warning when K is nearly singular because of missing supports) are passed to an
optional log function; the library prints nothing by default:
calfem::setLogFunction([](const std::string &message) { std::cerr << message << "\n"; });MIT, see LICENSE.