Transverse shear stresses#
Laminate.calc_transverse_shear_stress() gives the transverse shear
stresses at a height \(z\) for given shear forces \(Q_y\), \(Q_x\), with the
equilibrium distribution that defines Ats for
shear_correction='rohwer', see Choosing the transverse shear correction. The stresses
vanish at the faces, integrate to the shear forces, and their complementary
energy is \(Q^T A_{ts}^{-1} Q / 2\):
def test_example_shear_stress_cross_ply():
# [0/90/0] CFRP plate, SI units, under the shear force Qx only
laminaprop = (142e9, 8.7e9, 0.28, 5.1e9, 5.1e9, 3.2e9)
lam = laminated_plate([0, 90, 0], plyt=0.5e-3, laminaprop=laminaprop)
h = lam.h
Qy, Qx = 0., 1e3 # N/m
zs = np.linspace(-h/2, h/2, 31)
tau = np.array([lam.calc_transverse_shear_stress(z, Qy, Qx) for z in zs])
tau_yz, tau_xz = tau[:, 0], tau[:, 1]
tol = 1e-10*Qx/h
# traction-free faces
assert abs(tau_xz[0]) < tol and abs(tau_xz[-1]) < tol
# orthotropic plies in the laminate axes: Qx gives no tau_yz
assert np.allclose(tau_yz, 0, atol=tol)
# the stresses integrate to the shear forces, and their complementary
# energy defines Ats: 1/2 Q^T Ats^-1 Q = int 1/2 tau^T Cs^-1 tau dz
Q = np.array([Qy, Qx])
xi, w = np.polynomial.legendre.leggauss(3)
zk = -h/2
resultant = np.zeros(2)
Ustar = 0.
for ply in lam.plies:
Cs = np.asarray(ply.get_constitutive_matrix())[3:, 3:]
for x, wi in zip(xi, w):
z = zk + ply.h*(x + 1)/2
t = np.array(lam.calc_transverse_shear_stress(z, Qy, Qx))
resultant += wi*ply.h/2*t
Ustar += wi*ply.h/2*0.5*t @ np.linalg.solve(Cs, t)
zk += ply.h
assert np.allclose(resultant, Q, rtol=1e-12, atol=1e-12*Qx)
assert np.isclose(Ustar, 0.5*Q @ np.linalg.solve(lam.Ats, Q), rtol=1e-12)
# the constant-strain stiffness overestimates the shear stiffness
assert lam.scf_k13 < 1
For a homogeneous plate the distribution is the parabola \(\tau_{xz} = 3 Q_x/(2h) (1 - 4 \bar{z}^2/h^2)\), with \(\bar{z}\) measured from the mid-surface, for any offset:
@pytest.mark.parametrize('offset', [0., +0.37, -1.5])
def test_isotropic_parabola(offset):
h = 3.
lam = isotropic_plate(thickness=h, E=70e3, nu=0.3, offset=offset)
Qx, Qy = 7.3, -2.1
tol = 1e-13*3/(2*h) # machine precision relative to the peak stress
for z in np.linspace(-h/2, h/2, 21) + offset:
zbar = z - offset
tau_yz, tau_xz = lam.calc_transverse_shear_stress(z, Qy, Qx)
assert abs(tau_xz - 3*Qx/(2*h)*(1 - 4*zbar**2/h**2)) <= tol*abs(Qx)
assert abs(tau_yz - 3*Qy/(2*h)*(1 - 4*zbar**2/h**2)) <= tol*abs(Qy)
# no Qy contribution in tau_xz, and no Qx contribution in tau_yz
assert abs(lam.calc_transverse_shear_stress(z, 1., 0.)[1]) <= tol
assert abs(lam.calc_transverse_shear_stress(z, 0., 1.)[0]) <= tol
From the strain gradients of an actual FSDT solution,
Laminate.calc_equilibrium_transverse_shear() recovers the stresses by
integrating the equilibrium equations through the thickness, and
Laminate.calc_aposteriori_energy() gives their strain energies, here
for a homogeneous plate in cylindrical bending, recovering the factor 5/6:
def test_aposteriori_energy_homogeneous():
r"""Homogeneous plate in cylindrical bending: parabola, energy
3/5 Qx^2/(G h), i.e. k = 5/6 with Eq. (8) of Noor and Peters (1989)"""
h, E, nu = 2., 70e3, 0.3
G = E/(2*(1 + nu))
lam = isotropic_plate(thickness=h, E=E, nu=nu)
Qx = 1.7
D = E*h**3/(12*(1 - nu**2))
grad_x = np.zeros(6)
# M_xx,x = Qx with the plane strain curvature of cylindrical bending
grad_x[3] = Qx/D
U13, U23, Qy_r, Qx_r = lam.calc_aposteriori_energy(grad_x, np.zeros(6))
assert np.isclose(Qx_r[0], Qx, rtol=1e-12)
assert np.isclose(U13[0], 0.6*Qx**2/(G*h), rtol=1e-12)
assert np.isclose(0.5*Qx**2/(G*h)/U13[0], 5/6, rtol=1e-12)
assert abs(U23[0]) < 1e-20