Damage Monitoring Tools

The damage monitoring toolkit consists the files SectionLib.py and get_fibers.py, and the Python package opensees. Currently these files have to be copied to the location from where they are executed, and the opensees package should be installed by running the following command:

pip install opensees

The toolkit can be used from either Tcl, Python, or directly at the command line.

The following example shows how the get_fibers.py module may be invoked from Tcl:

proc py {args} {
    eval "[exec python.exe {*}$args]"
}

foreach ds {dsr1 dsr2 dsr3} {
    py get_fibers.py model.json record-${ds}.txt -e 4020,3020 -d 60 -s $ds
}

Note: This currently requires the get_fibers.py file to be visible from the current working directory of the Tcl interpreter.

1) Geometry building {#1-geometry-building}

Damage regions are built from the same plane geometry primitives that are offered by OpenSees (e.g. the patch and layer commands).

Additionally, the function section from the module opensees.render.mpl can be used to visualize components.

from opensees import patch, layer, section
import opensees.render.mpl as render
s = section.FiberSection(
    areas = [
        patch.circ(extRad=20, intRad=18),
        layer.line(vertices=[[-14, -10], [14, 10]]),
        patch.rect(vertices=[[-15, -10], [15, 10]])
    ]
)
render.section(s);

Print section properties:

print(s.area)
print(s.ixc)
print(s.iyc)
# print(s.ixc)
838.7610416728243
63215.74854278119
88215.74854278119

Additionally, the SectionLib module provides convenient wrappers for building complex sections.

The Octagon function from this library can be called in 3 ways:

  • ConfinedPolygon(radius) Constructs an octagon.
  • PolygonRing(extRad, intRad, n) Constructs an n-gon annulus
from opensees.section import ConfinedPolygon
render.section(section.ConfinedPolygon(8, 20));

render.section(section.PolygonRing(8, 20, 18));

render.section(section.ConfinedPolygon(8, 20, 20));

2) Define damage state regions; the get_fibers module {#2-define-damage-state-regions-the-get_fibers-module}

from get_fibers import iter_elem_fibers, damage_states

The get_fibers module provides the helper function iter_elem_fibers for iterating over a filtered collection of fibers. Fibers are filtered out by supplying a damage state dictionary with a required "regions" field, and optional "material" field.

from opensees.units.english import ft, inch
Dcol = 7*ft
Rcol = Dcol/2
cover = 2*inch
DS = damage_states(Dcol)
# DS = {
#   # Any outermost cover fiber
#   "dsr1" : {
#       "regions": [
#           section.PolygonRing(8, Rcol, Rcol-1.0)
#       ]
#   },
#   "dsr2" : {
#       "regions": [
#           section.FiberSection(areas=[
#               patch.circ(intRad=Rcol-cover-2, extRad=Rcol-cover)
#           ])
#       ],
#       "material": "*steel*"
#   },
#   "dsr3" : {
#       "regions": [
#       #                          external radius      internal radius
#           section.PolygonRing(8, Rcol-cover*(1-0.75), Rcol-cover*(1-0.5))
#       ],
#       "material": "*concr*"
#   },
# }

3) Iterating over fibers {#3-iterating-over-fibers}

import json
import numpy as np
model_file = "modelDetails.json"
elements = [4010]
with open(model_file, "r") as f:
    model = json.load(f)

If the third argument to iter_elem_fibers is omitted, all fibers are returned (Underscores are used in the following cell to name unused variables).

all_fibers = [
    f["coord"] for _,__,f in iter_elem_fibers(model, elements)
]

ds1_fibers = np.array([
    f["coord"] for e,s,f in iter_elem_fibers(model, elements, filt=DS["dsr1"], sections=[0])
])

ds2_fibers = np.array([
    f["coord"] for e,s,f in iter_elem_fibers(model, elements, filt=DS["dsr2"], sections=[0])
])

ds3_fibers = np.array([
    f["coord"] for e,s,f in iter_elem_fibers(model, elements, filt=DS["dsr3"], sections=[0])
])

ds4_fibers = np.array([
    f["coord"] for e,s,f in iter_elem_fibers(model, elements, filt=DS["dsr3"], sections=[0])
])

3) Visualizing {#3-visualizing}

sect = section.ConfinedPolygon(8, Dcol/2)
ax = render.section(sect)
render.section(DS["dsr1"]["regions"][0], facecolor="b", ax=ax)
render.section(DS["dsr2"]["regions"][0], facecolor="pink", ax=ax)
render.section(DS["dsr3"]["regions"][0], facecolor="y", ax=ax)
render.section(DS["dsr4"]["regions"][0], facecolor="g", ax=ax);
render.section(DS["dsr5"]["regions"][0], facecolor="r", ax=ax);
# ax.set_xlim([0, Rcol+1])
# ax.set_ylim([-1, Rcol+1])

# Create grid of points
import numpy as np

# Create plot axis and add sections to it
ax = render.section(sect)
render.section(DS["dsr1"]["regions"][0], facecolor="b", ax=ax)
render.section(DS["dsr2"]["regions"][0], facecolor="pink", ax=ax)
render.section(DS["dsr3"]["regions"][0], facecolor="y", ax=ax)
render.section(DS["dsr4"]["regions"][0], facecolor="g", ax=ax);
render.section(DS["dsr5"]["regions"][0], facecolor="r", ax=ax);

# add filtered fibers to plot
ax.scatter(*list(zip(*all_fibers)), color="grey", alpha=0.3, s=0.5);
ax.scatter(*list(zip(*ds1_fibers)), marker=".", color="blue")
ax.scatter(*list(zip(*ds2_fibers)), color="r", marker="o")#, s=0.8)
ax.scatter(*list(zip(*ds3_fibers)), color="r", marker="x")#,  s=1)
ax.scatter(*list(zip(*ds4_fibers)), color="r", marker="+")#,  s=1)

# Set plot limits
ax.set_xlim([ 0, Rcol+1])
ax.set_ylim([-1, Rcol+1])
ax.axis("on");

ax = render.section(sect)
ax.scatter(*list(zip(*all_fibers)), color="red", s=0.5);

Fiber Response Animation

fiberStrains.py -a -dsr dsr -sec sec ...

a is analysis type and can be any one of: [po cyclic]
dsr can be any set of: [dsr1 dsr2 dsr3 dsr4 dsr5 dsr6 all]
sec can be any one of: [1 np], where np is integer representing last integration point.

Options
-vmin <float>
-vmax <float>

vmin and vmax are customized colorbar limits, if defaults must be adjusted.
Back to top