Source code for opstool.pre.section._plot_fiber_sec_by_cmds

import copy
from collections import defaultdict
from functools import wraps
from typing import ClassVar, Optional, Union

import matplotlib.patches as mpathes
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.collections import LineCollection

from ...utils import get_cycle_color, get_opensees_module

ops = get_opensees_module()

COLORS = get_cycle_color()

plt.rcParams["xtick.direction"] = "in"
plt.rcParams["ytick.direction"] = "in"


class FiberSecPlot:
    patches: ClassVar = defaultdict(lambda: [])
    line2Ds: ClassVar = defaultdict(lambda: [])
    sec_tag = 1

    @classmethod
    def set_sec_tag(cls, tag):
        cls.sec_tag = tag

    @classmethod
    def get_sec_tag(cls):
        return cls.sec_tag

    @classmethod
    def add_fiber_points(cls, args: Union[list, tuple], color: Union[str, tuple, list], alpha: float):
        y, z, area = args[0], args[1], args[2]
        r = np.sqrt(area / np.pi)
        circle = mpathes.Circle((y, z), r, color=color, alpha=alpha)
        cls.patches[cls.sec_tag].append(circle)

    @classmethod
    def add_rect(cls, args: Union[list, tuple], color: Union[str, tuple, list], alpha: float):
        numy, numz = args[1], args[2]
        yi, zi, yj, zj = args[3:]
        rect = mpathes.Rectangle((yi, zi), yj - yi, zj - zi, color=color, alpha=alpha)
        cls.patches[cls.sec_tag].append(rect)

        delta_y = np.abs(yj - yi) / numy
        delta_z = np.abs(zj - zi) / numz
        yg = np.arange(yi, yj + 0.1 * delta_y, delta_y)
        zg = np.arange(zi, zj + 0.1 * delta_z, delta_z)
        for y_ in yg:
            cls.line2Ds[cls.sec_tag].append([(y_, zi), (y_, zj)])
        for z_ in zg:
            cls.line2Ds[cls.sec_tag].append([(yi, z_), (yj, z_)])

    @classmethod
    def add_quad(cls, args: Union[list, tuple], color: Union[str, tuple, list], alpha: float):
        numij, numjk = args[1], args[2]
        yi, zi, yj, zj, yk, zk, yl, zl = args[3:]
        yz = np.array([[yi, zi], [yj, zj], [yk, zk], [yl, zl]])
        yzij = _line_mesh(yi, zi, yj, zj, numij)
        yzjk = _line_mesh(yj, zj, yk, zk, numjk)
        yzkl = _line_mesh(yk, zk, yl, zl, numij)
        yzli = _line_mesh(yl, zl, yi, zi, numjk)

        polygon = mpathes.Polygon(yz, color=color, alpha=alpha)
        cls.patches[cls.sec_tag].append(polygon)

        for i in range(len(yzij)):
            yz1 = yzij[i]
            yz2 = yzkl[::-1][i]
            cls.line2Ds[cls.sec_tag].append([(yz1[0], yz1[1]), (yz2[0], yz2[1])])
        for i in range(len(yzjk)):
            yz1 = yzjk[i]
            yz2 = yzli[::-1][i]
            cls.line2Ds[cls.sec_tag].append([(yz1[0], yz1[1]), (yz2[0], yz2[1])])

    @classmethod
    def add_circ(cls, args: Union[list, tuple], color: Union[str, tuple, list], alpha: float):
        num_circ, num_rad = args[1], args[2]
        yc, zc = args[3], args[4]
        r_in, r_ex = args[5], args[6]
        if len(args) > 7:
            ang_s, ang_e = args[7], args[8]
        else:
            ang_s, ang_e = 0.0, 360
        node_ex = _arc_mesh(ang_s, ang_e, r_ex, num_circ, yc, zc)
        node_in = _arc_mesh(ang_s, ang_e, r_in, num_circ, yc, zc)

        wedge = mpathes.Wedge((yc, zc), r_ex, ang_s, ang_e, width=r_ex - r_in, color=color, alpha=alpha)
        cls.patches[cls.sec_tag].append(wedge)

        for i in range(num_circ + 1):
            yz_ex = node_ex[i]
            yz_in = node_in[i]
            cls.line2Ds[cls.sec_tag].append([(yz_in[0], yz_in[1]), (yz_ex[0], yz_ex[1])])

        for i in range(num_rad + 1):
            delta_r = (r_ex - r_in) / num_rad
            arc = mpathes.Arc(
                (yc, zc),
                2 * (r_in + i * delta_r),
                2 * (r_in + i * delta_r),
                theta1=ang_s,
                theta2=ang_e,
                lw=0.5,
                color="k",
                alpha=alpha,
            )
            cls.patches[cls.sec_tag].append(arc)

    @classmethod
    def add_straight_layer(cls, args: Union[list, tuple], color: Union[str, tuple, list], alpha: float):
        num, area = args[1], args[2]
        r = np.sqrt(area / np.pi)
        yi, zi, yj, zj = args[3:]
        node = _line_mesh(yi, zi, yj, zj, num - 1)
        for i in range(num):
            circle = mpathes.Circle(node[i], r, color=color, alpha=alpha)
            cls.patches[cls.sec_tag].append(circle)

    @classmethod
    def add_circ_layer(cls, args: Union[list, tuple], color: Union[str, tuple, list], alpha: float):
        num, area = args[1], args[2]
        yc, zc, r = args[3], args[4], args[5]
        if len(args) > 6:
            ang_s, ang_e = args[6], args[7]
        else:
            ang_s, ang_e = 0.0, 360.0 - 360 / num
        node = _arc_mesh(ang_s, ang_e, r, num - 1, yc, zc)
        for i in range(num):
            circle = mpathes.Circle(node[i], np.sqrt(area / np.pi), color=color, alpha=alpha)
            cls.patches[cls.sec_tag].append(circle)

    @classmethod
    def plot_sec(
        cls,
        sec_tag: int,
        title: str = "My Section",
        label_size: int = 15,
        tick_size: int = 12,
        title_size: int = 18,
    ):
        _, ax = plt.subplots()
        # patches
        for pat in cls.patches[sec_tag]:
            ax.add_patch(copy.copy(pat))
        # mesh lines
        lc = LineCollection(cls.line2Ds[sec_tag], linewidths=0.5, colors="k")
        ax.add_collection(lc)
        # ----------------------------------------
        ax.set_xlabel("y", fontsize=label_size)
        ax.set_ylabel("z", fontsize=label_size)
        ax.tick_params(axis="both", which="major", width=1.2, length=5, labelsize=tick_size)
        ax.tick_params(axis="both", which="minor", width=0.8, length=3)
        ax.set_title(title, fontsize=title_size)
        ax.grid(False)
        ax.autoscale()
        plt.minorticks_on()
        for loc in ["bottom", "top", "left", "right"]:
            ax.spines[loc].set_linewidth(1.0)
        # plt.gcf().subplots_adjust(bottom=0.15)
        plt.axis("equal")
        plt.tight_layout()
        plt.show()


def fetch_fiber_plot_data(func):  # noqa: C901
    """Fetch data from fiber creation commands for visualization; commands include
    'fiber', 'patch', 'layer'. Note that this is a decorator.
    """
    fiber_type = func.__name__

    @wraps(func)
    def wrapper(*args, color: Optional[Union[str, tuple, list]] = None, opacity: float = 1.0):
        if color is None:
            color = next(COLORS)
        if fiber_type == "fiber":
            FiberSecPlot.add_fiber_points(args, color=color, alpha=opacity)
        elif fiber_type == "patch":
            if args[0] in ["quad", "quadr", "quadrilateral"]:
                FiberSecPlot.add_quad(args[1:], color=color, alpha=opacity)
            elif args[0] in ["rect", "rectangular"]:
                FiberSecPlot.add_rect(args[1:], color=color, alpha=opacity)
            elif args[0] in ["circ", "circular"]:
                FiberSecPlot.add_circ(args[1:], color=color, alpha=opacity)
        elif fiber_type == "layer":
            if args[0] == "straight":
                FiberSecPlot.add_straight_layer(args[1:], color=color, alpha=opacity)
            elif args[0] in ["circ", "circular"]:
                FiberSecPlot.add_circ_layer(args[1:], color=color, alpha=opacity)
        return func(*args)

    return wrapper


[docs] def section(*args): """``args`` see `section command <https://openseespydoc.readthedocs.io/en/latest/src/section.html#section>`_""" sec_tag = args[1] FiberSecPlot.set_sec_tag(sec_tag) ops.section(*args)
[docs] @fetch_fiber_plot_data def fiber(*args): """``args`` see `fiber command <https://openseespydoc.readthedocs.io/en/latest/src/fiber.html>`_""" ops.fiber(*args)
[docs] @fetch_fiber_plot_data def patch(*args): """``args`` see `patch command <https://openseespydoc.readthedocs.io/en/latest/src/patch.html>`_""" ops.patch(*args)
[docs] @fetch_fiber_plot_data def layer(*args): """``args`` see `layer command <https://openseespydoc.readthedocs.io/en/latest/src/layer.html>`_""" ops.layer(*args)
[docs] def plot_fiber_sec_cmds( sec_tag: int, title: str = "My Section", label_size: int = 18, tick_size: int = 15, title_size: int = 20, ): """Plot the fiber section by section tag. Parameters ----------- sec_tag : int section tag in the ``OpenSeesPy`` domain. title : str, optional Title of plot, by default "My Section" label_size : int, optional Axis label size, by default 18 tick_size : int, optional Axis tick size, by default 15 title_size : int, optional Title size, by default 20 """ FiberSecPlot.plot_sec( sec_tag, title=title, label_size=label_size, tick_size=tick_size, title_size=title_size, )
def _line_mesh(y1, z1, y2, z2, num): length = np.sqrt((y2 - y1) ** 2 + (z2 - z1) ** 2) delta_l = length / num cos_alpha = np.abs(y2 - y1) / length sin_alpha = np.abs(z2 - z1) / length delta_y = np.sign(y2 - y1) * delta_l * cos_alpha delta_z = np.sign(z2 - z1) * delta_l * sin_alpha nodes = [] for i in range(num + 1): nodes.append((y1 + i * delta_y, z1 + i * delta_z)) return nodes def _arc_mesh(ang0, ang1, r, num_c, yc, zc): ang0 = np.deg2rad(ang0) ang1 = np.deg2rad(ang1) delta_ang = (ang1 - ang0) / num_c nodes = [] for i in range(num_c + 1): nodes.append(( r * np.cos(ang0 + i * delta_ang) + yc, r * np.sin(ang0 + i * delta_ang) + zc, )) return nodes if __name__ == "__main__": import openseespy.opensees as ops import opstool as opst ops.wipe() ops.model("basic", "-ndm", 3, "-ndf", 6) ops.uniaxialMaterial("Elastic", 1, 1000) # The following commands, like those in `OpenSeesPy <https://openseespydoc.readthedocs.io/en/latest/>`_, # create objects in the domain. sectag = 1 opst.pre.section.section("Fiber", sectag, "-GJ", 1.0e6) opst.pre.section.patch("circ", 1, 40, 1, 0, 0, 1.9, 2, 0, 360, color="blue", opacity=0.75) opst.pre.section.patch("circ", 1, 40, 5, 0, 0, 1, 1.9, 0, 360, color="green", opacity=0.35) opst.pre.section.layer("circ", 1, 40, np.pi * 0.016**2, 0, 0, 1.9 - 0.016, 0.0, 360.0, color="red") # plot opst.pre.section.plot_fiber_sec_cmds(sec_tag=1) sectag = 2 opst.pre.section.section("Fiber", sectag, "-GJ", 1.0e6) opst.pre.section.patch("quad", 1, 20, 20, -1, -1, 1, -1, 2, 3, -2, 3, color="blue", opacity=0.25) opst.pre.section.layer("straight", 1, 20, np.pi * 0.02**2, *[-0.9, -0.9], *[1.9, 2.9], color="black") opst.pre.section.fiber(0, 1, np.pi * 0.05**2, 1, color="red") # plot opst.pre.section.plot_fiber_sec_cmds(sec_tag=2)