Linear static analysis ====================== When to use ----------- Linear static analysis answers the question **"what is the displacement and stress field on a structure under a given load, assuming small strains, linear-elastic material, and time- independent loading?"**. It's the workhorse of structural design — the right answer for >80 % of mechanical design checks. Use it when: - Loads are constant in time (or change slowly enough that inertia is negligible). - Material is linear-elastic across the load range. - Geometry change under load is small (typically < 1 % of characteristic length). - You want one snapshot of stress, displacement, and reaction at a given operating point. For periodic loading, use :doc:`/user-guide/solving/harmonic` instead. For shock / impact / time-history, use :doc:`/user-guide/solving/transient`. For the lowest natural frequencies, use :doc:`modal`. Boundary conditions and loads ----------------------------- - **Dirichlet (displacement) constraints** via :meth:`Model.fix(nodes=..., dof=...) `. Pin individual DOFs (``dof="UX"``), all three translations (``dof="ALL"``), or specific values for non-zero displacement. See :doc:`/best-practices/bc-pitfalls` for the rigid-body-mode diagnostic — every static solve needs enough constraints to remove all six rigid-body modes. - **Concentrated forces and moments** via :meth:`Model.apply_force(node, fx=..., fy=..., fz=..., mx=..., my=..., mz=...) `. Use ``accumulate=True`` when adding multiple contributions to the same node — the default *overwrites*, which is the recurring "loads disappear" bug from :doc:`/best-practices/bc-pitfalls`. - **Distributed loads** are applied as nodal-force equivalents via the trapezoid-rule lumping pattern documented in :ref:`sphx_glr_gallery_verification_example_verify_cantilever_udl.py`. A future version will expose ``apply_pressure`` directly; for now, lump manually. The math (one paragraph) ------------------------ Linear static solves :math:`\mathbf{K}\, \mathbf{u} = \mathbf{f}` for the global displacement vector :math:`\mathbf{u}` given the stiffness matrix :math:`\mathbf{K}` and external force vector :math:`\mathbf{f}`. Constrained DOFs partition out via :doc:`/reference/theory/bc_elimination` and the solve factors only the free-DOF block :math:`\mathbf{K}_{ff}`. Reactions at constrained DOFs follow from the partitioned second block (Newton's third law). Full derivation in :doc:`/reference/theory/variational_form` plus :doc:`/reference/theory/bc_elimination`. Running the solve ----------------- The high-level path on a configured ``Model``: .. code-block:: python result = m.solve_static() That returns a :class:`~femorph_solver.solvers.static.StaticResult` carrying: - ``result.displacement`` — ``(N,)`` DOF-indexed displacements. - ``result.reaction`` — ``(N,)`` reaction forces, nonzero only at constrained DOFs. - ``result.free_mask`` — ``(N,)`` bool, ``True`` at the DOFs the solver actually computed. The lower-level :func:`~femorph_solver.solvers.static.solve_static` takes the assembled ``K`` + ``F`` directly, useful when you need to re-solve with different RHS vectors against the same factorised stiffness. See :doc:`/user-guide/solving/static` for the backend / threading details. Post-processing recipes ----------------------- Once the solve has run, the standard recipes: - **Stress recovery** — :ref:`sphx_glr_gallery_post-processing_example_nodal_stress_recovery.py`. - **Principal stresses + directions** — :ref:`sphx_glr_gallery_post-processing_example_principal_stress.py`. - **Reaction forces and global equilibrium** — :ref:`sphx_glr_gallery_post-processing_example_reaction_forces.py`. - **Strain recovery** — :ref:`sphx_glr_gallery_post-processing_example_strain_recovery.py`. - **Visualisation** — :doc:`/user-guide/post-processing/visualisation`. Common gotchas -------------- - **Under-constrained → enormous displacements.** A static solve on a free-floating model returns garbage (the matrix is singular). Run the :doc:`/best-practices/bc-pitfalls` rigid-body-mode check. - **Loads applied to the wrong node set.** Always sum the recovered ``ΣR_z`` reactions and confirm they match ``-ΣF_applied``. See the example above. - **Apply-force overwrites.** Default ``Model.apply_force(...)`` *overwrites* prior loads on the same node + DOF. Pass ``accumulate=True`` when summing across multiple sources. - **Ill-conditioned stiffness from material disparity.** Mixing rubber and steel in the same solve hits :math:`\kappa(K) > 10^9`; see :doc:`/best-practices/conditioning-and-performance`. End-to-end tutorial ------------------- Tutorial 1 walks through the full design-by-analysis loop on a cantilever bracket: :ref:`sphx_glr_gallery_tutorials_tutorial_01_cantilever_combined.py`. Verification examples that exercise this analysis type: - :ref:`sphx_glr_gallery_verification_example_verify_cantilever_eb.py` — Euler-Bernoulli tip deflection - :ref:`sphx_glr_gallery_verification_example_verify_lame_cylinder.py` — Lamé thick cylinder - :ref:`sphx_glr_gallery_verification_example_verify_nafems_le1.py` — NAFEMS LE1 elliptic membrane - :ref:`sphx_glr_gallery_verification_example_verify_plate_with_hole.py` — Kirsch stress concentration Solver mechanics + performance ------------------------------ For the matrix-partitioning derivation, backend selection, threading, and convergence details, see :doc:`/user-guide/solving/static` — that page is the solver-engineering deep-dive on the same ``Model.solve_static()`` invocation.