Troubleshooting flowchart#
Decision tree for the most-frequently-encountered failure modes. Walk it top-to-bottom; the first match usually isolates the root cause.
Symptom: solver fails with “matrix is singular” / “factorization failed”#
Did you fix at least three translations + three rotations’ worth of DOFs?
No — the model has surviving rigid-body modes. Run
m.solve_modal(n_modes=6): any frequency near zero is an unconstrained DOF. Add the missingModel.fix()calls. See Boundary-condition pitfalls.Yes — continue.
Are any element Jacobians near zero?
Visualise
det(J)per element (most CAD meshers export this directly; pyvista’scell_quality()filter computes it). Anything below 0.1 of the mesh maximum is a candidate for refinement / remesh.Refine or remesh the offending region. See Mesh quality.
Are there massive material-property ratios in the model?
A steel insert in a rubber matrix with E ratio 1e4 pushes condition number above 1e10 routinely. Solve the parts separately if possible, or add a rigidity scaling factor on the soft material that matches the stiff one within an order of magnitude.
Symptom: solve succeeds but displacements are absurdly large#
This is a soft form of the singular-matrix case — the matrix isn’t quite singular but is close enough that the solver returns a near-null-space vector. The diagnostics:
Run the rigid-body-mode check.
m.solve_modal(n_modes=6)on the un-loaded model. Any near-zero frequency is the unconstrained DOF.Check the support-reaction sum. After
m.solve_static(), sumStaticResult.reactionalong each global axis. The sum must equal-ΣF_applied(Newton’s third law). See Reaction forces — global equilibrium and root moment from a tip load. If it doesn’t, you have a load-application bug — usually a missingaccumulate=TrueonModel.apply_force()overwriting a previous load on the same node.
Symptom: stress field has spurious peaks at the clamp#
This is Saint-Venant edge effects, not a bug. The recovered nodal stress at a clamped boundary is artificially elevated because the clamp imposes a non-physical perfect constraint that the structure resists with concentrated local stress.
The closed-form analytical reference assumes a smooth distributed reaction; the FE clamp is a pointwise constraint on every face node, so the local stress is higher.
The fix:
For engineering-margin purposes, ignore the clamp- edge peak — read the stress one cell-row in from the clamp where Saint-Venant decay has cleaned the field.
For detailed-design purposes, model a small fillet / pad / pin at the clamp interface so the constraint imposes itself smoothly.
Symptom: modal solve returns first frequency near 0 Hz#
You have a surviving rigid-body mode. Either:
The model is intentionally free-free (a pressure vessel flying through space, a tuning-fork in vacuum). In that case the leading rigid-body modes are physical, and you’d pass them through any downstream response calculation unchanged — see Free-free axial rod — natural frequencies for the canonical free-free pattern.
The model should be constrained but isn’t — see the Boundary-condition pitfalls rigid-body diagnostic.
Symptom: solve is much slower than expected#
Check thread pinning.
echo $OMP_NUM_THREADS. If unset, the BLAS pool sizes itself to the host core count (often 32+) which thrashes 5-10× from scheduler contention. Pin to 4 on workstations, 8 on bigger nodes.Check backend selection.
Result.diagnosticsfrom the static solve reports which linear backend was chosen. If it’s SuperLU, install Pardiso or CHOLMOD — 2-3× speedup for free. See Conditioning and performance.Check matrix size.
Model.stiffness_matrix().shape. Above 500 k DOFs, direct factorisation memory becomes the bottleneck — switch to MUMPS out-of-core or to an iterative solver (CG + AMG vialinear_solver="cg+pyamg").
Symptom: modal frequencies don’t match the closed form#
Check element type. HEX8 plain-Gauss locks badly on slender bending — the recovered mode is artificially stiff. Switch to
ELEMENTS.HEX8(integration="enhanced_strain")(Simo-Rifai EAS).Check slenderness. At \(L/h < 20\), the Timoshenko shear correction the 3-D solid truthfully captures pushes frequencies a few percent below the Bernoulli closed form — that’s real physics, not a solver bug. See the cantilever-higher-modes verification: Cantilever beam — first four bending modes.
Check the mesh-density rule of thumb — Mesh quality. At least 8 elements per wavelength of the highest mode you care about.
Where the documentation can’t help#
If none of the above matches, the issue is more likely a genuine bug in femorph-solver or a use case the library doesn’t yet support. Either:
File a GitHub issue with a minimal reproducer (a
.pyscript that builds the model from scratch). The issue tracker is at femorph/solver#issues.Ask the question with code on hand — the
Result.diagnosticsdict + the rigid-body-mode check + the support-reaction sum are usually enough information for a maintainer to spot the cause without re-running the model.