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