Linear buckling analysis of a column#
A clamped-free column of BeamC or BeamLR elements,
along an arbitrary direction in space, under a compressive load at its free
end. Each bending plane buckles at its own Euler load, \(\pi^2 E I/(4
L^2)\). The geometric stiffness matrix is assembled with update_KG from
the linear static solution of a unit load, and the eigenvalue problem is
solved here with scipy.linalg.eigh(), see Linear buckling and natural frequencies
for linear_buckling():
def buckling_loads(name, b, h, direction, vxy):
"""Two lowest buckling loads, and the Euler loads of both planes"""
cls, probecls, datacls, num_elements = BEAMS[name]
L = 3.
E = 203.e9
prop = BeamProp()
prop.A = b*h
prop.E = E
prop.G = 5/6*E/2/1.3
prop.Izz = b*h**3/12
prop.Iyy = b**3*h/12
prop.J = prop.Izz + prop.Iyy
direction = np.asarray(direction, dtype=float)
direction /= np.linalg.norm(direction)
s = np.linspace(0, L, num_elements + 1)
x = (s[:, None]*direction[None, :]).flatten()
N = DOF*(num_elements + 1)
data = datacls()
probe = probecls()
KC0r = np.zeros(data.KC0_SPARSE_SIZE*num_elements, dtype=INT)
KC0c = np.zeros(data.KC0_SPARSE_SIZE*num_elements, dtype=INT)
KC0v = np.zeros(data.KC0_SPARSE_SIZE*num_elements, dtype=DOUBLE)
KGr = np.zeros(data.KG_SPARSE_SIZE*num_elements, dtype=INT)
KGc = np.zeros(data.KG_SPARSE_SIZE*num_elements, dtype=INT)
KGv = np.zeros(data.KG_SPARSE_SIZE*num_elements, dtype=DOUBLE)
beams = []
for i in range(num_elements):
beam = cls(probe)
beam.n1, beam.n2 = i + 1, i + 2
beam.c1, beam.c2 = DOF*i, DOF*(i + 1)
beam.init_k_KC0 = i*data.KC0_SPARSE_SIZE
beam.init_k_KG = i*data.KG_SPARSE_SIZE
beam.update_rotation_matrix(vxy[0], vxy[1], vxy[2], x)
beam.update_probe_xe(x)
beam.update_KC0(KC0r, KC0c, KC0v, prop)
beams.append(beam)
KC0 = coo_matrix((KC0v, (KC0r, KC0c)), shape=(N, N)).toarray()
bk = np.zeros(N, dtype=bool)
bk[:DOF] = True
bu = ~bk
# unit compressive load at the tip
fext = np.zeros(N)
fext[-DOF:-DOF + 3] = -direction
u = np.zeros(N)
u[bu] = np.linalg.solve(KC0[np.ix_(bu, bu)], fext[bu])
for beam in beams:
beam.update_probe_ue(u)
beam.update_KG(KGr, KGc, KGv, prop)
KG = coo_matrix((KGv, (KGr, KGc)), shape=(N, N)).toarray()
# KC0*phi = -lambda*KG*phi, solved as -KG*phi = mu*KC0*phi, lambda = 1/mu
mu = eigh(-KG[np.ix_(bu, bu)], KC0[np.ix_(bu, bu)], eigvals_only=True)
loads = np.sort(1/mu[mu > 1.e-12])
euler_z = pi**2*E*prop.Izz/(4*L**2)
euler_y = pi**2*E*prop.Iyy/(4*L**2)
return loads[:2], min(euler_z, euler_y), max(euler_z, euler_y)
@pytest.mark.parametrize('name', sorted(BEAMS))
@pytest.mark.parametrize('b, h', [(0.05, 0.05), (0.10, 0.05)])
@pytest.mark.parametrize('direction, vxy', [((1, 0, 0), (0, 1, 0)),
((1, 2, 3), (0, 0, 1))])
def test_column_buckles_at_the_euler_load(name, b, h, direction, vxy):
loads, euler_weak, euler_strong = buckling_loads(name, b, h, direction,
vxy)
assert np.isclose(loads[0], euler_weak, rtol=0.01)
# square section: the second plane buckles at the same load; rectangular
# section: the next load is the lower of the Euler load of the strong
# plane and the second mode of the weak plane, at nine times its load
expected = euler_weak if b == h else min(euler_strong, 9*euler_weak)
assert np.isclose(loads[1], expected, rtol=0.01)