Last active
February 4, 2020 05:43
-
-
Save terjehaukaas/de05752d02b5518e9ae22192925648da to your computer and use it in GitHub Desktop.
Thin-walled Cross-section Analysis
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ------------------------------------------------------------------------ | |
# The following Python code is implemented by Professor Terje Haukaas at | |
# the University of British Columbia in Vancouver, Canada. It is made | |
# freely available online at terje.civil.ubc.ca together with notes, | |
# examples, and additional Python code. Please be cautious when using | |
# this code; it may contain bugs and comes without warranty of any form. | |
# ------------------------------------------------------------------------ | |
import numpy as np | |
import matplotlib.pyplot as plt | |
# ------------------------------------------------------------------------ | |
# INPUT (The last one is used by the code) | |
# ------------------------------------------------------------------------ | |
# ------------------------------------------------------------------------ | |
# S-SHAPE | |
t = 10.0 | |
b = 200.0 | |
h = 200.0 | |
vertices = np.array([(-b, 0), | |
(0, 0), | |
(0, h), | |
(b, h)]) | |
edges = np.array([(1, 2, t), | |
(2, 3, t), | |
(3, 4, t)]) | |
# ------------------------------------------------------------------------ | |
# ONE CELL | |
b = 500.0 | |
h = 200.0 | |
ttop = 10.0 | |
tbottom = 10.0 | |
tleft = 15.0 | |
tright = 20.0 | |
vertices = np.array([(0.0, 0.0), | |
(b, 0.0), | |
(0.0, h), | |
(b, h)]) | |
edges = np.array([(1, 2, tbottom), | |
(3, 4, ttop), | |
(1, 3, tleft), | |
(2, 4, tright)]) | |
# ------------------------------------------------------------------------ | |
# LAMBDA-SHAPE | |
t = 10.0 | |
b = 100.0 | |
h = 200.0 | |
vertices = np.array([(0, 0), | |
(0, h), | |
(b, h)]) | |
edges = np.array([(1, 2, t), | |
(2, 3, t)]) | |
# ------------------------------------------------------------------------ | |
# CELL WITH FLANGES | |
b = 200.0 | |
h = 150.0 | |
a = 80.0 | |
t = 10.0 | |
vertices = np.array([(-a, h), | |
(0, h), | |
(b, h), | |
(b+a, h), | |
(0, 0), | |
(b, 0)]) | |
edges = np.array([(1, 2, t), | |
(2, 3, t), | |
(3, 4, t), | |
(5, 2, t), | |
(6, 3, t), | |
(5, 6, t)]) | |
# ------------------------------------------------------------------------ | |
# I-BEAM | |
ttop = 15.0 | |
tbottom = 15.0 | |
tweb = 15.0 | |
btop = 100.0 | |
bbottom = 150.0 | |
h = 250.0 | |
vertices = np.array([(-btop/2, h), | |
(0.0, h), | |
(btop/2, h), | |
(-bbottom/2, 0.0), | |
(0.0, 0.0), | |
(bbottom/2, 0.0)]) | |
edges = np.array([(1, 2, ttop), | |
(2, 3, ttop), | |
(2, 5, tweb), | |
(4, 5, tbottom), | |
(5, 6, tbottom)]) | |
# ------------------------------------------------------------------------ | |
# L-SHAPE | |
t = 10.0 | |
b = 100.0 | |
h = 200.0 | |
vertices = np.array([(0, 0), | |
(0, h), | |
(b, 0)]) | |
edges = np.array([(1, 2, t), | |
(1, 3, t)]) | |
# ------------------------------------------------------------------------ | |
# CHANNEL PROFILE | |
t = 20.0 | |
b = 80.0 | |
h = 200.0 | |
vertices = np.array([(b, 0), | |
(0, 0), | |
(0, h), | |
(b, h)]) | |
edges = np.array([(1, 2, t), | |
(2, 3, t), | |
(3, 4, t)]) | |
# ------------------------------------------------------------------------ | |
# DEFINE FUNCTION THAT SEARCHES THE OMEGA DIAGRAM | |
# ------------------------------------------------------------------------ | |
def getOmegaValue(diagram, node): | |
for i in range(len(diagram)): | |
index = 0 | |
value = 0.0 | |
if int((diagram[i])[0]) == int(node): | |
return int(node), (diagram[i])[1] | |
return index, value | |
# ------------------------------------------------------------------------ | |
# IDENTIFY CELLS AND FLANGES | |
# ------------------------------------------------------------------------ | |
# Initialize lists of paths and cells in the cross-section | |
pathList = [[int(edges[0, 0]), int(edges[0, 1])]] | |
cellList = [] | |
# Keep walking around in the cross-section, until all paths are covered | |
stillPathsToWalk = True | |
while stillPathsToWalk: | |
foundMatch = False | |
# Loop over all paths that have been walked | |
for i in range(len(pathList)): | |
# Extract path | |
currentPath = pathList[i].copy() | |
# Leave cells alone | |
isCell = False | |
for k in range(1, len(currentPath)+1): | |
num = currentPath.count(k) | |
if num > 1: | |
isCell = True | |
if not isCell: | |
# Initialize before looping over edges | |
leftCandidate = [] | |
rightCandidate = [] | |
# Loop over all edges and try to append the current path | |
for j in range(edges.shape[0]): | |
# Length of the current path | |
lengthOfCurrentPath = len(currentPath) | |
# Get the left-most items in the path | |
leftEnd = currentPath[0] | |
nextToLeftEnd = currentPath[1] | |
# Get the right-most items in the path | |
rightEnd = currentPath[lengthOfCurrentPath - 1] | |
nextToRightEnd = currentPath[lengthOfCurrentPath - 2] | |
# Detect link on the left-hand side, from first column of edges | |
if int(edges[j, 0]) == leftEnd: | |
# Make sure it is not the same edge | |
if int(edges[j, 1]) != nextToLeftEnd: | |
leftCandidate.append(int(edges[j, 1])) | |
foundMatch = True | |
# Is it a cell? | |
if int(edges[j, 1]) in currentPath: | |
cellCandidate = currentPath.copy() | |
cellCandidate.insert(0, int(edges[j, 1])) | |
for k in range(len(cellCandidate)): | |
if cellCandidate[0] != int(edges[j, 1]): | |
cellCandidate.pop(0) | |
cellList.append(cellCandidate) | |
# Detect link on the left-hand side, from second column of edges | |
if int(edges[j, 1]) == leftEnd: | |
# Make sure it is not the same edge | |
if int(edges[j, 0]) != nextToLeftEnd: | |
leftCandidate.append(int(edges[j, 0])) | |
foundMatch = True | |
# Is it a cell? | |
if int(edges[j, 0]) in currentPath: | |
cellCandidate = currentPath.copy() | |
cellCandidate.insert(0, int(edges[j, 0])) | |
for k in range(len(cellCandidate)): | |
if cellCandidate[0] != int(edges[j, 0]): | |
cellCandidate.pop(0) | |
cellList.append(cellCandidate) | |
# Detect link on the right-hand side, from first column of edges | |
if int(edges[j, 0]) == rightEnd: | |
# Make sure it is not the same edge | |
if int(edges[j, 1]) != nextToRightEnd: | |
rightCandidate.append(int(edges[j, 1])) | |
foundMatch = True | |
# Is it a cell? | |
if int(edges[j, 1]) in currentPath: | |
cellCandidate = currentPath.copy() | |
cellCandidate.append(int(edges[j, 1])) | |
for k in range(len(cellCandidate)): | |
if cellCandidate[0] != int(edges[j, 1]): | |
cellCandidate.pop(0) | |
cellList.append(cellCandidate) | |
# Detect link on the right-hand side, from second column of edges | |
if int(edges[j, 1]) == rightEnd: | |
# Make sure it is not the same edge | |
if int(edges[j, 0]) != nextToRightEnd: | |
rightCandidate.append(int(edges[j, 0])) | |
foundMatch = True | |
# Is it a cell? | |
if int(edges[j, 0]) in currentPath: | |
cellCandidate = currentPath.copy() | |
cellCandidate.append(int(edges[j, 0])) | |
for k in range(len(cellCandidate)): | |
if cellCandidate[0] != int(edges[j, 0]): | |
cellCandidate.pop(0) | |
cellList.append(cellCandidate) | |
# See if we have any candidates for appending/branching the current path | |
numLeftCandidates = len(leftCandidate) | |
numRightCandidates = len(rightCandidate) | |
if numLeftCandidates == 1: | |
currentPath.insert(0, leftCandidate[0]) | |
elif numLeftCandidates > 1: | |
for k in range(1, numLeftCandidates): | |
newPath = currentPath.copy() | |
newPath.insert(0, leftCandidate[0]) | |
currentPath.insert(0, leftCandidate[0]) | |
# Insert the new path into the path list | |
pathList.append(newPath) | |
if numRightCandidates == 1: | |
currentPath.append(rightCandidate[0]) | |
elif numRightCandidates > 1: | |
for k in range(1, numRightCandidates): | |
newPath = currentPath.copy() | |
newPath.append(rightCandidate[k]) | |
currentPath.append(rightCandidate[0]) | |
# Insert the new path into the path list | |
pathList.append(newPath) | |
# Put the current path back into the path list | |
pathList[i] = currentPath | |
# Stop if there were no links anywhere | |
if not foundMatch: | |
stillPathsToWalk = False | |
# Identify all flanges, i.e., segments not part of cells | |
else: | |
flangeList = [] | |
for i in range(len(pathList)): | |
currentList = pathList[i] | |
for j in range(len(pathList[i])-1): | |
if len(cellList) == 0: | |
flangeList.append([currentList[j], currentList[j + 1]]) | |
else: | |
segmentAppearsInCell = False | |
for k in range(len(cellList)): | |
if currentList[j] in cellList[k] and currentList[j+1] in cellList[k]: | |
segmentAppearsInCell = True | |
if not segmentAppearsInCell: | |
flangeList.append([currentList[j], currentList[j+1]]) | |
# Create list of duplicate flanges | |
flangesThatShouldBeRemoved = [] | |
for i in range(len(flangeList)): | |
list1 = sorted(flangeList[i]) | |
for j in range(i+1, len(flangeList)): | |
list2 = sorted(flangeList[j]) | |
if list1 == list2: | |
flangesThatShouldBeRemoved.append(j) | |
# Remove the duplicate flanges | |
flangesThatShouldBeRemoved = np.unique(flangesThatShouldBeRemoved) | |
uniqueFlanges = [] | |
for i in range(len(flangeList)): | |
if i not in flangesThatShouldBeRemoved: | |
uniqueFlanges.append(flangeList[i]) | |
# Create list of duplicate cells | |
cellsThatShouldBeRemoved = [] | |
for i in range(len(cellList)): | |
list1 = np.unique(cellList[i]) | |
for j in range(i+1, len(cellList)): | |
list2 = np.unique(cellList[j]) | |
if np.array_equal(list1, list2): | |
cellsThatShouldBeRemoved.append(j) | |
# Remove the duplicate cells | |
cellsThatShouldBeRemoved = np.unique(cellsThatShouldBeRemoved) | |
uniqueCells = [] | |
for i in range(len(cellList)): | |
if i not in cellsThatShouldBeRemoved: | |
uniqueCells.append(cellList[i]) | |
# Determine max-coordinates of the cells | |
yMaxPerCell = [] | |
zMaxPerCell = [] | |
yMinPerCell = [] | |
zMinPerCell = [] | |
for i in range(len(uniqueCells)): | |
currentCell = uniqueCells[i] | |
yValues = [] | |
zValues = [] | |
for j in range(len(currentCell)): | |
yValues.append(vertices[int(currentCell[j])-1, 0]) | |
zValues.append(vertices[int(currentCell[j])-1, 1]) | |
yMaxPerCell.append(np.max(yValues)) | |
zMaxPerCell.append(np.max(zValues)) | |
yMinPerCell.append(np.min(yValues)) | |
zMinPerCell.append(np.min(zValues)) | |
# Create list of cells that encompass other cells | |
cellsThatShouldBeRemoved = [] | |
for i in range(len(uniqueCells)): | |
for j in range(i+1, len(uniqueCells)): | |
if yMaxPerCell[j] >= yMaxPerCell[i] and yMinPerCell[j] <= yMinPerCell[i] and \ | |
zMaxPerCell[j] >= zMaxPerCell[i] and zMinPerCell[j] <= zMinPerCell[i]: | |
cellsThatShouldBeRemoved.append(j) | |
# Remove the duplicate cells | |
cellsThatShouldBeRemoved = np.unique(cellsThatShouldBeRemoved) | |
finalCells = [] | |
for i in range(len(uniqueCells)): | |
if i not in cellsThatShouldBeRemoved: | |
finalCells.append(uniqueCells[i]) | |
# Output | |
print('\n'"Paths:", pathList) | |
print('\n'"Cells:", finalCells) | |
print('\n'"Flanges:", uniqueFlanges) | |
# ------------------------------------------------------------------------ | |
# AREA AND CENTROID | |
# ------------------------------------------------------------------------ | |
A = 0 | |
yA = 0 | |
zA = 0 | |
areas = [] | |
centroids = [] | |
thetas = [] | |
lengths = [] | |
thicknesses = [] | |
for i in range(len(edges)): | |
# Coordinates of end 1 | |
end1 = edges[i, 0] | |
y1 = vertices[int(end1)-1, 0] | |
z1 = vertices[int(end1)-1, 1] | |
# Coordinates of end 2 | |
end2 = edges[i, 1] | |
y2 = vertices[int(end2)-1, 0] | |
z2 = vertices[int(end2)-1, 1] | |
# Midpoint coordinates | |
yMidpoint = 0.5 * (y1 + y2) | |
zMidpoint = 0.5 * (z1 + z2) | |
centroids.append([yMidpoint, zMidpoint]) | |
# Length of segment | |
delta_y = y2-y1 | |
delta_z = z2-z1 | |
L = np.sqrt(delta_y**2 + delta_z**2) | |
lengths.append(L) | |
# Angle relative to y-axis | |
if delta_y != 0: | |
theta = np.arctan(delta_z / delta_y) | |
else: | |
theta = 0.5 * np.pi | |
thetas.append(theta) | |
# Area of segment | |
thickness = edges[i, 2] | |
area = L * thickness | |
areas.append(area) | |
thicknesses.append(thickness) | |
# Accumulate total area | |
A += area | |
# Accumulate A * y and A * z | |
yA += yMidpoint * area | |
zA += zMidpoint * area | |
# Centroid location for the cross-section | |
yCentroid = yA/A | |
zCentroid = zA/A | |
# ------------------------------------------------------------------------ | |
# MOMENTS OF INERTIA and PRINCIPAL AXES ORIENTATION | |
# ------------------------------------------------------------------------ | |
Iy = 0 | |
Iz = 0 | |
Iyz = 0 | |
for i in range(len(edges)): | |
# Local moments of inertia | |
Iyo = lengths[i]**3 * thicknesses[i] * np.sin(thetas[i])**2 / 12.0 | |
Izo = lengths[i]**3 * thicknesses[i] * np.cos(thetas[i])**2 / 12.0 | |
Iyzo = lengths[i]**3 * thicknesses[i] * np.cos(thetas[i]) * np.sin(thetas[i]) / 12.0 | |
# Add contributions from the parallel axis theorem (Steiner's sats) | |
Iy += Iyo + ((centroids[i])[1] - zCentroid)** 2 * areas[i] | |
Iz += Izo + ((centroids[i])[0] - yCentroid)** 2 * areas[i] | |
Iyz += Iyzo + ((centroids[i])[0] - yCentroid) * ((centroids[i])[1] - zCentroid) * areas[i] | |
# Apply caution with the principal axes rotation: what if Iy=Iz | |
thetaPrincipal = 0.0 | |
if np.abs(Iyz) < 1e-10: | |
thetaPrincipal = 0.0 | |
elif np.abs(Iy-Iz) < 1e-10: | |
if np.abs(Iyz) < 1e-10: | |
thetaPrincipal = 0.0 | |
else: | |
thetaPrincipal = np.sign(Iyz) * 0.25 * np.pi | |
else: | |
thetaPrincipal = 0.5 * np.arctan(2.0*Iyz/(Iy-Iz)) | |
# Let counter-clockwise rotation of the system be positive | |
thetaPrincipal *= -1 | |
# Transform the moments of inertia | |
IyRotated = 0.5*(Iy+Iz) + 0.5*np.sqrt((Iz-Iy)**2 + 4.0* Iyz**2) | |
IzRotated = 0.5*(Iy+Iz) - 0.5*np.sqrt((Iz-Iy)**2 + 4.0* Iyz**2) | |
# ------------------------------------------------------------------------ | |
# SAINT VENANT CONSTANT J | |
# ------------------------------------------------------------------------ | |
J = 0 | |
cellArea = 0 | |
if len(finalCells) > 1: | |
print('\n'"Multi-cell cross-sections not considered in St. Venant torsion calculations") | |
# Open cross-section | |
elif len(finalCells) == 0: | |
for i in range(len(edges)): | |
# Pick up contributions to J | |
J += lengths[i] * thicknesses[i]**3 / 3.0 | |
# Single-cell cross-sections | |
elif len(finalCells) == 1: | |
# Loop over edges to calculate the area | |
circleIntegral = 0 | |
for i in range(len(finalCells[0])-1): | |
# Get the two points that are connected to this edge | |
vertex1 = (finalCells[0])[i] | |
vertex2 = (finalCells[0])[i+1] | |
# Calculate the length of this edge | |
y1 = vertices[vertex1-1, 0] | |
z1 = vertices[vertex1-1, 1] | |
y2 = vertices[vertex2-1, 0] | |
z2 = vertices[vertex2-1, 1] | |
delta_y = y2 - y1 | |
delta_z = z2 - z1 | |
length = np.sqrt( delta_y**2 + delta_z**2) | |
# Get the thickness of this edge | |
for i in range(edges.shape[0]): | |
node1 = int(edges[i, 0]) | |
node2 = int(edges[i, 1]) | |
if node1 == vertex1 and node2 == vertex2: | |
thickness = edges[i, 2] | |
elif node1 == vertex2 and node2 == vertex1: | |
thickness = edges[i, 2] | |
# Add to the denominator integral | |
circleIntegral += length / thickness | |
# Add to the cell area, using the cross-product | |
yCoordVector1 = y1 - yCentroid | |
zCoordVector1 = z1 - zCentroid | |
yCoordVector2 = y2 - yCentroid | |
zCoordVector2 = z2 - zCentroid | |
cellArea += 0.5 * np.abs(yCoordVector1*zCoordVector2-yCoordVector2*zCoordVector1) | |
J = 4.0 * cellArea**2 / circleIntegral | |
Jcell = J | |
# Check if there are free flanges too | |
for i in range(len(uniqueFlanges)): | |
# Get the two points that are connected to this edge | |
vertex1 = (uniqueFlanges[i])[0] | |
vertex2 = (uniqueFlanges[i])[1] | |
# Calculate the length of this edge | |
y1 = vertices[vertex1-1, 0] | |
z1 = vertices[vertex1-1, 1] | |
y2 = vertices[vertex2-1, 0] | |
z2 = vertices[vertex2-1, 1] | |
delta_y = y2 - y1 | |
delta_z = z2 - z1 | |
length = np.sqrt(delta_y**2 + delta_z**2) | |
# Get the thickness of this edge | |
for i in range(edges.shape[0]): | |
node1 = int(edges[i, 0]) | |
node2 = int(edges[i, 1]) | |
if node1 == vertex1 and node2 == vertex2: | |
thickness = edges[i, 2] | |
elif node1 == vertex2 and node2 == vertex1: | |
thickness = edges[i, 2] | |
J += length * thickness**3 / 3.0 | |
# Contributions to J expressed as fractions | |
cellFractionOfJ = Jcell/J | |
# ------------------------------------------------------------------------ | |
# ROTATE AXES IF Iyz HAS VALUE | |
# ------------------------------------------------------------------------ | |
# Shift and rotate axis system to align with centroid and principal axes | |
if np.abs(thetaPrincipal) > 1e-10: | |
for i in range(len(vertices)): | |
y = vertices[i-1, 0] - yCentroid | |
z = vertices[i-1, 1] - zCentroid | |
vertices[i - 1, 0] = y * np.cos(thetaPrincipal) + z * np.sin(thetaPrincipal) | |
vertices[i - 1, 1] = -y * np.sin(thetaPrincipal) + z * np.cos(thetaPrincipal) | |
storeYcentroid = yCentroid | |
storeZcentroid = zCentroid | |
yCentroid = 0.0 | |
zCentroid = 0.0 | |
# ------------------------------------------------------------------------ | |
# WARPING DIAGRAM PLUS ysc, zsc, Cw | |
# ------------------------------------------------------------------------ | |
Cw = 0 | |
if len(finalCells) > 1: | |
print('\n'"Multi-cell cross-sections not considered in warping torsion calculations") | |
# Open cross-section | |
elif len(finalCells) == 0: | |
# Walk through the first path | |
omegaTrial = [] | |
veryFirstEnd = (pathList[0])[0] | |
omegaTrial.append([veryFirstEnd, 0.0]) | |
for k in range(len(pathList[0]) - 1): | |
# Coordinates of end 1 | |
end1 = (pathList[0])[k] | |
y1 = vertices[int(end1) - 1, 0] | |
z1 = vertices[int(end1) - 1, 1] | |
# Coordinates of end 2 | |
end2 = (pathList[0])[k + 1] | |
y2 = vertices[int(end2) - 1, 0] | |
z2 = vertices[int(end2) - 1, 1] | |
# Cross-product | |
cross = y2 * z1 - y1 * z2 | |
# Set omega values | |
omegaTrial.append([end2, (omegaTrial[k])[1] + cross]) | |
# Walk through the rest of the paths | |
for i in range(1, len(pathList)): | |
# Find the first node in this path that already has an omega value | |
foundOmegaValue = False | |
for k in range(len(pathList[i])): | |
# Check if there is an omega value at this node | |
node = (pathList[i])[k] | |
omegaIndex, value = getOmegaValue(omegaTrial, node) | |
if omegaIndex != 0: | |
foundOmegaValue = True | |
locationInPath = k | |
# Walk forward from first-found-node-with-omega-value | |
for kk in range(locationInPath, len(pathList[i])-1): | |
# Nothing to do if next node has omega value too | |
end2 = (pathList[i])[kk+1] | |
omegaIndex, value = getOmegaValue(omegaTrial, end2) | |
if omegaIndex == 0: | |
# Coordinates of end 1 | |
end1 = (pathList[i])[kk] | |
y1 = vertices[int(end1)-1, 0] | |
z1 = vertices[int(end1)-1, 1] | |
# Omega value at end 1 | |
omegaIndex, value = getOmegaValue(omegaTrial, end1) | |
# Coordinates of end 2 | |
y2 = vertices[int(end2)-1, 0] | |
z2 = vertices[int(end2)-1, 1] | |
# Cross-product | |
cross = y2 * z1 - y1 * z2 | |
# Amend omega diagram | |
omegaTrial.append([end2, value + cross]) | |
# Walk backward from first-found-node-with-omega-value | |
for kk in range(len(pathList[i])-1, locationInPath-1, -1): | |
# Nothing to do if next node has omega value too | |
end2 = (pathList[i])[kk-1] | |
omegaIndex, value = getOmegaValue(omegaTrial, end2) | |
if omegaIndex == 0: | |
# Coordinates of end 1 | |
end1 = (pathList[i])[kk] | |
y1 = vertices[int(end1)-1, 0] | |
z1 = vertices[int(end1)-1, 1] | |
# Omega value at end 1 | |
omegaIndex, value = getOmegaValue(omegaTrial, end1) | |
# Coordinates of end 2 | |
y2 = vertices[int(end2)-1, 0] | |
z2 = vertices[int(end2)-1, 1] | |
# Cross-product | |
cross = y2 * z1 - y1 * z2 | |
# Amend omega diagram | |
omegaTrial.append([end2, value + cross]) | |
if foundOmegaValue == False: | |
print("Error: Found a path without any omega values") | |
# Single-cell cross-sections | |
elif len(finalCells) == 1: | |
# Figure out if the path around the cell is clockwise | |
clockwise = False | |
ySum = 0 | |
zSum = 0 | |
for i in range(len(finalCells[0])-1): | |
node = (finalCells[0])[i] | |
ySum += vertices[node-1, 0] | |
zSum += vertices[node-1, 1] | |
yCentre = ySum/float(len(finalCells[0])-1) | |
zCentre = zSum/float(len(finalCells[0])-1) | |
y1 = vertices[(finalCells[0])[0]-1, 0] | |
y2 = vertices[(finalCells[0])[1]-1, 0] | |
z1 = vertices[(finalCells[0])[0]-1, 1] | |
z2 = vertices[(finalCells[0])[1]-1, 1] | |
cross = (y1-yCentre) * (z2-zCentre) - (y2-yCentre) * (z1-zCentre) | |
if cross < 0: | |
clockwise = True | |
# Walk around the cell | |
omegaTrial = [] | |
veryFirstEnd = (finalCells[0])[0] | |
omegaTrial.append([veryFirstEnd, 0.0]) | |
for i in range(len(finalCells[0])-2): | |
# Length of this part | |
end1 = (finalCells[0])[i] | |
end2 = (finalCells[0])[i+1] | |
y1 = vertices[end1-1, 0] | |
z1 = vertices[end1-1, 1] | |
y2 = vertices[end2-1, 0] | |
z2 = vertices[end2-1, 1] | |
delta_y = y2 - y1 | |
delta_z = z2 - z1 | |
length = np.sqrt( delta_y**2 + delta_z**2) | |
# Thickness of this part | |
for k in range(edges.shape[0]): | |
node1 = int(edges[k, 0]) | |
node2 = int(edges[k, 1]) | |
if node1 == end1 and node2 == end2: | |
thickness = edges[k, 2] | |
elif node1 == end2 and node2 == end1: | |
thickness = edges[k, 2] | |
# Cross-product | |
cross = y1 * z2 - y2 * z1 | |
# Correction for cell walls | |
theSign = -1 | |
if clockwise: | |
theSign = 1 | |
correction = theSign * cellFractionOfJ * J * length / (2.0 * thickness * cellArea) | |
# Set omega values | |
omegaTrial.append([end2, (omegaTrial[i])[1] + cross + correction]) | |
# Now address free flanges obeying kinematic compatibility with the cell they are connected to | |
for i in range(len(uniqueFlanges)): | |
# Get the two points that are connected to this flange | |
end1 = (uniqueFlanges[i])[0] | |
end2 = (uniqueFlanges[i])[1] | |
y1 = vertices[end1-1, 0] | |
z1 = vertices[end1-1, 1] | |
y2 = vertices[end2-1, 0] | |
z2 = vertices[end2-1, 1] | |
# Find the cell node that is connected to this flange | |
for j in range(len(finalCells[0])-1): | |
node = (finalCells[0])[j] | |
yNode = vertices[node - 1, 0] | |
zNode = vertices[node - 1, 1] | |
if end1 == node: | |
# Must now find the cell-node next to "node" that is in the same plane as End 1 | |
forwardNode = 0 | |
backNode = 0 | |
if j == 0: | |
backNode = (finalCells[0])[len(finalCells[0])-2] | |
forwardNode = (finalCells[0])[j + 1] | |
else: | |
forwardNode = (finalCells[0])[j+1] | |
backNode = (finalCells[0])[j-1] | |
yBack = vertices[backNode - 1, 0] | |
zBack = vertices[backNode - 1, 1] | |
yForward = vertices[forwardNode - 1, 0] | |
zForward = vertices[forwardNode - 1, 1] | |
if zBack == z1 and z1 == z2: | |
omegaIndex, nodeValue = getOmegaValue(omegaTrial, node) | |
omegaIndex, backValue = getOmegaValue(omegaTrial, backNode) | |
change = (nodeValue-backValue)/(yNode-yBack)*(y2-y1) | |
omegaTrial.append([end2, nodeValue + change]) | |
break | |
elif yBack == y1 and y1 == y2: | |
omegaIndex, nodeValue = getOmegaValue(omegaTrial, node) | |
omegaIndex, backValue = getOmegaValue(omegaTrial, backNode) | |
change = (nodeValue-backValue)/(zNode-zBack)*(z2-z1) | |
omegaTrial.append([end2, nodeValue + change]) | |
break | |
elif zForward == z1 and z1 == z2: | |
omegaIndex, nodeValue = getOmegaValue(omegaTrial, node) | |
omegaIndex, forwardValue = getOmegaValue(omegaTrial, forwardNode) | |
change = (nodeValue-forwardValue)/(yNode-yForward)*(y2-y1) | |
omegaTrial.append([end2, nodeValue + change]) | |
break | |
elif yForward == y1 and y1 == y2: | |
omegaIndex, nodeValue = getOmegaValue(omegaTrial, node) | |
omegaIndex, forwardValue = getOmegaValue(omegaTrial, forwardNode) | |
change = (nodeValue-forwardValue)/(zNode-zForward)*(z2-z1) | |
omegaTrial.append([end2, nodeValue + change]) | |
break | |
elif end2 == node: | |
# Now find the cell-node next to "node" that is in the same plane as End 2 | |
forwardNode = 0 | |
backNode = 0 | |
if j == 0: | |
backNode = (finalCells[0])[len(finalCells[0]) - 2] | |
forwardNode = (finalCells[0])[j + 1] | |
else: | |
forwardNode = (finalCells[0])[j + 1] | |
backNode = (finalCells[0])[j - 1] | |
yBack = vertices[backNode - 1, 0] | |
zBack = vertices[backNode - 1, 1] | |
yForward = vertices[forwardNode - 1, 0] | |
zForward = vertices[forwardNode - 1, 1] | |
if zBack == z1 and z1 == z2: | |
omegaIndex, nodeValue = getOmegaValue(omegaTrial, node) | |
omegaIndex, backValue = getOmegaValue(omegaTrial, backNode) | |
change = (nodeValue - backValue) / (yNode - yBack) * (y1 - y2) | |
omegaTrial.append([end1, nodeValue + change]) | |
break | |
elif yBack == y1 and y1 == y2: | |
omegaIndex, nodeValue = getOmegaValue(omegaTrial, node) | |
omegaIndex, backValue = getOmegaValue(omegaTrial, backNode) | |
change = (nodeValue - backValue) / (zNode - zBack) * (z1 - z2) | |
omegaTrial.append([end1, nodeValue + change]) | |
break | |
elif zForward == z1 and z1 == z2: | |
omegaIndex, nodeValue = getOmegaValue(omegaTrial, node) | |
omegaIndex, forwardValue = getOmegaValue(omegaTrial, forwardNode) | |
change = (nodeValue - forwardValue) / (yNode - yForward) * (y1 - y2) | |
omegaTrial.append([end1, nodeValue + change]) | |
break | |
elif yForward == y1 and y1 == y2: | |
omegaIndex, nodeValue = getOmegaValue(omegaTrial, node) | |
omegaIndex, forwardValue = getOmegaValue(omegaTrial, forwardNode) | |
change = (nodeValue - forwardValue) / (zNode - zForward) * (z1 - z2) | |
omegaTrial.append([end1, nodeValue + change]) | |
break | |
# Flip the sign of the omega diagram if the walk was counterclockwise | |
for i in range(len(omegaTrial)): | |
(omegaTrial[i])[1] = theSign * (omegaTrial[i])[1] | |
# Calculate integrals needed to find final omega diagram | |
intOmega = 0 | |
intyOmega = 0 | |
intzOmega = 0 | |
for i in range(len(edges)): | |
# End 1 of this edge | |
end1 = edges[i, 0] | |
y1 = vertices[int(end1) - 1, 0] - yCentroid | |
z1 = vertices[int(end1) - 1, 1] - zCentroid | |
# End 2 of this edge | |
end2 = edges[i, 1] | |
y2 = vertices[int(end2) - 1, 0] - yCentroid | |
z2 = vertices[int(end2) - 1, 1] - zCentroid | |
# Omega values for this edge | |
index, omegaTrialEnd1 = getOmegaValue(omegaTrial, end1) | |
index, omegaTrialEnd2 = getOmegaValue(omegaTrial, end2) | |
# Pick up stored length and thickness | |
L = lengths[i] | |
t = thicknesses[i] | |
# Integrals | |
intOmega += 0.5*(omegaTrialEnd1+omegaTrialEnd2)*L*t | |
intyOmega += L*t/6.0*(y1*(2.0*omegaTrialEnd1+omegaTrialEnd2)+y2*(2.0*omegaTrialEnd2+omegaTrialEnd1)) | |
intzOmega += L*t/6.0*(z1*(2.0*omegaTrialEnd1+omegaTrialEnd2)+z2*(2.0*omegaTrialEnd2+omegaTrialEnd1)) | |
# Shear centre coordinates and normalizing constant | |
C = -intOmega/A | |
if np.abs(thetaPrincipal) > 1e-10: | |
ysc = -intzOmega/IyRotated | |
zsc = intyOmega/IzRotated | |
else: | |
ysc = -intzOmega / Iy | |
zsc = intyOmega / Iz | |
# Final omega diagram | |
omegaFinal = [] | |
minWarp = 0 | |
maxWarp = 0 | |
for i in range(len(omegaTrial)): | |
node = (omegaTrial[i])[0] | |
yHere = vertices[node-1, 0] - yCentroid | |
zHere = vertices[node-1, 1] - zCentroid | |
trialValue = (omegaTrial[i])[1] | |
finalValue = trialValue + C + ysc * zHere - zsc * yHere | |
omegaFinal.append([node, finalValue]) | |
if finalValue < minWarp: | |
minWarp = finalValue | |
if finalValue > maxWarp: | |
maxWarp = finalValue | |
# Cross-section constant for warping torsion | |
for i in range(len(edges)): | |
# Nodes of this edge | |
end1 = edges[i, 0] | |
end2 = edges[i, 1] | |
# Omega values for this edge | |
index, omegaFinalEnd1 = getOmegaValue(omegaFinal, end1) | |
index, omegaFinalEnd2 = getOmegaValue(omegaFinal, end2) | |
# Length and thickness of this edge | |
L = lengths[i] | |
t = thicknesses[i] | |
# Accumulate the integral | |
if np.sign(omegaFinalEnd1) == np.sign(omegaFinalEnd2): | |
Cw += L*t/3.0*(omegaFinalEnd1**2 + omegaFinalEnd2**2 + omegaFinalEnd1*omegaFinalEnd2) | |
else: | |
Cw += L*t/3.0*(omegaFinalEnd1**2 + omegaFinalEnd2**2 - np.abs(omegaFinalEnd1*omegaFinalEnd2)) | |
# ------------------------------------------------------------------------ | |
# ROTATE AXES BACK | |
# ------------------------------------------------------------------------ | |
if np.abs(thetaPrincipal) > 1e-10: | |
yCentroid = storeYcentroid | |
zCentroid = storeZcentroid | |
y = ysc | |
z = zsc | |
ysc = y * np.cos(thetaPrincipal) - z * np.sin(thetaPrincipal) + yCentroid | |
zsc = y * np.sin(thetaPrincipal) + z * np.cos(thetaPrincipal) + zCentroid | |
for i in range(len(vertices)): | |
y = vertices[i-1, 0] | |
z = vertices[i-1, 1] | |
vertices[i - 1, 0] = y * np.cos(thetaPrincipal) - z * np.sin(thetaPrincipal) + yCentroid | |
vertices[i - 1, 1] = y * np.sin(thetaPrincipal) + z * np.cos(thetaPrincipal) + zCentroid | |
# ------------------------------------------------------------------------ | |
# PRINT RESULTS | |
# ------------------------------------------------------------------------ | |
print('\n'"Area:", A) | |
print('\n'"Centroid y-coordinate: %.2f" % yCentroid) | |
print('\n'"Centroid z-coordinate: %.2f" % zCentroid) | |
print('\n'"Moment of inertia Iy: %.2f" % Iy) | |
print('\n'"Moment of inertia Iz: %.2f" % Iz) | |
if np.abs(thetaPrincipal) > 1e-10: | |
print('\n'"Principal axis rotation (degrees): %.2f" % (thetaPrincipal/np.pi*180.0)) | |
print('\n'"Product of inertia Iyz: %.2f" % Iyz) | |
print('\n'"New principal moment of inertia Iy: %.2f" % IyRotated) | |
print('\n'"New principal moment of inertia Iz: %.2f" % IzRotated) | |
print('\n'"Shear centre y-coordinate in original coordinate system: %.2f" % ysc) | |
print('\n'"Shear centre z-coordinate in original coordinate system: %.2f" % zsc) | |
else: | |
print('\n'"Shear centre y-coordinate: %.2f" % ysc) | |
print('\n'"Shear centre z-coordinate: %.2f" % zsc) | |
print('\n'"St. Venant torsion constant J: %.2f" % J) | |
print('\n'"Warping torsion constant Cw: %.2f" % Cw) | |
# ------------------------------------------------------------------------ | |
# PLOT | |
# ------------------------------------------------------------------------ | |
for i in range(len(edges)): | |
# End 1 of this edge | |
end1 = edges[i, 0] | |
y1 = vertices[int(end1)-1, 0] | |
z1 = vertices[int(end1)-1, 1] | |
# End 2 of this edge | |
end2 = edges[i, 1] | |
y2 = vertices[int(end2)-1, 0] | |
z2 = vertices[int(end2)-1, 1] | |
# Omega values for this edge | |
if Cw != 0: | |
index, omegaFinalEnd1 = getOmegaValue(omegaFinal, end1) | |
index, omegaFinalEnd2 = getOmegaValue(omegaFinal, end2) | |
# Discretize and plot the line | |
segments = 10 | |
for j in range(segments): | |
y1Here = y1 + j/float(segments)*(y2-y1) | |
z1Here = z1 + j/float(segments)*(z2-z1) | |
y2Here = y1 + (j+1)/float(segments)*(y2-y1) | |
z2Here = z1 + (j+1)/float(segments)*(z2-z1) | |
if Cw > 1e-5: | |
warpValueHere = omegaFinalEnd1 + j/float(segments) * (omegaFinalEnd2-omegaFinalEnd1) | |
colorCode = (warpValueHere - minWarp) / (maxWarp - minWarp) | |
plt.plot(np.array([y1Here, y2Here]), np.array([z1Here, z2Here]), color=(colorCode, 0, 1 - colorCode), linewidth=6.0, zorder=1) | |
else: | |
colorCode = 0.0 | |
plt.plot(np.array([y1Here, y2Here]), np.array([z1Here, z2Here]), color=(0.52, 0.52, 0.52), linewidth=6.0, zorder=1) | |
# Create the plot | |
max = np.amax(np.amax((vertices))) | |
min = np.amin(np.amin(vertices)) | |
yMin = min - (max-min)*0.4 | |
border = (max - min) * 0.1 | |
plt.plot(np.array([yCentroid, max+border]), np.array([zCentroid, zCentroid+(np.abs(max)+border-yCentroid)*np.tan(thetaPrincipal)]), 'k-',linewidth=1.0) | |
plt.plot(np.array([yCentroid, yMin-border]), np.array([zCentroid, zCentroid-(np.abs(yMin)+border+yCentroid)*np.tan(thetaPrincipal)]), 'k-',linewidth=1.0) | |
plt.plot(np.array([yCentroid, yCentroid-(np.abs(max)+border-zCentroid)*np.tan(thetaPrincipal)]), np.array([zCentroid, max+border]), 'k-',linewidth=1.0) | |
plt.plot(np.array([yCentroid, yCentroid+(np.abs(min)+border+zCentroid)*np.tan(thetaPrincipal)]), np.array([zCentroid, min-border]), 'k-',linewidth=1.0) | |
plt.scatter([yCentroid], [zCentroid], s=100, c='k', marker='x', zorder=2) | |
plt.scatter([ysc], [zsc], s=100, c='k', marker='x', zorder=2) | |
plt.axis([yMin-border, max+border, min-border, max+border]) | |
plt.title("Centroid + Shear Centre + Warping") | |
plt.show() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment