Modal analysis of a solid cantilever beam¶
This example computes the natural frequencies and mode shapes of a 3-D cantilever beam
using Solid.solve_modes().
import torch
from torchfem import Solid
from torchfem.materials import IsotropicElasticity3D
from torchfem.mesh import cube_hexa
torch.set_default_dtype(torch.float64)
Model setup¶
We build a cantilever beam with length $L = 10$ and square cross-section $H = W = 0.5$, clamped at one end.
# Geometric parameters
L = 10.0
H = 0.5
W = 0.5
# Material parameters
E = 1000.0
nu = 0.3
rho = 1.0
# Build model
material = IsotropicElasticity3D(E=E, nu=nu, rho=rho)
nodes, elements = cube_hexa(81, 5, 5, Lx=L, Ly=H, Lz=W)
model = Solid(nodes, elements, material)
left = nodes[:, 0] == 0.0
model.constraints[left, :] = True
model.plot()
Modal analysis¶
Solve for the first four natural frequencies and mode shapes. The ABAQUS reference uses C3D8 elements.
Note on the expected discrepancy. Abaqus C3D8 unconditionally applies selective reduced integration with $\bar{B}$: the volumetric part of $\mathbf{K}$ is computed with a single centroid Gauss point, while the deviatoric part uses the full 2×2×2 rule. torch-fem's
Hexa1uses full 2×2×2 Gauss for both parts, producing a slightly stiffer volumetric response and therefore higher frequencies on coarse meshes (~12% in $\omega^2$ for this 30×2×2 mesh). The two formulations converge to the same analytical answer under mesh refinement.
N = 4
omega_sq, modes = model.solve_modes(n_modes=N)
# ABAQUS reference values for the first 4 modes
omega_sq_ref = [0.02595, 0.02595, 0.99589, 0.99589]
header = f"{'Mode':>4} {'ω² (torch-fem)':>14} {'ω² (ABAQUS)':>12}"
print(header)
print("-" * len(header))
for k in range(N):
print(f"{k + 1:>4} {omega_sq[k]:>14.6f} {omega_sq_ref[k]:>12.6f}")
Mode ω² (torch-fem) ω² (ABAQUS) ---------------------------------- 1 0.026699 0.025950 2 0.026699 0.025950 3 1.025721 0.995890 4 1.025721 0.995890
Visualise mode shapes¶
The plotting call opens an interactive 3-D viewer for each mode.
for k in range(N):
mode_k = modes[k]
max_mag = torch.norm(mode_k, dim=1).max()
u_plot = mode_k / max_mag
model.plot(
u=u_plot,
node_property={"|u|": torch.norm(u_plot, dim=1)},
show_undeformed=True,
cmap="viridis",
)