Source code for vane.viz.modeshape_3d

"""Three-dimensional complex mode-shape plotting.

A complex mode shape carries both magnitude and phase per degree of freedom. This
view renders the most active degrees of freedom as phasors in three dimensions:
the horizontal axis orders the degrees of freedom by participation while the other
two axes are the real and imaginary parts of each component, so the spread out of
the real plane shows how non-proportional (complex) the mode is.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

import numpy as np
import plotly.graph_objects as go

if TYPE_CHECKING:
    from vane.modal.eigensolver import ModalSolution

__all__ = ["plot_mode_3d"]


[docs] def plot_mode_3d( solution: ModalSolution, mode_index: int, *, top_n: int = 12 ) -> go.Figure: """Plot a mode's most active degrees of freedom as 3-D complex phasors. 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-magnitude degrees of freedom to show. Returns ------- plotly.graph_objects.Figure The 3-D phasor 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) shape = solution.mode_shapes[:, mode_index] order = np.argsort(np.abs(shape))[::-1][:top_n] figure = go.Figure() for rank, dof in enumerate(order.tolist()): component = shape[dof] figure.add_trace( go.Scatter3d( x=[float(rank), float(rank)], y=[0.0, float(component.real)], z=[0.0, float(component.imag)], mode="lines+markers", name=solution.dof_descriptions[dof], ) ) figure.update_layout( title=f"Mode {mode_index} - " f"{solution.natural_frequencies_hz[mode_index]:.3f} Hz", scene={ "xaxis_title": "DOF (by participation)", "yaxis_title": "Real", "zaxis_title": "Imaginary", }, ) return figure