import os
import shelve
from typing import Union
import numpy as np
import openseespy.opensees as ops
from numpy.typing import ArrayLike
from ..utils import (ELE_TAG_Beam, ELE_TAG_Brick, ELE_TAG_Joint, ELE_TAG_Link,
ELE_TAG_Plane, ELE_TAG_Tetrahedron, ELE_TAG_Truss,
check_file, shape_dict)
[docs]class GetFEMdata:
"""
Get the data in the openseespy model domain.
Parameters
----------
results_dir: str, default="opstool_output"
The dir that results saved.
"""
def __init__(self, results_dir: str = "opstool_output"):
self.out_dir = results_dir
self.model_info_names = [
"coord_no_deform",
"coord_ele_midpoints",
"bound",
"max_bound",
"num_node",
"num_ele",
"NodeTags",
"EleTags",
"model_dims",
]
self.model_info = dict()
self.get_model_data_finished = False
# Initialize cell connection data
self.cells_names = [
"truss",
"link",
"beam",
"other_line",
"all_lines",
"plane",
"tetrahedron",
"brick",
"all_faces",
]
self.cells = dict()
# Initialize eigenvalue data
self.eigen_names = (
["f", "eigenvector"] + self.model_info_names + self.cells_names
)
self.eigen = None
# Analysis step model update data
self.model_info_steps = dict()
# Ele connection update data
self.cells_steps = dict()
# Update node response data
self.node_resp_names = ("disp", "vel", "accel")
self.node_resp_steps = dict()
self.step_node_track = 0
self.beam_info_names = ['beam_tags', 'beam_node_coords', 'beam_cells', 'beam_cell_map',
'xlocal', 'ylocal', 'zlocal', 'bounds']
self.beam_infos = dict()
self.beam_resp_names = ['localForces', 'basicDeformations']
self.beam_resp_step = dict()
# ["N_1", "Vy_1", "Vz_1", "T_1", "My_1", "Mz_1",
# "N_2", "Vy_2", "Vz_2", "T_2", "My_2", "Mz_2"]
# ["eps", "thetaZ_1", "thetaZ_2", "thetaY_1", "thetaY_2", "thetaX"]
self.step_beam_track = 0
# On/Off and Tracking for Model Updates
self.model_update = False
# self.reset_model_state()
# self.reset_eigen_state()
self.reset_steps_state()
# fiber section data
self.fiber_sec_tags = None
self.fiber_sec_data = dict()
self.fiber_sec_step_data = dict()
self.step_fiber_track = 0
[docs] def reset_model_state(self):
"""Reset the state of results extract of model data."""
for name in self.model_info_names:
self.model_info[name] = None
for name in self.cells_names:
self.cells[name] = None
[docs] def reset_eigen_state(self):
"""Reset the state of results extract of eigen data."""
self.eigen = dict()
for name in self.eigen_names:
self.eigen[name] = None
[docs] def reset_steps_state(self):
"""Reset the state of results extract in analysis step"""
self.step_node_track = 0
self.step_beam_track = 0
self.step_fiber_track = 0
for name in self.model_info_names:
self.model_info_steps[name] = []
for name in self.cells_names:
self.cells_steps[name] = []
for name in self.node_resp_names:
self.node_resp_steps[name] = []
for name in self.beam_info_names:
self.beam_infos[name] = None
for name in self.beam_resp_names:
self.beam_resp_step[name] = []
self.fiber_sec_step_data = dict()
# Truss Element Analysis Step Response Data
# Beam Element Analysis Step Response Data
[docs] def get_model_data(self, save_file: bool = True):
"""Get data from the current model domain. The data will saved to file ``ModelData.dat``.
Parameters
-----------
save_file: bool, default=True
Wether to save model infomation data.
"""
# --------------------------------
node_tags = ops.getNodeTags()
node_tags.sort()
num_node = len(node_tags)
# Get all the ele tags
ele_tags = ops.getEleTags()
ele_tags.sort()
num_ele = len(ele_tags)
# Dict of node coordinates
node_coords = np.zeros((num_node, 3))
node_index = dict() # key: nodeTag, value: index in Node_Coords
model_dims = []
for i, Tag in enumerate(node_tags):
coord = ops.nodeCoord(Tag)
model_dim = len(coord)
if model_dim == 1:
coord.extend([0, 0])
elif model_dim == 2:
coord.extend([0])
model_dims.append(model_dim)
node_coords[i] = np.array(coord)
node_index[Tag] = i
points = node_coords
# lines and faces
# How the ele is connected,node number n-node tag 1-node tag 2-...-node tag n
truss_cells = []
truss_cells_tags = []
link_cells = []
link_cells_tags = []
beam_cells = []
beam_cells_tags = []
beam_midpoints = []
beam_xlocal = []
beam_ylocal = []
beam_zlocal = []
other_line_cells = []
other_line_cells_tags = []
all_lines_cells = []
all_lines_cells_tags = []
plane_cells = []
plane_cells_tags = []
tetrahedron_cells = []
tetrahedron_cells_tags = []
brick_cells = []
brick_cells_tags = []
all_faces_cells = []
all_faces_cells_tags = []
ele_midpoints = [] # the coordinates of the ele's midpoint
for i, ele in enumerate(ele_tags):
ele_nodes = ops.eleNodes(ele)
# Determine the element type based on the number of element nodes
if len(ele_nodes) == 2:
node_i, node_j = ele_nodes
idx_i, idx_j = node_index[node_i], node_index[node_j]
all_lines_cells.append([2, idx_i, idx_j])
all_lines_cells_tags.append(ele)
if ops.getEleClassTags(ele)[0] in ELE_TAG_Truss:
truss_cells.append([2, idx_i, idx_j])
truss_cells_tags.append(ele)
elif ops.getEleClassTags(ele)[0] in ELE_TAG_Link:
link_cells.append([2, idx_i, idx_j])
link_cells_tags.append(ele)
elif ops.getEleClassTags(ele)[0] in ELE_TAG_Beam:
beam_cells.append([2, idx_i, idx_j])
beam_cells_tags.append(ele)
beam_midpoints.append((node_coords[idx_i] + node_coords[idx_j]) / 2)
xlocal = ops.eleResponse(ele, "xlocal")
ylocal = ops.eleResponse(ele, "ylocal")
zlocal = ops.eleResponse(ele, "zlocal")
beam_xlocal.append(np.array(xlocal) / np.linalg.norm(xlocal))
beam_ylocal.append(np.array(ylocal) / np.linalg.norm(ylocal))
beam_zlocal.append(np.array(zlocal) / np.linalg.norm(zlocal))
else:
other_line_cells.append([2, idx_i, idx_j])
other_line_cells_tags.append(ele)
ele_midpoints.append((node_coords[idx_i] + node_coords[idx_j]) / 2)
elif len(ele_nodes) == 3 or len(ele_nodes) == 6:
if len(ele_nodes) == 3:
node_i, node_j, node_k = ops.eleNodes(ele)
elif len(ele_nodes) == 6:
node_i, node_j, node_k = ops.eleNodes(ele)[1], ops.eleNodes(ele)[3], ops.eleNodes(ele)[5]
idx_i, idx_j, idx_k = (
node_index[node_i],
node_index[node_j],
node_index[node_k],
)
all_faces_cells.append([3, idx_i, idx_j, idx_k])
all_faces_cells_tags.append(ele)
plane_cells.append([3, idx_i, idx_j, idx_k])
plane_cells_tags.append(ele)
ele_midpoints.append(
(node_coords[idx_i] + node_coords[idx_j] + node_coords[idx_k]) / 3
)
elif len(ele_nodes) == 4 or len(ele_nodes) == 9:
if len(ele_nodes) == 4:
node_i, node_j, node_k, node_l = ops.eleNodes(ele)
else:
node_i, node_j, node_k, node_l = ops.eleNodes(ele)[0:4]
idx_i, idx_j = node_index[node_i], node_index[node_j]
idx_k, idx_l = node_index[node_k], node_index[node_l]
if ops.getEleClassTags(ele)[0] in ELE_TAG_Tetrahedron: # tetrahedron
tetrahedron_cells.append([3, idx_i, idx_j, idx_k])
tetrahedron_cells.append([3, idx_i, idx_j, idx_l])
tetrahedron_cells.append([3, idx_i, idx_k, idx_l])
tetrahedron_cells.append([3, idx_j, idx_k, idx_l])
tetrahedron_cells_tags.append(ele)
all_faces_cells.append([3, idx_i, idx_j, idx_k])
all_faces_cells.append([3, idx_i, idx_j, idx_l])
all_faces_cells.append([3, idx_i, idx_k, idx_l])
all_faces_cells.append([3, idx_j, idx_k, idx_l])
all_faces_cells_tags.append(ele)
else:
plane_cells.append([4, idx_i, idx_j, idx_k, idx_l])
plane_cells_tags.append(ele)
all_faces_cells.append([4, idx_i, idx_j, idx_k, idx_l])
all_faces_cells_tags.append(ele)
ele_midpoints.append(np.mean(node_coords[[idx_i, idx_j, idx_k, idx_l]], axis=0))
elif len(ele_nodes) == 8 or len(ele_nodes) == 20:
if len(ele_nodes) == 8:
(
node1,
node2,
node3,
node4,
node5,
node6,
node7,
node8,
) = ops.eleNodes(ele)
else:
(
node1,
node2,
node3,
node4,
node5,
node6,
node7,
node8,
) = ops.eleNodes(ele)[0:8]
tag_list = [
node_index[node1],
node_index[node2],
node_index[node3],
node_index[node4],
node_index[node5],
node_index[node6],
node_index[node7],
node_index[node8],
]
temp_points = np.array([node_coords[i] for i in tag_list])
idxxx = np.argsort(temp_points[:, -1])
temp_points = temp_points[idxxx]
tag_list = np.array(tag_list)[idxxx]
temp_points = [tuple(i) for i in temp_points]
tag_list = list(tag_list)
tag_counter1 = counter_clockwise(temp_points[:4], tag_list[:4])
tag_counter2 = counter_clockwise(temp_points[4:], tag_list[4:])
tag_counts = tag_counter1 + tag_counter2
idx1, idx2, idx3, idx4, idx5, idx6, idx7, idx8 = tag_counts
brick_cells.append([4, idx1, idx4, idx3, idx2])
brick_cells.append([4, idx5, idx6, idx7, idx8])
brick_cells.append([4, idx1, idx5, idx8, idx4])
brick_cells.append([4, idx2, idx3, idx7, idx6])
brick_cells.append([4, idx1, idx2, idx6, idx5])
brick_cells.append([4, idx3, idx4, idx8, idx7])
brick_cells_tags.append(ele)
all_faces_cells.append([4, idx1, idx4, idx3, idx2])
all_faces_cells.append([4, idx5, idx6, idx7, idx8])
all_faces_cells.append([4, idx1, idx5, idx8, idx4])
all_faces_cells.append([4, idx2, idx3, idx7, idx6])
all_faces_cells.append([4, idx1, idx2, idx6, idx5])
all_faces_cells.append([4, idx3, idx4, idx8, idx7])
all_faces_cells_tags.append(ele)
idxs1_8 = [idx1, idx2, idx3, idx4, idx5, idx6, idx7, idx8]
ele_midpoints.append(np.mean(node_coords[idxs1_8], axis=0))
ele_midpoints = np.array(ele_midpoints)
beam_midpoints = np.array(beam_midpoints)
beam_xlocal = np.array(beam_xlocal)
beam_ylocal = np.array(beam_ylocal)
beam_zlocal = np.array(beam_zlocal)
# Automatically determine the coordinate axis boundary
# according to the model node coordinates
min_node = np.min(points, axis=0)
max_node = np.max(points, axis=0)
space = (max_node - min_node) / 15
min_node = min_node - 2 * space
max_node = max_node + 2 * space
bounds = [
min_node[0],
max_node[0],
min_node[1],
max_node[1],
min_node[2],
max_node[2],
]
max_bound = np.max(max_node - min_node)
# FEM data, including link for points, lines, surfaces, solids
model_info = dict()
model_info["coord_no_deform"] = points
model_info["coord_ele_midpoints"] = ele_midpoints
model_info["bound"] = bounds
model_info["max_bound"] = max_bound
model_info["num_node"] = num_node
model_info["num_ele"] = num_ele
model_info["NodeTags"] = node_tags
model_info["EleTags"] = ele_tags
model_info["model_dims"] = model_dims
model_info["coord_ele_midpoints"] = ele_midpoints
model_info["beam_midpoints"] = beam_midpoints
model_info["beam_xlocal"] = beam_xlocal
model_info["beam_ylocal"] = beam_ylocal
model_info["beam_zlocal"] = beam_zlocal
cells = dict()
cells["all_lines"] = all_lines_cells
cells['all_lines_tags'] = all_lines_cells_tags
cells["all_faces"] = all_faces_cells
cells["all_faces_tags"] = all_faces_cells_tags
cells["plane"] = plane_cells
cells["plane_tags"] = plane_cells_tags
cells["tetrahedron"] = tetrahedron_cells
cells["tetrahedron_tags"] = tetrahedron_cells_tags
cells["brick"] = brick_cells
cells["brick_tags"] = brick_cells_tags
cells["truss"] = truss_cells
cells["truss_tags"] = truss_cells_tags
cells["link"] = link_cells
cells["link_tags"] = link_cells_tags
cells["beam"] = beam_cells
cells["beam_tags"] = beam_cells_tags
cells["other_line"] = other_line_cells
cells["other_line_tags"] = other_line_cells_tags
self.model_info.update(model_info)
self.cells.update(cells)
self.get_model_data_finished = True
if save_file:
if not os.path.exists(self.out_dir):
os.makedirs(self.out_dir)
output_filename = self.out_dir + '/ModelData'
with shelve.open(output_filename) as db:
db["ModelInfo"] = self.model_info
db["Cell"] = self.cells
print(f"Model data saved in {output_filename}!")
# if output_file:
# with h5py.File(output_file, "w") as f:
# grp1 = f.create_group("ModelInfo")
# for name in self.model_info_names:
# grp1.create_dataset(name, data=self.model_info[name])
# grp2 = f.create_group("Cell")
# for name in self.cells_names:
# grp2.create_dataset(name, data=self.cells[name])
[docs] def get_eigen_data(
self,
mode_tag: int = 1,
solver: str = "-genBandArpack",
):
"""Get eigenvalue Analysis Data. The data will saved to file ``EigenData.dat``.
Parameters
----------
mode_tag: int
mode tag.
solver: str, default '-genBandArpack'
type of solver, optional '-genBandArpack', '-fullGenLapack',
see https://openseespydoc.readthedocs.io/en/latest/src/eigen.html.
Returns
-------
None
"""
# ----------------------------------
if not self.get_model_data_finished:
self.get_model_data()
self.reset_eigen_state()
# ----------------------------------
ops.wipeAnalysis()
if mode_tag == 1:
eigen_values = ops.eigen(solver, 2)[:1]
else:
eigen_values = ops.eigen(solver, mode_tag)
omega = np.sqrt(eigen_values)
f = omega / (2 * np.pi)
self.eigen["f"] = f
eigenvectors = []
for mode_tag in range(1, mode_tag + 1):
# ------------------------------------------
eigen_vector = np.zeros((self.model_info["num_node"], 3))
for i, Tag in enumerate(self.model_info["NodeTags"]):
coord = ops.nodeCoord(Tag)
eigen = ops.nodeEigenvector(Tag, mode_tag)
if len(coord) == 1:
coord.extend([0, 0])
eigen.extend([0, 0])
elif len(coord) == 2:
coord.extend([0])
if len(eigen) == 3 or len(eigen) == 2:
eigen = eigen[:2]
eigen.extend([0])
elif len(eigen) == 1:
eigen.extend([0, 0])
else:
eigen = eigen[:3]
eigen_vector[i] = np.array(eigen)
eigenvectors.append(eigen_vector)
self.eigen["eigenvector"] = eigenvectors
self.eigen.update(self.model_info)
self.eigen.update(self.cells)
# ----------------------------------------------------------------
if not os.path.exists(self.out_dir):
os.makedirs(self.out_dir)
output_filename = self.out_dir + '/EigenData'
with shelve.open(output_filename) as db:
db["EigenInfo"] = self.eigen
print(f"Eigen data saved in {output_filename}!")
[docs] def get_node_resp_step(self, analysis_tag: int, num_steps: int = 10000000000,
total_time: float = 10000000000, model_update: bool = False):
"""Get the response data step by step. The data will saved to file ``NodeRespStepData-{analysis_tag}.dat``.
.. tip::
You need to call this function at each analysis step in OpenSees.
The advantage is that you can modify the iterative algorithm at each analysis step to facilitate convergence.
Parameters
----------
analysis_tag: int
Analysis tag used to assign the analysis data.
num_steps: int, default=10000000000
Total number of steps, set to determine when to save data.
total_time: float, default=10000000000
Total analysis time, set to determine when to save data.
You can specify one of the parameters *num_steps* and `total_time`.
If both are used, it depends on which one arrives first.
model_update: bool, default False
whether to update the model domain data at each analysis step,
this will be useful if model data has changed.
Returns
-------
None
Note
-----
You need to call this function at each analysis step in OpenSees.
The advantage is that you can modify the iterative algorithm at each analysis step to facilitate convergence.
"""
if model_update:
self.get_model_data(save_file=False)
else:
if not self.get_model_data_finished:
self.get_model_data()
num_node = self.model_info["num_node"]
# num_ele = self.num_ele
node_tags = self.model_info["NodeTags"]
# EleTags = self.EleTags
# Used to store the response data of each node at each time step,
# the index is time step, node, coordinate dimension
node_disp = np.zeros((num_node, 3))
node_vel = np.zeros((num_node, 3))
node_accel = np.zeros((num_node, 3))
# Used to store the deformed coordinate data of each node at each time step,
# the index is time step, node, coordinate dimension
node_deform_coord = np.zeros((num_node, 3))
for i, Tag in enumerate(node_tags):
coord = ops.nodeCoord(Tag)
disp = ops.nodeDisp(Tag)
vel = ops.nodeVel(Tag)
accel = ops.nodeAccel(Tag)
if len(coord) == 1:
coord.extend([0, 0])
disp.extend([0, 0])
vel.extend([0, 0])
accel.extend([0, 0])
elif len(coord) == 2:
if len(disp) in [2, 3]:
coord.extend([0])
disp = disp[:2]
disp.extend([0])
vel = vel[:2]
vel.extend([0])
accel = accel[:2]
accel.extend([0])
else:
coord.extend([0])
disp = disp[:2]
disp.extend([0, 0])
vel = vel[:2]
vel.extend([0, 0])
accel = accel[:2]
accel.extend([0, 0])
else:
disp = disp[:3] # ignore the rotation
vel = vel[:3]
accel = accel[:3]
node_disp[i] = disp
node_vel[i] = vel
node_accel[i] = accel
node_deform_coord[i] = [disp[ii] + coord[ii] for ii in range(3)]
self.node_resp_steps["disp"].append(node_disp)
self.node_resp_steps["vel"].append(node_vel)
self.node_resp_steps["accel"].append(node_accel)
self.model_update = model_update
if model_update:
for name in self.model_info_names:
self.model_info_steps[name].append(self.model_info[name])
for name in self.cells_names:
self.cells_steps[name].append(self.cells[name])
else:
if self.step_node_track == 0:
self.model_info_steps.update(self.model_info)
self.cells_steps.update(self.cells)
# ----------------------------------------------------------------
self.step_node_track += 1
if self.step_node_track >= num_steps or ops.getTime() >= total_time:
if not os.path.exists(self.out_dir):
os.makedirs(self.out_dir)
output_filename = self.out_dir + f'/NodeRespStepData-{analysis_tag}'
with shelve.open(output_filename) as db:
db["ModelInfoSteps"] = self.model_info_steps
db["CellSteps"] = self.cells_steps
db["NodeRespSteps"] = self.node_resp_steps
print(f"Node response data saved in {output_filename}!")
[docs] def get_frame_resp_step(self, analysis_tag: int,
num_steps: int = 10000000000,
total_time: float = 10000000000):
"""Get the response data step by step. The data will saved to file ``BeamRespStepData-{analysis_tag}.dat``.
Parameters
----------
analysis_tag: int
Analysis tag used to assign the analysis data.
num_steps: int, default=10000000000
Total number of steps, set to determine when to save data.
total_time: float, default=10000000000
Total analysis time, set to determine when to save data.
You can specify one of the parameters *num_steps* and `total_time`.
If both are used, it depends on which one arrives first.
Returns
-------
None
Note
----
You need to call this function at each analysis step in OpenSees.
The advantage is that you can modify the iterative algorithm at each analysis step to facilitate convergence.
"""
ele_tags = ops.getEleTags()
ele_tags.sort()
beam_tags = []
beam_node_tags = []
beam_node_map = dict()
for i, eletag in enumerate(ele_tags):
if ops.getEleClassTags(eletag)[0] in ELE_TAG_Beam:
beam_tags.append(eletag)
ele_nodes = ops.eleNodes(eletag)
if ele_nodes[0] not in beam_node_tags:
beam_node_tags.append(ele_nodes[0])
if ele_nodes[1] not in beam_node_tags:
beam_node_tags.append(ele_nodes[1])
beam_node_map[eletag] = ele_nodes
beam_node_coords = []
node_coord_map = dict()
for i, nodetag in enumerate(beam_node_tags):
coord = ops.nodeCoord(nodetag)
model_dim = len(coord)
if model_dim == 1:
coord.extend([0, 0])
elif model_dim == 2:
coord.extend([0])
beam_node_coords.append(coord)
node_coord_map[nodetag] = i
beam_node_coords = np.array(beam_node_coords)
beam_cells = []
beam_cell_map = dict()
xlocal_map = dict()
ylocal_map = dict()
zlocal_map = dict()
for i, eletag in enumerate(beam_tags):
node1, node2 = beam_node_map[eletag]
idx_i, idx_j = node_coord_map[node1], node_coord_map[node2]
beam_cells.append([2, idx_i, idx_j])
beam_cell_map[eletag] = i
xlocal = ops.eleResponse(eletag, "xlocal")
ylocal = ops.eleResponse(eletag, "ylocal")
zlocal = ops.eleResponse(eletag, "zlocal")
xlocal_map[eletag] = np.array(xlocal) / np.linalg.norm(xlocal)
ylocal_map[eletag] = np.array(ylocal) / np.linalg.norm(ylocal)
zlocal_map[eletag] = np.array(zlocal) / np.linalg.norm(zlocal)
beam_cells = np.array(beam_cells)
bounds = np.array(ops.nodeBounds())
self.beam_infos['beam_tags'] = beam_tags
self.beam_infos['beam_node_coords'] = beam_node_coords
self.beam_infos['beam_cells'] = beam_cells
self.beam_infos['beam_cell_map'] = beam_cell_map
self.beam_infos['xlocal'] = xlocal_map
self.beam_infos['ylocal'] = ylocal_map
self.beam_infos['zlocal'] = zlocal_map
self.beam_infos['bounds'] = bounds
beam_resp_steps = []
for eletag in beam_tags:
local_forces = ops.eleResponse(eletag, "localForce")
if len(local_forces) == 6:
local_forces = [local_forces[0], local_forces[1], 0, 0, 0, local_forces[2],
local_forces[3], local_forces[4], 0, 0, 0, local_forces[5]]
# for ii in range(6):
# local_forces[ii] = -local_forces[ii]
beam_resp_steps.append(local_forces)
beam_resp_steps = np.array(beam_resp_steps)
self.beam_resp_step['localForces'].append(beam_resp_steps)
# ----------------------------------------------------------------
self.step_beam_track += 1
if self.step_beam_track >= num_steps or ops.getTime() >= total_time:
if not os.path.exists(self.out_dir):
os.makedirs(self.out_dir)
output_filename = self.out_dir + f'/BeamRespStepData-{analysis_tag}'
with shelve.open(output_filename) as db:
db["BeamInfos"] = self.beam_infos
db["BeamRespSteps"] = self.beam_resp_step
print(f"Frame elements responses data saved in {output_filename}!")
[docs] def get_fiber_data(self, ele_sec: list[tuple[int, int]]):
"""Get data from the section assigned by parameter ele_sec.
The data will saved to file ``FiberData.dat``.
Parameters
----------
ele_sec: list[tuple[int, int]]
A list or tuple composed of element tag and sectag.
e.g., [(ele1, sec1), (ele2, sec2), ... , (elen, secn)],
The section is attached to an element in the order from end i to end j of that element.
Returns
-------
None
"""
self.fiber_sec_tags = ele_sec
for ele_sec_i in ele_sec:
key = f"{ele_sec_i[0]}-{ele_sec_i[1]}"
self.fiber_sec_data[key] = None
# get data
for ele_sec_i in self.fiber_sec_tags:
ele_tag = ele_sec_i[0]
sec_tag = ele_sec_i[1]
key = f"{ele_sec_i[0]}-{ele_sec_i[1]}"
fiber_data = _get_fiber_sec_data(ele_tag, sec_tag)
self.fiber_sec_data[key] = fiber_data
if not os.path.exists(self.out_dir):
os.makedirs(self.out_dir)
output_filename = self.out_dir + '/FiberData'
with shelve.open(output_filename) as db:
db["Fiber"] = self.fiber_sec_data
print(f"Fiber section data saved in {output_filename}!")
[docs] def get_fiber_resp_step(self, analysis_tag: int,
num_steps: int = 10000000000,
total_time: float = 10000000000):
"""Get analysis step data for fiber section.
The data will saved to file ``FiberRespStepData-{analysis_tag}.dat``.
Parameters
----------
analysis_tag: int
num_steps: int, default=10000000000
Total number of steps, set to determine when to save data.
total_time: float, default=10000000000
Total analysis time, set to determine when to save data.
You can specify one of the parameters *num_steps* and `total_time`.
If both are used, it depends on which one arrives first.
Returns
-------
None
"""
if not self.fiber_sec_data:
raise ValueError(
"Run get_fiber_step_data must run get_fiber_data() in advance!"
)
if self.step_fiber_track == 0:
for ele_sec_i in self.fiber_sec_tags:
key = f"{ele_sec_i[0]}-{ele_sec_i[1]}"
self.fiber_sec_step_data[key] = []
for ele_sec_i in self.fiber_sec_tags:
ele_tag = ele_sec_i[0]
sec_tag = ele_sec_i[1]
key = f"{ele_sec_i[0]}-{ele_sec_i[1]}"
fiber_data = _get_fiber_sec_data(ele_tag, sec_tag)
defo_fo = ops.eleResponse(ele_tag, "section", sec_tag, "forceAndDeformation")
if len(defo_fo) == 6:
defo_fo = [defo_fo[0], defo_fo[1], 0., defo_fo[2],
defo_fo[3], defo_fo[4], 0., defo_fo[5]]
sec_defo_fo = np.array([defo_fo] * fiber_data.shape[0], dtype=float)
data = np.hstack([fiber_data, sec_defo_fo])
self.fiber_sec_step_data[key].append(data)
# ----------------------------------------------------------------
self.step_fiber_track += 1
if self.step_fiber_track >= num_steps or ops.getTime() >= total_time:
if not os.path.exists(self.out_dir):
os.makedirs(self.out_dir)
output_filename = self.out_dir + f'/FiberRespStepData-{analysis_tag}'
with shelve.open(output_filename) as db:
db["FiberRespSteps"] = self.fiber_sec_step_data
print(f"Fiber section responses data saved in {output_filename}!")
def _get_fiber_sec_data(ele_tag: int, sec_tag: int = 1) -> ArrayLike:
"""Get the fiber sec data for a beam element.
Parameters
----------
ele_tag: int
The element tag to which the section is to be displayed.
sec_tag: int
Which integration point section is displayed, tag from 1 from segment i to j.
Returns
-------
fiber_data: ArrayLike
"""
# Extract fiber data using eleResponse() command
fiber_data = ops.eleResponse(ele_tag, "section", sec_tag, "fiberData2")
if len(fiber_data) == 0:
fiber_data = ops.eleResponse(ele_tag, "section", "fiberData2")
# From column 1 to 6: "yCoord", "zCoord", "area", 'mat', "stress", "strain"
fiber_data = np.array(fiber_data).reshape((-1, 6)) # 变形为6列数组
return fiber_data
def sort_xy(x, y):
"""
Sort points counterclockwise
"""
x0 = np.mean(x)
y0 = np.mean(y)
r = np.sqrt((x - x0) ** 2 + (y - y0) ** 2)
angles = np.where(
(y - y0) >= 0, np.arccos((x - x0) / r), 4 * np.pi - np.arccos((x - x0) / r)
)
mask = np.argsort(angles)
x_max = np.max(x)
if x[mask][0] != x_max:
mask = np.roll(mask, 1)
return mask
def counter_clockwise(points, tag):
"""
Used to sort the points on a face counterclockwise
"""
x = np.array([point[0] for point in points])
y = np.array([point[1] for point in points])
z = np.array([point[2] for point in points])
if all(np.abs(x - x[0]) < 1e-6): # yz
# def algo(point):
# return (math.atan2(point[2] - z_center, point[1] - y_center) + 2 * math.pi) % (2*math.pi)
# sorted_points = sorted(points,key = algo )
index = sort_xy(y, z)
sorted_points = list(zip(x[index], y[index], z[index]))
sorted_id = [points.index(i) for i in sorted_points]
sorted_tag = [tag[i] for i in sorted_id]
elif all(np.abs(y - y[0]) < 1e-6): # xz
# def algo(point):
# return (math.atan2(point[2] - z_center, point[0] - x_center) + 2 * math.pi) % (2*math.pi)
# sorted_points = sorted(points,key = algo )
index = sort_xy(x, z)
sorted_points = list(zip(x[index], y[index], z[index]))
sorted_id = [points.index(i) for i in sorted_points]
sorted_tag = [tag[i] for i in sorted_id]
else:
# def algo(point):
# return (math.atan2(point[1] - y_center, point[0] - x_center) + 2 * math.pi) % (2*math.pi)
# sorted_points = sorted(points,key = algo )
index = sort_xy(x, y)
sorted_points = list(zip(x[index], y[index], z[index]))
sorted_id = [points.index(i) for i in sorted_points]
sorted_tag = [tag[i] for i in sorted_id]
return sorted_tag
def _lines_angle(v1, v2):
# return np.arctan2(np.linalg.norm(np.cross(v1, v2)), np.dot(v1, v2))
x = np.array(v1)
y = np.array(v2)
l_x = np.sqrt(x.dot(x))
l_y = np.sqrt(y.dot(y))
cos_ = x.dot(y) / (l_x * l_y)
angle_r = np.arccos(cos_)
return angle_r