Skip to content

Instantly share code, notes, and snippets.

@terjehaukaas
Last active January 29, 2020 01:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save terjehaukaas/b71e9e6ca88c5b5b3a4dcf9c4978419c to your computer and use it in GitHub Desktop.
Save terjehaukaas/b71e9e6ca88c5b5b3a4dcf9c4978419c to your computer and use it in GitHub Desktop.
MDOF Buckling Analysis
# ------------------------------------------------------------------------
# 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 any form of warranty.
# Please see the Programming note for how to get started, and notice
# that you must copy certain functions into the file terjesfunctions.py
# ------------------------------------------------------------------------
import numpy as np
import matplotlib.pyplot as plt
import terjesfunctions as fcns
# ------------------------------------------------------------------------
# INPUT [N, m, s, kg]
# ------------------------------------------------------------------------
# Constants
E = 2.0e+11
I = 8.09e-5
A = 0.0091
storeyHeight = 4.0
bayWidth = 6.0
# Nodes (x, y)
nodes = np.array([(0, 0),
(bayWidth, 0),
(2*bayWidth, 0),
(0, storeyHeight),
(bayWidth, storeyHeight),
(2*bayWidth, storeyHeight),
(0, 2*storeyHeight),
(bayWidth, 2*storeyHeight),
(2*bayWidth, 2*storeyHeight)])
# Frame elements (node1, node2, E, I, A)
frameElements = np.array([(1, 4, E, I, A),
(2, 5, E, I, A),
(3, 6, E, I, A),
(4, 5, E, I, A),
(5, 6, E, I, A),
(4, 7, E, I, A),
(5, 8, E, I, A),
(6, 9, E, I, A),
(7, 8, E, I, A),
(8, 9, E, I, A)])
# Nodal load multipliers for the buckling load (node, value, dof)
bucklingLoadMultiplier = np.array([(4, -1, 2),
(5, -1, 2),
(6, -1, 2),
(7, -1, 2),
(8, -1, 2),
(9, -1, 2)])
# Boundary conditions (node, 0=free, 1=fixed)
constraints = np.array([(1, 1, 1, 1),
(2, 1, 1, 1),
(3, 1, 1, 1)])
# ------------------------------------------------------------------------
# LOOP OVER ELEMENTS, ASSEMBLE THE ELASTIC STIFFNESS MATRIX
# ------------------------------------------------------------------------
# Number of DOFs
numDOFsPerNodeInStructure = 3
numNodes = nodes.shape[0]
totalNumDOFs = numDOFsPerNodeInStructure * numNodes
# Count elements
numFrameElements = frameElements.shape[0]
# Set axial force in elements
Nframe = np.zeros(numFrameElements)
# Initialize the big stiffness matrix
Ka_elastic = np.zeros((totalNumDOFs, totalNumDOFs))
# Assemble frame elements
KlStorageFrame = []
TlgStorageFrame = []
for i in range(numFrameElements):
# Information about element
E = frameElements[i, 2]
I = frameElements[i, 3]
A = frameElements[i, 4]
# Information about nodes
node1 = int(frameElements[i, 0])
node2 = int(frameElements[i, 1])
nodeList = np.array([node1, node2])
nodalcoords = np.array([nodes[node1-1, 0], nodes[node1-1, 1], nodes[node2-1, 0], nodes[node2-1, 1]])
# Element length and orientation
delta_x = nodalcoords[2] - nodalcoords[0]
delta_y = nodalcoords[3] - nodalcoords[1]
L = np.sqrt(delta_x ** 2 + delta_y ** 2)
# Get Kl and Tlg
nu = 0
beta = 0
[Kl_elastic, Kl_geometric, Tlg] = fcns.getFrameStiffness2D(E, nu, I, A, beta, L, delta_x, delta_y, Nframe[i])
KlStorageFrame.append(Kl_elastic)
TlgStorageFrame.append(Tlg)
# Calculate Kg = Tlg^T * Kl * Tlg
Kg_elastic = (np.transpose(Tlg).dot(Kl_elastic)).dot(Tlg)
# Assemble
numDOFsPerNodeInElement = 3
Ka_elastic = fcns.assembleStiffnessMatrix(Ka_elastic, Kg_elastic, nodeList, numDOFsPerNodeInElement, numDOFsPerNodeInStructure)
# ------------------------------------------------------------------------
# ASSEMBLE THE LOAD VECTOR Fa
# ------------------------------------------------------------------------
# Notice that only point loads are included for now
Fa = np.zeros(totalNumDOFs)
numBucklingLoadMultipliers = bucklingLoadMultiplier.shape[0]
for i in range(numBucklingLoadMultipliers):
Fa[(bucklingLoadMultiplier[i, 0]-1)*numDOFsPerNodeInStructure+(bucklingLoadMultiplier[i, 2]-1)] \
= bucklingLoadMultiplier[i, 1]
# ------------------------------------------------------------------------
# INTRODUCE BOUNDARY CONDITIONS & LOCK ZERO-STIFFNESS DOFs
# ------------------------------------------------------------------------
# DOFs locked by input
dofStatus = np.zeros(totalNumDOFs)
nfix = constraints.shape[0]
for k in range(nfix):
fixedNode = int(constraints[k, 0])
for m in range(numDOFsPerNodeInStructure):
if constraints[k, m + 1] == 1:
dofStatus[(fixedNode - 1) * numDOFsPerNodeInStructure + m] = 1
numFreeDOFs = int(totalNumDOFs - np.sum(dofStatus))
Kf_elastic = np.zeros((numFreeDOFs, numFreeDOFs))
Ff = np.zeros(numFreeDOFs)
iCounter = 0
jCounter = 0
for i in range(totalNumDOFs):
if dofStatus[i] == 0:
jCounter = 0
for j in range(i + 1):
if dofStatus[j] == 0:
Kf_elastic[iCounter, jCounter] = Ka_elastic[i, j]
Kf_elastic[jCounter, iCounter] = Ka_elastic[i, j]
jCounter += 1
Ff[iCounter] = Fa[i]
iCounter += 1
# ------------------------------------------------------------------------
# SOLVE THE SYSTEM OF EQUILIBRIUM EQUATIONS AND GET ua
# ------------------------------------------------------------------------
uf = np.linalg.solve(Kf_elastic, Ff)
# Get ua by simply adding zero numbers to the array
ua = np.zeros(totalNumDOFs)
iCounter = 0
for i in range(totalNumDOFs):
if dofStatus[i] == 0:
ua[i] = uf[iCounter]
iCounter += 1
# ------------------------------------------------------------------------
# RECOVER LOCAL ELEMENT FORCES
# ------------------------------------------------------------------------
# Recover frame element forces
for i in range(numFrameElements):
# From ua to ug
node1 = int(frameElements[i, 0])
node2 = int(frameElements[i, 1])
nodeList = np.array([node1, node2])
numNodesInElement = len(nodeList)
numDOFsPerNodeInElement = 3
ug = np.zeros(numNodesInElement * numDOFsPerNodeInElement)
for j in range(numNodesInElement):
for m in range(numDOFsPerNodeInElement):
ug[j * numDOFsPerNodeInElement + m] = ua[(nodeList[j] - 1) * numDOFsPerNodeInStructure + m]
# From ug to ul
ul = TlgStorageFrame[i].dot(ug)
# From ul to Fl
Fl = KlStorageFrame[i].dot(ul)
# Get axial force for P-delta analysis (compression positive)
Nframe[i] = Fl[0]
# ------------------------------------------------------------------------
# LOOP OVER ELEMENTS, ASSEMBLE THE GEOMETRIC STIFFNESS MATRIX
# ------------------------------------------------------------------------
# Initialize the big stiffness matrices
Ka_geometric = np.zeros((totalNumDOFs, totalNumDOFs))
# Assemble frame elements
for i in range(numFrameElements):
# Information about element
E = frameElements[i, 2]
I = frameElements[i, 3]
A = frameElements[i, 4]
# Information about nodes
node1 = int(frameElements[i, 0])
node2 = int(frameElements[i, 1])
nodeList = np.array([node1, node2])
nodalcoords = np.array([nodes[node1-1, 0], nodes[node1-1, 1], nodes[node2-1, 0], nodes[node2-1, 1]])
# Element length and orientation
delta_x = nodalcoords[2] - nodalcoords[0]
delta_y = nodalcoords[3] - nodalcoords[1]
L = np.sqrt(delta_x ** 2 + delta_y ** 2)
# Get Kl and Tlg
nu = 0
beta = 0
[Kl_elastic, Kl_geometric, Tlg] = fcns.getFrameStiffness2D(E, nu, I, A, beta, L, delta_x, delta_y, Nframe[i])
# Calculate Kg = Tlg^T * Kl * Tlg
Kg_geometric = (np.transpose(Tlg).dot(Kl_geometric)).dot(Tlg)
# Assemble
numDOFsPerNodeInElement = 3
Ka_geometric = fcns.assembleStiffnessMatrix(Ka_geometric, Kg_geometric, nodeList, numDOFsPerNodeInElement, numDOFsPerNodeInStructure)
# ------------------------------------------------------------------------
# INTRODUCE BOUNDARY CONDITIONS
# ------------------------------------------------------------------------
Kf_geometric = np.zeros((numFreeDOFs, numFreeDOFs))
iCounter = 0
jCounter = 0
for i in range(totalNumDOFs):
if dofStatus[i] == 0:
jCounter = 0
for j in range(i + 1):
if dofStatus[j] == 0:
Kf_geometric[iCounter, jCounter] = Ka_geometric[i, j]
Kf_geometric[jCounter, iCounter] = Ka_geometric[i, j]
jCounter += 1
iCounter += 1
# ------------------------------------------------------------------------
# EIGENVALUE ANALYSIS
# ------------------------------------------------------------------------
# Find outer plot limits
xmin = 0
xmax = 0
ymin = 0
ymax = 0
for i in range(numNodes):
if nodes[i, 0] < xmin:
xmin = nodes[i, 0]
elif nodes[i, 0] > xmax:
xmax = nodes[i, 0]
elif nodes[i, 1] < ymin:
ymin = nodes[i, 1]
elif nodes[i, 1] > ymax:
ymax = nodes[i, 1]
xmin = xmin - (xmax - xmin) * 0.2
ymin = ymin - (ymax - ymin) * 0.2
xmax = xmax + (xmax - xmin) * 0.2
ymax = ymax + (ymax - ymin) * 0.2
# Make the plotting area square
xrange = xmax-xmin
yrange = ymax-ymin
if xrange > yrange:
ymin -= 0.5*(xrange-yrange)
ymax += 0.5*(xrange-yrange)
else:
xmin -= 0.5*(yrange-xrange)
xmax += 0.5*(yrange-xrange)
# Here we need scipy, to solve the *generalized* eigenvalue problem [K-omega^2*M]{u} = 0
import scipy
# Determine eigenvalues
allEigenvalues, allEigenvectors = scipy.linalg.eig(Kf_elastic, Kf_geometric)
eigenvalues = []
eigenvectors = []
for k in range(len(allEigenvalues)):
if allEigenvalues[k].real == np.inf:
pass
else:
eigenvalues.append(allEigenvalues[k].real)
modeShape = []
# Extract the mode
for j in range(numFreeDOFs):
modeShape.append(allEigenvectors[j, k])
eigenvectors.append(modeShape)
# Sort the modes
idx = np.array(eigenvalues).argsort()[::]
# Print summary of buckling loads
print('\n'"Found the following", len(eigenvalues), "buckling loads:")
for i in range(len(eigenvalues)):
index = idx[i]
print("P = %.1f kN" % (0.001*eigenvalues[index]))
# Clear plot
plt.cla()
# Get ua by adding zero numbers to the array
ua = np.zeros(totalNumDOFs)
mode = eigenvectors[index]
jCounter = 0
for j in range(totalNumDOFs):
if dofStatus[j] == 0:
ua[j] = mode[jCounter]
jCounter += 1
xcoord = np.zeros(2)
ycoord = np.zeros(2)
xdisp = np.zeros(2)
ydisp = np.zeros(2)
scale_factor = 1/np.max(np.abs(mode))
for j in range(numFrameElements):
# Get end nodes for this element
node1 = int(frameElements[j, 0])
node2 = int(frameElements[j, 1])
# Get end-point coordinates for this element
x1 = nodes[node1 - 1, 0]
y1 = nodes[node1 - 1, 1]
x2 = nodes[node2 - 1, 0]
y2 = nodes[node2 - 1, 1]
# Calculate element length and direction cosines
delta_x = x2 - x1
delta_y = y2 - y1
L = np.sqrt(delta_x ** 2 + delta_y ** 2)
cx = delta_x / L
cy = delta_y / L
# Get EI for this element
E = frameElements[j, 2]
I = frameElements[j, 3]
EI = E * I
# Get local displacements
nodeList = np.array([node1, node2])
numNodesInElement = len(nodeList)
numDOFsPerNodeInElement = 3
ug = np.zeros(numNodesInElement * numDOFsPerNodeInElement)
for n in range(numNodesInElement):
for m in range(numDOFsPerNodeInElement):
ug[n * numDOFsPerNodeInElement + m] = ua[(nodeList[n] - 1) * numDOFsPerNodeInStructure + m]
ul = TlgStorageFrame[j].dot(ug)
# Rename the displacements for clarity
u1 = ul[0]
u2 = ul[1]
u3 = ul[2]
u4 = ul[3]
u5 = ul[4]
u6 = ul[5]
# Coefficients in the solution to the differential equation
C1 = -2.0 * u5 / L ** 3 - u3 / L ** 2 + 2.0 * u2 / L ** 3 - u6 / L ** 2
C2 = 3.0 * u5 / L ** 2 + 2.0 * u3 / L - 3.0 * u2 / L ** 2 + u6 / L
C3 = -u3
C4 = u2
# Divide the element into "discretization" number of parts
discretization = 10
xi = 0.0
for j in range(discretization):
# First point of this segment of the element
x = xi * L
xA = x1 + (x2 - x1) * xi
yA = y1 + (y2 - y1) * xi
# Calculate displacements and bending moment at this point
u = u1 + xi * (u4 - u1)
w = C1 * x ** 3 + C2 * x ** 2 + C3 * x + C4
# Rotate and scale displacements, BMD, and SFD into the global coordinate system: global = R * local
xAdisp = xA + (cx * u - cy * w) * scale_factor
yAdisp = yA + (cy * u + cx * w) * scale_factor
# Increment xi to get to the second point
xi += 1.0 / (float(discretization))
# Second point of this segment of the element
x = xi * L
xB = x1 + (x2 - x1) * xi
yB = y1 + (y2 - y1) * xi
# Displacement and bending moment at this point
u = ul[0] + xi * (ul[3] - ul[0])
w = C1 * x ** 3 + C2 * x ** 2 + C3 * x + C4
# Rotate and scale displacements, BMD, and SFD into the global coordinate system: global = R * local
xBdisp = xB + (cx * u - cy * w) * scale_factor
yBdisp = yB + (cy * u + cx * w) * scale_factor
# Plotting for this segment
xcoord[0] = xA
xcoord[1] = xB
ycoord[0] = yA
ycoord[1] = yB
plt.plot(xcoord, ycoord, color='silver')
xdisp[0] = xAdisp
xdisp[1] = xBdisp
ydisp[0] = yAdisp
ydisp[1] = yBdisp
plt.plot(xdisp, ydisp, 'k-')
plt.ion()
plt.axis([xmin, xmax, ymin, ymax])
plt.title("Buckling Mode for $P_{cr}=%.1f kN$" % (0.001*eigenvalues[index]))
plt.show()
plt.pause(2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment