Frequency analysis (structsolve.freq)#

structsolve.freq(K, M, tol=0, sparse_solver=True, silent=False, sort=True, num_eigvalues=25, num_eigvalues_print=5, skip_null_cols=False, symmetric=False, check_rtol=1e-06, max_dense_size=2000)#

Frequency analysis

Calculates the eigenvalues \(\lambda^2\) and eigenvectors \(\{u\}\) of the free-vibration eigenvalue problem:

\[([K] + \lambda^2 [M])\{u\} = \{0\}\]

where \(\lambda^2 = -\omega_n^2\) and \(\omega_n\) is the natural frequency in rad/s. The null rows and columns of K are removed from both matrices before solving the eigenvalue problem, unless skip_null_cols=True.

Parameters:
Ksparse_matrix

Stiffness matrix. Should include initial stress stiffness matrix, aerodynamic matrix and so forth when applicable.

Msparse_matrix

Mass matrix.

tolfloat, optional

A tolerance value passed to scipy.sparse.linalg.eigs() and scipy.sparse.linalg.eigsh().

sparse_solverbool, optional

With True, the default, the sparse solver always runs first, whatever the size of the problem: scipy.sparse.linalg.eigs(), or scipy.sparse.linalg.eigsh() with symmetric=True, in shift-invert mode with a negative shift estimated from the matrices, calculating the num_eigvalues eigenvalues closest to the shift. Its eigenpairs are verified, and a dense solver is only used as a last resort, see the Notes. With False, the full problem is explicitly solved with a dense solver, scipy.linalg.eig(), or scipy.linalg.eigh() with symmetric=True, without trying the sparse solver, which calculates all eigenvalues, e.g. for the kinetic criterion of stability, and is a reference for validations.

silentbool, optional

A boolean to tell whether the log messages should be printed.

sortbool, optional

Sort the eigenvectors by increasing natural frequency, keeping only those with a natural frequency larger than 1e-6 rad/s. The returned eigenvalues are not affected by this option.

num_eigvaluesint, optional

Number of calculated eigenvalues with the sparse solver, limited to the size of M minus 2.

num_eigvalues_printint, optional

Number of eigenvalues to print.

skip_null_colsbool, optional

If True, skip the removal of null columns from the matrices. Use only when K and M are known to be non-singular.

symmetricbool or None, optional

False, the default, uses the general solvers described above, valid for unsymmetric matrices, e.g. K with an aerodynamic matrix or with the load stiffness of follower loads. True uses the solvers of symmetric matrices, scipy.sparse.linalg.eigsh() in shift-invert mode or scipy.linalg.eigh(), which require symmetric K and M and a positive definite M, and return real eigenvalues and eigenvectors normalized with respect to M. None selects them when linear_buckling.is_symmetric() is true for both matrices.

check_rtolfloat or None, optional

Maximum relative residual of an eigenpair, \(||K u + \lambda^2 M u||/(||K u|| + |\lambda^2| ||M u||)\). A larger residual of the sparse solver is a failed verification, see the Notes. For sparse_solver=False, a warning is issued. None skips the check.

max_dense_sizeint, optional

Maximum size of the dense problem solved as a last resort when the sparse solver failed, see the Notes. Larger problems raise a RuntimeError instead, and 0 disables the dense fallback.

Returns:
lambda2ndarray

Complex array with the eigenvalues \(\lambda^2 = -\omega_n^2\), in the order returned by the eigenvalue solver, real with symmetric=True.

eigvecsndarray

The \(i^{th}\) eigenvector is eigvecs[:, i], with the size of the original matrices. The sparse solver returns eigenvectors normalized with respect to M, the dense solver returns eigenvectors with unit Euclidean norm. If sort=True the columns are sorted and filtered as described above, in which case they correspond to the eigenvalues in lambda2 only if the solver already returned them in the order of increasing natural frequency.

Raises:
RuntimeError

When the sparse solver failed and the dense fallback was not possible or failed too, listing the attempted solvers and their failures.

Warns:
.linear_buckling.DenseFallbackWarning

When the sparse solver failed and the dense solver was used.

Notes

Solver policy. As in lb(), with sparse_solver=True the sparse solver runs first for every problem size, and its eigenpairs are verified:

  • the relative residual of each eigenpair must not be larger than check_rtol;

  • with symmetric=True, no eigenvalue \(\omega_n^2\) lower than the highest one returned may be missing: the number of negative pivots of \([K] - s [M]\), for \(s\) slightly lower than this eigenvalue, is the number of eigenvalues below \(s\) (Sylvester’s law of inertia).

When the verification fails or the sparse solver raises an error, e.g. ARPACK did not converge, the dense solver is used as a last resort if the problem has at most max_dense_size dofs, with a linear_buckling.DenseFallbackWarning giving the reasons. It returns the num_eigvalues lowest eigenvalues with symmetric=True, otherwise the num_eigvalues eigenvalues closest to the shift, as the sparse solver, and its eigenpairs are verified as well. Otherwise a RuntimeError is raised. Until version 0.5.3, the sparse solver only issued a warning when the residual check failed.

scipy.sparse.linalg.eigsh() uses ARPACK’s symmetric drivers, which return wrong eigenpairs when linked against Intel MKL 2024.2.0 to 2025.0.0 and ncv > 32, see lb() and structsolve.arpackutils, where ncv is capped accordingly.

Non-conservative systems, e.g. under follower loads, may lose stability by flutter, which the static criterion of lb() does not detect: with K the tangent stiffness matrix at a load level, including the load stiffness, the eigenvalues \(\lambda^2\) of the general solvers are real and negative below the flutter load and become complex conjugate pairs above it, when two natural frequencies coalesce (the kinetic criterion). A real positive \(\lambda^2\) indicates divergence.

See also Eigenvalue solvers for the solver policy.