Source code for vane.viz.modeshape_static

"""Static mode-shape plotting.

Since OpenFAST mode shapes are expressed over modal degrees of freedom rather than
spatial coordinates, a mode is visualized as a horizontal bar chart of the signed
participation of its most active degrees of freedom: bar length is the relative
contribution and the sign encodes the in-/out-of-phase relationship to the
dominant degree of freedom.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

import matplotlib.pyplot as plt
import numpy as np

from vane.modal.participation import participation_from_modes

if TYPE_CHECKING:
    from matplotlib.figure import Figure

    from vane.modal.eigensolver import ModalSolution

__all__ = ["plot_mode_shape"]


[docs] def plot_mode_shape( solution: ModalSolution, mode_index: int, *, top_n: int = 10 ) -> Figure: """Plot the signed participation of a mode's most active degrees of freedom. Parameters ---------- solution : ModalSolution A solution whose ``dof_descriptions`` are populated. mode_index : int Index of the mode to plot. top_n : int, optional Number of highest-participation degrees of freedom to show. Returns ------- matplotlib.figure.Figure The bar-chart figure. Raises ------ ValueError If the solution has no DOF descriptions, ``mode_index`` is out of range, or ``top_n`` is not positive. """ if not solution.dof_descriptions: msg = "ModalSolution has no dof_descriptions; cannot plot mode shape" raise ValueError(msg) if not 0 <= mode_index < solution.n_modes: msg = f"mode_index {mode_index} out of range [0, {solution.n_modes})" raise ValueError(msg) if top_n < 1: msg = f"top_n must be at least 1, got {top_n}" raise ValueError(msg) participation = participation_from_modes(solution) signed = participation.signed_magnitude[:, mode_index] order = np.argsort(np.abs(signed))[::-1][:top_n] labels = [solution.dof_descriptions[i] for i in order.tolist()] values = signed[order] figure, axes = plt.subplots() positions = np.arange(len(order)) axes.barh(positions, values, color="steelblue") axes.set_yticks(positions) axes.set_yticklabels(labels) axes.invert_yaxis() axes.axvline(0.0, color="black", linewidth=0.8) axes.set_xlabel("Signed participation") axes.set_title( f"Mode {mode_index} - {solution.natural_frequencies_hz[mode_index]:.3f} Hz" ) figure.tight_layout() result: Figure = figure return result