Modal analysis ============== Free-vibration modal eigenproblem :math:`\mathbf{K}\, \boldsymbol{\phi} = \omega^{2}\, \mathbf{M}\, \boldsymbol{\phi}`, driven by :meth:`Model.modal_solve `. The mathematical foundations live in :doc:`../theory/eigenproblem`; this chapter covers the implementation. Algorithm --------- 1. **Assemble** :math:`\mathbf{K}` and :math:`\mathbf{M}` from the element kernels (consistent or row-summed lumped, per the ``lumped`` keyword). 2. **Eliminate Dirichlet DOFs** — the same partition the static path uses (:doc:`../theory/bc_elimination`). Both matrices are reduced to the free-DOF block. 3. **Shift-invert Lanczos.** The default backend is :func:`scipy.sparse.linalg.eigsh` with ``sigma=0``, which constructs the operator :math:`\mathbf{S}_{0} = \mathbf{K}^{-1}\, \mathbf{M}`, factors :math:`\mathbf{K}` once through the registered linear backend (see :doc:`linear_backends`), then re-uses the factor for every Lanczos matrix-vector product. Eigenvalues are back-transformed via :math:`\omega^{2} = 1 / \mu`. 4. **Mass-orthonormalise** the returned eigenvectors — :math:`\boldsymbol{\phi}_{i}^{\!\top}\, \mathbf{M}\, \boldsymbol{\phi}_{j} = \delta_{ij}`. This is the natural basis for modal-superposition post-processing because the modal-mass matrix is the identity. 5. **Sort ascending** by frequency and populate the :class:`ModalResult `. Backend dispatch ---------------- The ``eigen_solver`` keyword controls backend selection: * ``"auto"`` (default) — ARPACK ``eigsh`` for small / medium problems, PRIMME (when installed) for large or many-mode runs. * ``"arpack"`` — explicit ARPACK shift-invert. * ``"primme"`` — PRIMME block-Davidson (Stathopoulos & McCombs 2010); needs the optional ``primme`` extra. * ``"lobpcg"`` — factor-free locally-optimal block PCG (Knyazev 2001) with selectable preconditioner. See :doc:`eigen_backends` for backend-side details. Tunables -------- * ``n_modes`` — number of lowest modes to extract. Default 10. * ``sigma`` — shift target for the inverse map. Default 0; pass an explicit value to extract modes near a target frequency on a model whose lowest 50 modes lie below it. * ``lumped`` — diagonal HRZ row-sum mass for explicit-dynamics parity. Default consistent. * ``linear_solver`` — backend identifier for the inner shift-invert factor (see :doc:`linear_backends`). Public API ---------- * :meth:`femorph_solver.Model.modal_solve` * :class:`femorph_solver.solvers.modal.ModalResult` — ``frequency`` (Hz), ``omega_sq`` ((rad/s)²), ``mode_shapes`` (mass-orthonormal). * :func:`femorph_solver.io.modal_result_to_grid` — attaches one ``mode__disp`` array per mode plus a magnitude scalar to a copy of the input grid. Verification cross-references ----------------------------- * :ref:`sphx_glr_gallery_verification_example_verify_axial_rod_nf.py` — fixed-free rod natural frequencies. * :ref:`sphx_glr_gallery_verification_example_verify_cantilever_nf.py` — first-mode cantilever. * :ref:`sphx_glr_gallery_verification_example_verify_cantilever_higher_modes.py` — higher β_n L roots. * :ref:`sphx_glr_gallery_verification_example_verify_ss_plate_modes.py` — Kirchhoff plate fundamental. Implementation: :mod:`femorph_solver.solvers.modal`. References ---------- * Parlett, B. N. (1998) *The Symmetric Eigenvalue Problem*, SIAM, §13–§14 (shift-invert Lanczos). * Lehoucq, R. B., Sorensen, D. C. and Yang, C. (1998) *ARPACK Users' Guide*, SIAM SET 6. * Ericsson, T. and Ruhe, A. (1980) "The spectral transformation Lanczos method," *Math. Comp.* 35, 1251–1268. * Stathopoulos, A. and McCombs, J. R. (2010) "PRIMME: PReconditioned Iterative MultiMethod Eigensolver — Methods and Software Description," *ACM TOMS* 37 (2), 1–30. * Knyazev, A. V. (2001) "Toward the optimal preconditioned eigensolver: locally optimal block preconditioned conjugate gradient method," *SIAM J. Sci. Comput.* 23 (2), 517–541. * Bathe, K.-J. (2014) *Finite Element Procedures*, 2nd ed., §11 (eigensolution methods).