Sensitivity and reliability analysis of tower cranes
As part of CIVL 492: Directed Studies (under the supervision of Dr. Terje Haukaas), this project focuses on the sensitivity and reliability analysis of tower cranes. The research examines how variations in loads, material properties, and structural configurations influence performance and safety. Probabilistic methods and computational modeling are applied to identify critical parameters and provide insights for reliability-based design.
SAMPLE PARAMETRIC CODE
from G2AnalysisNonlinearStatic import *
from G2Model import *
import math
elementType = 2
materialType = 'Bilinear' # 'Bilinear'/'Plasticity'/'BoucWen'
P = 100e3
A = 0.04**2
E = 200e9
fy = 350e6
alpha = 0.02
eta = 2.0
gamma = 0.5
beta = 0.5
Hk = alpha*E/(1-alpha)
K = 0.0
delta = 0.0
fy_inf = 100.0
dt = 1/20
nsteps = 25
KcalcFrequency= 1
maxIter = 100
tol = 1e-5
def build_crane(Hm=30.0, Lj=45.0, Lc=15.0, width=5.0, panel=5.0):
r6 = lambda x: round(x, 6)
nodes = []
idx = {}
def add_node(x, y):
key = (r6(x), r6(y))
if key not in idx:
idx[key] = len(nodes)
nodes.append([float(x), float(y)])
return idx[key]
# Mast chords: x=0 and x=width; y = 0..Hm
n_mast = int(round(Hm/panel))
mast_left_ids = [add_node(0.0, i*panel) for i in range(n_mast+1)]
mast_right_ids = [add_node(width, i*panel) for i in range(n_mast+1)]
# Jib chords: y = Hm (top), y = Hm - width (bottom)
n_jib = int(round(Lj/panel))
# top chord starts at x >= width to avoid duplicate at mast-right top
jib_top_ids = [add_node(i*panel, Hm) for i in range(n_jib+1) if i*panel >= width - 1e-9]
# bottom chord from x = 0 .. Lj
jib_bot_ids = [add_node(i*panel, Hm - width) for i in range(n_jib+1)]
# Counter-jib chords at negative x: y=Hm (top), y=Hm-width (bottom)
n_cj = int(round(Lc/panel))
cj_top_ids = [add_node(-i*panel, Hm) for i in range(1, n_cj+1)] # -panel .. -Lc
cj_bot_ids = [add_node(-i*panel, Hm - width) for i in range(1, n_cj+1)]
top_by_x = { r6(nodes[n][0]): n for n in jib_top_ids }
bot_by_x = { r6(nodes[n][0]): n for n in jib_bot_ids }
cj_top_by_x = { r6(nodes[n][0]): n for n in cj_top_ids }
cj_bot_by_x = { r6(nodes[n][0]): n for n in cj_bot_ids }
# Elements (0-connectivity)
ELEMENTS_0B = []
def add(sec, i, j):
if i != j:
ELEMENTS_0B.append([elementType, sec, i, j])
# 0: Mast chords & horizontals
for a,b in zip(mast_left_ids[:-1], mast_left_ids[1:]): add(0, a, b)
for a,b in zip(mast_right_ids[:-1], mast_right_ids[1:]): add(0, a, b)
for a,b in zip(mast_left_ids, mast_right_ids): add(0, a, b)
# 1: Mast diagonals (alternating)
for k in range(len(mast_left_ids)-1):
if k % 2 == 0: add(1, mast_left_ids[k], mast_right_ids[k+1])
else: add(1, mast_right_ids[k], mast_left_ids[k+1])
# 2: Jib chords
for a,b in zip(jib_top_ids[:-1], jib_top_ids[1:]): add(2, a, b)
for a,b in zip(jib_bot_ids[:-1], jib_bot_ids[1:]): add(2, a, b)
# 2: Jib verticals (skip x=0 and x=width to avoid duplicates)
for x,t in top_by_x.items():
if x in bot_by_x and x > r6(width):
add(2, t, bot_by_x[x])
# 3: Jib diagonals
# even i: bottom-left -> top-right ; odd i: top-left -> bottom-right
xs = [i*panel for i in range(n_jib)] # left edges of panels
for i, xL in enumerate(xs):
xR = r6(xL + panel)
xLr = r6(xL)
tL, tR = top_by_x.get(xLr), top_by_x.get(xR)
bL, bR = bot_by_x.get(xLr), bot_by_x.get(xR)
if i % 2 == 0:
if bL is not None and tR is not None:
add(3, bL, tR)
else:
if tL is not None and bR is not None:
add(3, tL, bR)
if bot_by_x.get(r6(0.0)) is not None and len(top_by_x) > 0:
first_top_x = min(top_by_x.keys())
if first_top_x > r6(width) - 1e-9 and first_top_x not in {r6(panel)}:
add(3, bot_by_x[r6(0.0)], top_by_x[first_top_x])
# 4: Counter-jib chords & verticals (rooted at mast-left top/bottom)
mast_top_left = mast_left_ids[-1] # (0, Hm)
mast_bot_left = mast_left_ids[-1-1] if width > 0 else mast_left_ids[-1]
# top chord: 0 -> -panel -> ... -> -Lc
prev = mast_top_left
for x in sorted(cj_top_by_x.keys(), key=lambda v: -v):
add(4, prev, cj_top_by_x[x]); prev = cj_top_by_x[x]
# bottom chord
prev = mast_left_ids[n_mast-1] # (0, Hm-width)
for x in sorted(cj_bot_by_x.keys(), key=lambda v: -v):
add(4, prev, cj_bot_by_x[x]); prev = cj_bot_by_x[x]
# verticals
for x in cj_top_by_x:
if x in cj_bot_by_x:
add(4, cj_top_by_x[x], cj_bot_by_x[x])
# 5: Counter-jib diagonals
cj_xs = sorted(cj_top_by_x.keys())
for i in range(len(cj_xs)-1):
xL, xR = cj_xs[i], cj_xs[i+1]
tL, tR = cj_top_by_x[xL], cj_top_by_x[xR]
bL, bR = cj_bot_by_x.get(xL), cj_bot_by_x.get(xR)
if bL is None or bR is None:
continue
if i % 2 == 0:
add(5, bL, tR) # bottom-left -> top-right
else:
add(5, tL, bR) # top-left -> bottom-right
# PATCH
if len(cj_xs) >= 1:
x_first = r6(-panel) # nearest counter-jib x to the mast
if x_first in cj_top_by_x and x_first in cj_bot_by_x:
# The next virtual index would be i = len(cj_xs)-1
mast_top = mast_top_left # (0, Hm)
# bottom root of counter-jib at the mast (same node used to start cj bottom chord)
mast_bot_cj_root = mast_left_ids[n_mast-1] # (0, Hm - width) when width==panel
i_virtual = len(cj_xs) - 1
if i_virtual % 2 == 0:
# bottom-left (-panel, Hm-width) -> top-right (0, Hm)
add(5, cj_bot_by_x[x_first], mast_top)
else:
# top-left (-panel, Hm) -> bottom-right (0, Hm-width)
add(5, cj_top_by_x[x_first], mast_bot_cj_root)
# fix base nodes at (0,0) and (width,0)
CONSTRAINTS = [[0,0] for _ in nodes]
base_L = idx[(r6(0.0), r6(0.0))]
base_R = idx[(r6(width), r6(0.0))]
CONSTRAINTS[base_L] = [1,1]
CONSTRAINTS[base_R] = [1,1]
# Loads: load downward at jib tip (top chord, max x at y=Hm)
LOADS = [[0.0, 0.0] for _ in nodes]
tip_top_id = max((n for n in jib_top_ids), key=lambda n: nodes[n][0])
LOADS[tip_top_id][1] -= P
# Section
nel = len(ELEMENTS_0B)
SECTIONS = [['Truss', A] for _ in range(nel)]
MATERIALS = []
for _ in range(nel):
if materialType == 'BoucWen':
MATERIALS.append(['BoucWen', E, fy, alpha, eta, beta, gamma])
elif materialType == 'Bilinear':
MATERIALS.append(['Bilinear', E, fy, alpha])
elif materialType == 'Plasticity':
MATERIALS.append(['Plasticity', E, fy, Hk, K, delta, fy_inf])
else:
raise ValueError("Unknown materialType")
# 1-based elements
ELEMENTS_1B = [[etype, sec, i+1, j+1] for (etype, sec, i, j) in ELEMENTS_0B]
return nodes, CONSTRAINTS, ELEMENTS_0B, ELEMENTS_1B, SECTIONS, MATERIALS, LOADS, tip_top_id
Hm, Lj, Lc, width, panel = 20.0, 75.0, 12.0, 5.0, 5.0
NODES, CONSTRAINTS, ELEMENTS_0B, ELEMENTS, SECTIONS, MATERIALS, LOADS, tip_id = build_crane(
Hm=Hm, Lj=Lj, Lc=Lc, width=width, panel=panel
)
# Track Node
trackNode = tip_id # 0-based tip node
trackDOF = 2
# model
MASS = [[0.0, 0.0] for _ in NODES]
input = [NODES, CONSTRAINTS, ELEMENTS, SECTIONS, MATERIALS, LOADS, MASS]
m = model(input)
#import matplotlib.pyplot as plt
#[plt.plot([NODES[i][0], NODES[j][0]], [NODES[i][1], NODES[j][1]]) for _,_,i,j in ELEMENTS_0B]
#plt.gca().set_aspect('equal'); plt.grid(True); plt.show()
# Response with DDM sensitivities
DDMparameters = [['Element', 'E', [1]],
['Element', 'fy', [1]],
['Element', 'alpha', [1]],
['Element', 'A', [1]]]
# Run
t, loadFactor, u, dudx, ddm2 = nonlinearStaticAnalysis(m, nsteps, dt, maxIter, KcalcFrequency, tol, trackNode, trackDOF, DDMparameters, True)
# Plot the DDM sensitivities
plt.ion()
plt.figure()
plt.autoscale(True)
plt.plot(t, dudx[0, :], 'k-')
plt.plot(t, dudx[1, :], 'r-')
plt.plot(t, dudx[2, :], 'b-')
plt.plot(t, dudx[3, :], 'g-')
print('\n'"Click somewhere in the plot to continue...")
plt.waitforbuttonpress()