Source code for opstool.vis.pyvista.vis_frame_resp

from functools import partial
from typing import Optional, Union

import numpy as np
import pyvista as pv

from .._plot_frame_resp_base import PlotFrameResponseBase
from .plot_resp_base import PlotResponsePyvistaBase
from .plot_utils import (
    PLOT_ARGS,
    # _plot_lines_cmap,
    _plot_face_cmap,
    _plot_lines,
    _update_point_label_actor,
)


class PlotFrameResponse(PlotFrameResponseBase, PlotResponsePyvistaBase):
    def __init__(self, odb_tag: Union[int, str], lazy_load: bool = False):
        super().__init__(odb_tag, lazy_load=lazy_load)

    @staticmethod
    def _set_segment_mesh(pos_bot, pos_top, force, resp_points: list, resp_cells: list, scalars: list):
        for i in range(len(force) - 1):
            if force[i] * force[i + 1] >= 0:
                resp_cells.append([
                    4,
                    len(resp_points),
                    len(resp_points) + 1,
                    len(resp_points) + 2,
                    len(resp_points) + 3,
                ])
                resp_points.extend([pos_bot[i], pos_bot[i + 1], pos_top[i + 1], pos_top[i]])
                scalars.extend([force[i], force[i + 1], force[i + 1], force[i]])
            else:
                t = force[i] / (force[i] - force[i + 1])
                coord0 = pos_bot[i] + t * (pos_bot[i + 1] - pos_bot[i])
                resp_cells.append([
                    4,
                    len(resp_points),
                    len(resp_points) + 1,
                    len(resp_points) + 2,
                    len(resp_points) + 3,
                ])
                resp_cells.append([
                    4,
                    len(resp_points) + 4,
                    len(resp_points) + 5,
                    len(resp_points) + 6,
                    len(resp_points) + 7,
                ])
                resp_points.extend([
                    pos_bot[i],
                    coord0,
                    coord0,
                    pos_top[i],
                    pos_bot[i + 1],
                    coord0,
                    coord0,
                    pos_top[i + 1],
                ])
                scalars.extend([force[i], 0.0, 0.0, force[i + 1], force[i + 1], 0.0, 0.0, force[i + 1]])

    def _set_segment_label(self, show_values, pos_top, force, label_points: list, labels: list):
        if isinstance(show_values, str) and show_values.lower() in [
            "elemaxmin",
            "eleminmax",
            "elemax",
            "elemin",
            "all",
        ]:
            if self.resp_type in [
                "localForces",
                "basicForces",
                "basicDeformations",
                "plasticDeformation",
            ]:
                label_points.extend([pos_top[0], pos_top[-1]])
                labels.extend([force[0], force[-1]])
            else:
                if show_values.lower() == "all":
                    label_points.extend(pos_top)
                    labels.extend(force)
                elif show_values.lower() == "elemax":
                    idx = np.argmax(force)
                    label_points.append(pos_top[idx])
                    labels.append(force[idx])
                elif show_values.lower() == "elemin":
                    idx = np.argmin(force)
                    label_points.append(pos_top[idx])
                    labels.append(force[idx])
                else:
                    idxmin = np.argmin(force)
                    idxmax = np.argmax(force)
                    label_points.extend([pos_top[idxmin], pos_top[idxmax]])
                    labels.extend([force[idxmin], force[idxmax]])

    def _get_resp_mesh(self, beam_node_coords, beam_cells, sec_locs, resp, resp_scale, axis_data, show_values):
        show_values = "eleMaxMin" if show_values is True else show_values
        label_points, labels, resp_points, resp_cells, scalars = [], [], [], [], []
        for i, cell in enumerate(beam_cells):
            axis = axis_data[i]
            node1, node2 = cell[1:]
            coord1, coord2 = beam_node_coords[node1], beam_node_coords[node2]
            if self.resp_type in [
                "localForces",
                "basicForces",
                "basicDeformations",
                "plasticDeformation",
            ]:
                f1, f2 = resp_scale[i]
                f1_, f2_ = resp[i]
                locs = np.linspace(0, 1, 11)
                force = np.interp(locs, [0, 1], [f1_, f2_])
                force_scale = np.interp(locs, [0, 1], [f1, f2])
            else:
                locs = sec_locs[i][~np.isnan(sec_locs[i])]
                force = resp[i][~np.isnan(resp[i])]
                force_scale = resp_scale[i][~np.isnan(resp_scale[i])]
            pos1 = [coord1 + loc * (coord2 - coord1) for loc in locs]  # lower
            pos2 = [coord + force_scale[i] * axis * self.plot_axis_sign for i, coord in enumerate(pos1)]  # upper

            self._set_segment_mesh(pos1, pos2, force, resp_points, resp_cells, scalars)
            self._set_segment_label(show_values, pos2, force, label_points, labels)

        resp_points = np.array(resp_points)
        scalars = np.array(scalars)

        if isinstance(show_values, str) and show_values.lower() in ["maxmin", "minmax"]:
            idxmin = np.argmin(scalars)
            idxmax = np.argmax(scalars)
            label_points.extend([resp_points[idxmin], resp_points[idxmax]])
            labels.extend([scalars[idxmin], scalars[idxmax]])
        fmt = self.pargs.scalar_bar_kargs["fmt"]
        labels = [f"{fmt}" % label for label in labels]
        label_poins = np.array(label_points)
        if isinstance(show_values, str) and show_values.lower() in ["maxmin", "minmax"]:
            labels = [f"Min: {labels[0]}", f"Max: {labels[1]}"]
        return resp_points, resp_cells, scalars, labels, label_poins

    def _make_title(self, scalars, step, time):
        info = {
            "title": "Frame",
            "resp_type": self.resp_type.capitalize(),
            "dof": self.component_type,
            "min": np.nanmin(scalars),
            "max": np.nanmax(scalars),
            "step": step,
            "time": time,
        }
        lines = [
            f"{info['step']} (step)",
            f"{info['time']:.3f} (time)",
            f"{info['min']:.3E} (min)",
            f"{info['max']:.3E} (max)",
            "",
            f"{info['title']} Responses",
            f"{info['resp_type']}",
            f"{info['dof']} (DOF)",
        ]
        if self.unit_symbol:
            info["unit"] = self.unit_symbol
            lines.append(f"{info['unit']} (unit)")
        max_len = max(len(line) for line in lines)
        padded_lines = [line.rjust(max_len) for line in lines]
        text = "\n".join(padded_lines)
        return text + "\n"

    def _create_mesh(
        self,
        plotter,
        value,
        ele_tags=None,
        alpha=1.0,
        show_values=True,
        plot_all_mesh=True,
        clim=None,
        line_width=1.0,
        style="surface",
        color=None,
        opacity=1.0,
        cpos="iso",
        show_outline=False,
        show_bc: bool = True,
        bc_scale: float = 1.0,
        show_mp_constraint: bool = False,
    ):
        step = round(value)
        resp = self.resp_step[step].to_numpy()
        resp_scale = resp * alpha
        _, beam_node_coords, beam_cells, yaxis, zaxis = self._make_frame_info(ele_tags, step)
        axis_data = yaxis if self.plot_axis == "y" else zaxis
        sec_locs = self.sec_locs[step].to_numpy()
        resp_points, resp_cells, scalars, labels, label_poins = self._get_resp_mesh(
            beam_node_coords, beam_cells, sec_locs, resp, resp_scale, axis_data, show_values
        )
        #  ---------------------------------
        # plotter.clear_actors()  # !!!!!!
        self.clear_plotter(plotter)
        if plot_all_mesh:
            self._plot_all_mesh(plotter, color="gray", step=step)
        line_grid = _plot_lines(
            plotter,
            pos=beam_node_coords,
            cells=beam_cells,
            width=self.pargs.line_width,
            color="black",
            render_lines_as_tubes=self.pargs.render_lines_as_tubes,
        )
        opacity = 1.0 if style.lower() != "surface" else opacity
        resp_grid = _plot_face_cmap(
            plotter,
            resp_points,
            resp_cells,
            scalars,
            cmap=self.pargs.cmap,
            clim=clim,
            show_edges=False,
            edge_width=line_width,
            opacity=opacity,
            style=style,
            show_scalar_bar=False,
            color=color,
        )

        t_ = self.time[step]
        title = self._make_title(scalars, step, t_)
        scalar_bar = plotter.add_scalar_bar(title=title, **self.pargs.scalar_bar_kargs) if color is None else None
        if scalar_bar:
            # scalar_bar.SetTitle(title)
            title_prop = scalar_bar.GetTitleTextProperty()
            # title_prop.SetJustificationToRight()
            title_prop.BoldOn()

        title_grid = (
            None
            if scalar_bar
            else plotter.add_text(title, position="upper_right", font_size=self.pargs.font_size - 2, font="courier")
        )

        if show_values:
            if isinstance(show_values, str) and show_values.lower() in ["maxmin", "minmax"]:
                shape_opacity = 100.0
                shape = "rounded_rect"
            else:
                shape_opacity = 0.0
                shape = None

            label_grid = plotter.add_point_labels(
                label_poins,
                labels,
                # text_color="white",
                font_size=self.pargs.font_size,
                font_family="courier",
                bold=False,
                always_visible=True,
                shape_color="#c0fb2d",
                shape=shape,
                shape_opacity=shape_opacity,
                show_points=False,
            )
        else:
            label_grid = None
        if show_outline:
            self._plot_outline(plotter)

        bc_grid, mp_grid = None, None
        if show_bc:
            bc_grid = self._plot_bc(plotter, step, defo_scale=0.0, bc_scale=bc_scale)
        if show_mp_constraint:
            mp_grid = self._plot_mp_constraint(plotter, step, defo_scale=0.0)
        self._update_plotter(plotter, cpos=cpos)
        return line_grid, resp_grid, scalar_bar, title_grid, label_grid, bc_grid, mp_grid

    def _update_mesh(
        self,
        step,
        alpha,
        ele_tags,
        show_values,
        line_grid,
        resp_grid,
        scalar_bar,
        title_grid,
        label_grid,
        bc_grid,
        mp_grid,
        bc_scale,
        plotter,
    ):
        step = round(step)
        resp = self.resp_step[step].to_numpy()
        resp_scale = resp * alpha
        beam_tags, beam_node_coords, beam_cells, yaxis, zaxis = self._make_frame_info(ele_tags, step)
        axis_data = yaxis if self.plot_axis == "y" else zaxis
        sec_locs = self.sec_locs[step].to_numpy()
        resp_points, resp_cells, scalars, labels, label_points = self._get_resp_mesh(
            beam_node_coords, beam_cells, sec_locs, resp, resp_scale, axis_data, show_values
        )

        if line_grid:
            line_grid.points = beam_node_coords
            line_grid.lines = beam_cells

        if resp_grid:
            resp_grid.points = resp_points
            # resp_grid.lines = resp_cells
            resp_grid.faces = resp_cells
            resp_grid["scalars"] = scalars

        if scalar_bar:
            title = self._make_title(scalars, step, self.time[step])
            scalar_bar.SetTitle(title)

        if title_grid:
            title_grid.SetText(3, self._make_title(scalars, step, self.time[step]))

        if label_grid:
            if isinstance(show_values, str) and show_values.lower() in ["maxmin", "minmax"]:
                shape_opacity = 100.0
            else:
                shape_opacity = 0.0
            # mapper = label_grid.GetMapper()
            text_property = pv.TextProperty(
                bold=False,
                font_size=self.pargs.font_size,
                font_family="courier",
                color=pv.global_theme.font.color,
            )
            _update_point_label_actor(
                label_grid,
                label_points,
                labels,
                text_property=text_property,
                renderer=plotter.renderer,
                shape_opacity=shape_opacity,
                always_visible=True,
            )

        if mp_grid:
            self._plot_mp_constraint_update(mp_grid, step, defo_scale=0.0)
        if bc_grid:
            self._plot_bc_update(bc_grid, step, defo_scale=0.0, bc_scale=bc_scale)

    def plot_slide(
        self,
        plotter,
        ele_tags=None,
        alpha=1.0,
        resp_type=None,
        component=None,
        show_values=True,
        line_width=1.0,
        style="surface",
        opacity=1.0,
        plot_model=True,
        cpos="iso",
        show_bc: bool = True,
        bc_scale: float = 1.0,
        show_mp_constraint: bool = True,
        show_outline=False,
        color=None,
    ):
        self.refactor_resp_data(ele_tags, resp_type, component)
        alpha_, maxstep, clim = self._get_resp_scale_factor()

        if self.ModelUpdate:
            func = partial(
                self._create_mesh,
                plotter,
                alpha=alpha * alpha_,
                ele_tags=ele_tags,
                show_values=show_values,
                clim=clim,
                plot_all_mesh=plot_model,
                line_width=line_width,
                style=style,
                opacity=opacity,
                cpos=cpos,
                show_outline=show_outline,
                show_bc=show_bc,
                bc_scale=bc_scale,
                show_mp_constraint=show_mp_constraint,
                color=color,
            )
        else:
            line_grid, resp_grid, scalar_bar, title_grid, label_grid, bc_grid, mp_grid = self._create_mesh(
                plotter,
                self.num_steps - 1,
                ele_tags=ele_tags,
                clim=clim,
                plot_all_mesh=plot_model,
                show_values=show_values,
                alpha=alpha * alpha_,
                line_width=line_width,
                style=style,
                opacity=opacity,
                cpos=cpos,
                show_outline=show_outline,
                show_bc=show_bc,
                bc_scale=bc_scale,
                show_mp_constraint=show_mp_constraint,
                color=color,
            )
            func = partial(
                self._update_mesh,
                alpha=alpha * alpha_,
                ele_tags=ele_tags,
                show_values=show_values,
                line_grid=line_grid,
                resp_grid=resp_grid,
                scalar_bar=scalar_bar,
                title_grid=title_grid,
                label_grid=label_grid,
                bc_grid=bc_grid,
                mp_grid=mp_grid,
                bc_scale=bc_scale,
                plotter=plotter,
            )
        plotter.add_slider_widget(func, [0, self.num_steps - 1], value=self.num_steps - 1, **self.slider_widget_args)

    def plot_peak_step(
        self,
        plotter,
        ele_tags=None,
        step="absMax",
        alpha=1.0,
        resp_type=None,
        component=None,
        show_values=True,
        line_width=1.0,
        style="surface",
        opacity=1.0,
        plot_model=True,
        cpos="iso",
        show_bc: bool = True,
        bc_scale: float = 1.0,
        show_mp_constraint: bool = True,
        show_outline=False,
        color=None,
    ):
        self.refactor_resp_data(ele_tags, resp_type, component)
        alpha_, step, clim = self._get_resp_scale_factor(idx=step)
        self._create_mesh(
            plotter=plotter,
            value=step,
            alpha=alpha * alpha_,
            ele_tags=ele_tags,
            show_values=show_values,
            clim=clim,
            plot_all_mesh=plot_model,
            line_width=line_width,
            style=style,
            opacity=opacity,
            cpos=cpos,
            show_outline=show_outline,
            show_bc=show_bc,
            bc_scale=bc_scale,
            show_mp_constraint=show_mp_constraint,
            color=color,
        )

    def plot_anim(
        self,
        plotter,
        ele_tags=None,
        alpha=1.0,
        resp_type=None,
        component=None,
        show_values=True,
        framerate=None,
        savefig: str = "FrameForcesAnimation.gif",
        line_width=1.0,
        style="surface",
        opacity=1.0,
        plot_model=True,
        cpos="iso",
        show_bc: bool = True,
        bc_scale: float = 1.0,
        show_mp_constraint: bool = True,
        show_outline=False,
        color=None,
    ):
        if framerate is None:
            framerate = np.ceil(self.num_steps / 10)
        if savefig.endswith(".gif"):
            plotter.open_gif(savefig, fps=framerate)
        else:
            plotter.open_movie(savefig, framerate=framerate)
        self.refactor_resp_data(ele_tags, resp_type, component)
        alpha_, maxstep, clim = self._get_resp_scale_factor()
        # plotter.write_frame()  # write initial data
        if self.ModelUpdate:
            for step in range(self.num_steps):
                self._create_mesh(
                    plotter,
                    step,
                    alpha=alpha * alpha_,
                    ele_tags=ele_tags,
                    show_values=show_values,
                    clim=clim,
                    plot_all_mesh=plot_model,
                    line_width=line_width,
                    style=style,
                    opacity=opacity,
                    cpos=cpos,
                    show_outline=show_outline,
                    show_bc=show_bc,
                    bc_scale=bc_scale,
                    show_mp_constraint=show_mp_constraint,
                    color=color,
                )
                plotter.write_frame()
        else:
            line_grid, resp_grid, scalar_bar, title_grid, label_grid, bc_grid, mp_grid = self._create_mesh(
                plotter,
                0,
                ele_tags=ele_tags,
                clim=clim,
                plot_all_mesh=plot_model,
                show_values=show_values,
                alpha=alpha * alpha_,
                line_width=line_width,
                style=style,
                opacity=opacity,
                cpos=cpos,
                show_outline=show_outline,
                show_bc=show_bc,
                bc_scale=bc_scale,
                show_mp_constraint=show_mp_constraint,
                color=color,
            )
            plotter.write_frame()
            for step in range(1, self.num_steps):
                self._update_mesh(
                    step=step,
                    alpha=alpha * alpha_,
                    ele_tags=ele_tags,
                    show_values=show_values,
                    line_grid=line_grid,
                    resp_grid=resp_grid,
                    scalar_bar=scalar_bar,
                    title_grid=title_grid,
                    label_grid=label_grid,
                    bc_grid=bc_grid,
                    mp_grid=mp_grid,
                    bc_scale=bc_scale,
                    plotter=plotter,
                )
                plotter.write_frame()


[docs] def plot_frame_responses( odb_tag: Union[int, str] = 1, ele_tags: Optional[Union[int, list]] = None, resp_type: str = "sectionForces", resp_dof: str = "MZ", unit_symbol: Optional[str] = None, unit_factor: Optional[float] = None, slides: bool = False, step: Union[int, str] = "absMax", scale: float = 1.0, show_values: Union[bool, str] = "MaxMin", style: str = "surface", color: Optional[str] = None, line_width: float = 1.5, opacity: float = 1.0, show_bc: bool = True, bc_scale: float = 1.0, show_mp_constraint: bool = False, show_outline: bool = False, cpos: str = "iso", show_model: bool = True, lazy_load: bool = False, ) -> pv.Plotter: """Plot the responses of the frame element. Parameters ---------- odb_tag: Union[int, str], default: 1 Tag of output databases (ODB) to be visualized. ele_tags: Union[int, list], default: None The tags of frame elements to be visualized. If None, all frame elements are selected. resp_type: str, default: "sectionforces" Response type, optional, one of ["localForces", "basicForces", "basicDeformations", "plasticDeformation", "sectionForces", "sectionDeformations"]. resp_dof: str, default: "MZ" Component type corrsponding to the resp_type. - For `localForces`: ["FX", "FY", "FZ", "MX", "MY", "MZ"] - For `basicForces`: ["N", "MZ", "MY", "T"] - For `basicDeformations`: ["N", "MZ", "MY", "T"] - For `plasticDeformation`: ["N", "MZ", "MY", "T"] - For `sectionForces`: ["N", "MZ", "VY", "MY", "VZ", "T"] - For `sectionDeformations`: ["N", "MZ", "VY", "MY", "VZ", "T"] .. Note:: For `sectionForces` and `sectionDeformations`, not all sections include the shear dof VY and VZ. For instance, in the most commonly used 3D fiber cross-sections, only the axial force N, bending moments MZ and MY, and torsion T are available. unit_symbol: str, default: None Unit symbol to be displayed in the plot. unit_factor: float, default: None The multiplier used to convert units. For example, if you want to visualize stress and the current data unit is kPa, you can set ``unit_symbol="kPa" and unit_factor=1.0``. If you want to visualize in MPa, you can set ``unit_symbol="MPa" and unit_factor=0.001``. slides: bool, default: False Display the response for each step in the form of a slideshow. Otherwise, show the step with the following ``step`` parameter. step: Union[int, str], default: "absMax" If slides = False, this parameter will be used as the step to plot. If str, Optional: [absMax, absMin, Max, Min]. If int, this step will be demonstrated (counting from 0). show_values: Union[bool, str], default: MaxMin Whether to display the response value. If str, optional: ["MaxMin", "eleMaxMin", "eleMax", "eleMin", "all"]. - "MaxMin": show the max and min values of the response. - "eleMaxMin": show the max and min values of the response for each element. - "eleMax": show the max value of the response for each element. - "eleMin": show the min value of the response for each element. - "all": show all values of the response for each element. scale: float, default: 1.0 Scale the size of the response graph. .. Note:: You can adjust the scale to make the response graph more visible. A negative number will reverse the direction. cpos: str, default: iso Model display perspective, optional: "iso", "xy", "yx", "xz", "zx", "yz", "zy". If 3d, defaults to "iso". If 2d, defaults to "xy". style: str, default: "surface Display style for responses plot, optional, one of ["surface", "wireframe"] color: str, default: None Single color of the response graph. If None, the colormap will be used. line_width: float, default: 1.5. Line width of the response graph when style="wireframe". opacity: float, default: 1.0 Face opacity when style="surface". show_bc: bool, default: True Whether to display boundary supports. bc_scale: float, default: 1.0 Scale the size of boundary support display. show_mp_constraint: bool, default: False Whether to show multipoint (MP) constraint. show_outline: bool, default: False Whether to display the outline of the model. show_model: bool, default: True Whether to plot the all model or not. lazy_load: bool, default: False Whether to lazily load the response data. If True, the response data will be loaded on demand when needed for plotting. This can save memory when dealing with large datasets. If False, all response data will be loaded into memory at once. If you encounter memory issues, consider setting this parameter to True, elsewise, set it to False for plotting in safety. Returns ------- Plotting object of PyVista to display vtk meshes or numpy arrays. See `pyvista.Plotter <https://docs.pyvista.org/api/plotting/_autosummary/pyvista.plotter>`_. You can use `Plotter.show <https://docs.pyvista.org/api/plotting/_autosummary/pyvista.plotter.show#pyvista.Plotter.show>`_. to display the plotting window. You can also use `Plotter.export_html <https://docs.pyvista.org/api/plotting/_autosummary/pyvista.plotter.export_html#pyvista.Plotter.export_html>`_. to export this plotter as an interactive scene to an HTML file. """ plotter = pv.Plotter( notebook=PLOT_ARGS.notebook, line_smoothing=PLOT_ARGS.line_smoothing, off_screen=PLOT_ARGS.off_screen, ) plotbase = PlotFrameResponse(odb_tag, lazy_load=lazy_load) plotbase.set_unit(symbol=unit_symbol, factor=unit_factor) if slides: plotbase.plot_slide( plotter, ele_tags=ele_tags, alpha=scale, show_values=show_values, resp_type=resp_type, component=resp_dof, line_width=line_width, style=style, opacity=opacity, plot_model=show_model, cpos=cpos, show_bc=show_bc, bc_scale=bc_scale, show_mp_constraint=show_mp_constraint, show_outline=show_outline, color=color, ) else: plotbase.plot_peak_step( plotter, ele_tags=ele_tags, step=step, alpha=scale, show_values=show_values, resp_type=resp_type, component=resp_dof, line_width=line_width, style=style, opacity=opacity, plot_model=show_model, cpos=cpos, show_bc=show_bc, bc_scale=bc_scale, show_mp_constraint=show_mp_constraint, show_outline=show_outline, color=color, ) if PLOT_ARGS.anti_aliasing: plotter.enable_anti_aliasing(PLOT_ARGS.anti_aliasing) return plotbase._update_plotter(plotter, cpos)
[docs] def plot_frame_responses_animation( odb_tag: Union[int, str] = 1, ele_tags: Optional[Union[int, list]] = None, resp_type: str = "sectionForces", resp_dof: str = "MZ", unit_symbol: Optional[str] = None, unit_factor: Optional[float] = None, scale: float = 1.0, show_values: Union[bool, str] = "MaxMin", framerate: Optional[int] = None, savefig: str = "FrameForcesAnimation.gif", off_screen: bool = True, style: str = "surface", color: Optional[str] = None, line_width: float = 1.5, opacity: float = 1.0, show_bc: bool = True, bc_scale: float = 1.0, show_mp_constraint: bool = False, show_outline: bool = False, cpos: str = "iso", show_model: bool = True, lazy_load: bool = False, ) -> pv.Plotter: """Animate the responses of frame elements. Parameters ---------- odb_tag: Union[int, str], default: 1 Tag of output databases (ODB) to be visualized. ele_tags: Union[int, list], default: None The tags of frame elements to be visualized. If None, all frame elements are selected. resp_type: str, default: "sectionforces" Response type, optional, one of ["localForces", "basicForces", "basicDeformations", "plasticDeformation", "sectionForces", "sectionDeformations"]. resp_dof: str, default: "MZ" Component type corrsponding to the resp_type. - For `localForces`: ["FX", "FY", "FZ", "MX", "MY", "MZ"] - For `basicForces`: ["N", "MZ", "MY", "T"] - For `basicDeformations`: ["N", "MZ", "MY", "T"] - For `plasticDeformation`: ["N", "MZ", "MY", "T"] - For `sectionForces`: ["N", "MZ", "VY", "MY", "VZ", "T"] - For `sectionDeformations`: ["N", "MZ", "VY", "MY", "VZ", "T"] .. Note:: For `sectionForces` and `sectionDeformations`, not all sections include the shear dof VY and VZ. For instance, in the most commonly used 3D fiber cross-sections, only the axial force N, bending moments MZ and MY, and torsion T are available. unit_symbol: str, default: None Unit symbol to be displayed in the plot. unit_factor: float, default: None The multiplier used to convert units. For example, if you want to visualize stress and the current data unit is kPa, you can set ``unit_symbol="kPa" and unit_factor=1.0``. If you want to visualize in MPa, you can set ``unit_symbol="MPa" and unit_factor=0.001``. scale: float, default: 1.0 Scale the size of the response graph. .. Note:: You can adjust the scale to make the response graph more visible. A negative number will reverse the direction. show_values: Union[bool, str], default: MaxMin Whether to display the response value. If str, optional: ["MaxMin", "eleMaxMin", "eleMax", "eleMin", "all"]. - "MaxMin": show the max and min values of the response. - "eleMaxMin": show the max and min values of the response for each element. - "eleMax": show the max value of the response for each element. - "eleMin": show the min value of the response for each element. - "all": show all values of the response for each element. framerate: int, default: None Framerate for the display, i.e., the number of frames per second. savefig: str, default: FrameForcesAnimation.gif Path to save the animation. The suffix can be ``.gif`` or ``.mp4``. cpos: str, default: iso Model display perspective, optional: "iso", "xy", "yx", "xz", "zx", "yz", "zy". If 3d, defaults to "iso". If 2d, defaults to "xy". off_screen: bool, default: True Whether to display the plotting window. If True, the plotting window will not be displayed. style: str, default: "surface Display style for responses plot, optional, one of ["surface", "wireframe"] color: str, default: None Single color of the response graph. If None, the colormap will be used. line_width: float, default: 1.5. Line width of the response graph when style="wireframe". opacity: float, default: 1.0 Face opacity when style="surface". show_model: bool, default: True Whether to plot the all model or not. show_bc: bool, default: True Whether to display boundary supports. bc_scale: float, default: 1.0 Scale the size of boundary support display. show_mp_constraint: bool, default: False Whether to show multipoint (MP) constraint. show_outline: bool, default: False Whether to display the outline of the model. lazy_load: bool, default: False Whether to lazily load the response data. If True, the response data will be loaded on demand when needed for plotting. This can save memory when dealing with large datasets. If False, all response data will be loaded into memory at once. If you encounter memory issues, consider setting this parameter to True, elsewise, set it to False for plotting in safety. Returns ------- Plotting object of PyVista to display vtk meshes or numpy arrays. See `pyvista.Plotter <https://docs.pyvista.org/api/plotting/_autosummary/pyvista.plotter>`_. You can use `Plotter.show <https://docs.pyvista.org/api/plotting/_autosummary/pyvista.plotter.show#pyvista.Plotter.show>`_. to display the plotting window. You can also use `Plotter.export_html <https://docs.pyvista.org/api/plotting/_autosummary/pyvista.plotter.export_html#pyvista.Plotter.export_html>`_. to export this plotter as an interactive scene to an HTML file. """ plotter = pv.Plotter( notebook=PLOT_ARGS.notebook, line_smoothing=PLOT_ARGS.line_smoothing, off_screen=off_screen, ) plotbase = PlotFrameResponse(odb_tag, lazy_load=lazy_load) plotbase.set_unit(symbol=unit_symbol, factor=unit_factor) plotbase.plot_anim( plotter, ele_tags=ele_tags, alpha=scale, show_values=show_values, resp_type=resp_type, component=resp_dof, framerate=framerate, savefig=savefig, line_width=line_width, style=style, opacity=opacity, plot_model=show_model, cpos=cpos, show_bc=show_bc, bc_scale=bc_scale, show_mp_constraint=show_mp_constraint, show_outline=show_outline, color=color, ) if PLOT_ARGS.anti_aliasing: plotter.enable_anti_aliasing(PLOT_ARGS.anti_aliasing) print(f"Animation has been saved as {savefig}!") return plotbase._update_plotter(plotter, cpos)