Natural frequency analysis of a plate#
A simply supported plate of Quad4 elements, whose lowest natural
frequency is compared with the analytical one, with the matrices of
plate_matrices, see Linear buckling analysis of a plate. The mass matrix is
assembled with update_M and the frequencies are computed with
natural_frequency(), with and without the equilibration of the
diagonal:
def test_natural_frequency_plate():
r"""End-to-end against the natural frequency of a simply supported
plate"""
KC0uu, KGuu, Muu = plate_matrices(with_mass=True)
omegan, eigvecs = natural_frequency(KC0uu, Muu, num_eigvalues=4)
Dp = 2*H_PLATE**3*E_PLATE/(3*(1 - NU_PLATE**2))
wmn = (1./A_PLATE**2 + 1./B_PLATE**2)*np.sqrt(
Dp*np.pi**4/(2*RHO_PLATE*H_PLATE))/2
print('omegan[0] %.4f theory %.4f' % (omegan[0], wmn))
assert np.isclose(wmn, omegan[0], rtol=0.05)
assert np.all(np.diff(omegan) >= -1e-9)
# NOTE same frequencies without the scaling
omegan_raw, _ = natural_frequency(KC0uu, Muu, num_eigvalues=4,
precondition=False)
print('preconditioned', omegan[:3], 'raw', omegan_raw[:3])
assert np.allclose(omegan, omegan_raw, rtol=1e-6)
The consistent, lumped and reduced-integration mass matrices of
Quad4, mtype 0, 1 and 2 of update_M:
def test_nat_freq_plate(plot=False, mode=0, mtypes=range(3), refinement=1):
data = Quad4Data()
probe = Quad4Probe()
for mtype in mtypes:
nx = refinement*9
ny = refinement*11
a = 0.3
b = 0.5
E = 203.e9 # Pa
nu = 0.33
rho = 7.83e3 # kg/m3
h = 0.01 # m
xtmp = np.linspace(0, a, nx)
ytmp = np.linspace(0, b, ny)
xmesh, ymesh = np.meshgrid(xtmp, ytmp)
ncoords = np.vstack((xmesh.T.flatten(), ymesh.T.flatten(), np.zeros_like(ymesh.T.flatten()))).T
x = ncoords[:, 0]
y = ncoords[:, 1]
z = ncoords[:, 2]
ncoords_flatten = ncoords.flatten()
nids = 1 + np.arange(ncoords.shape[0])
nid_pos = dict(zip(nids, np.arange(len(nids))))
nids_mesh = nids.reshape(nx, ny)
n1s = nids_mesh[:-1, :-1].flatten()
n2s = nids_mesh[1:, :-1].flatten()
n3s = nids_mesh[1:, 1:].flatten()
n4s = nids_mesh[:-1, 1:].flatten()
num_elements = len(n1s)
print('num_elements', num_elements)
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)
Mr = np.zeros(data.M_SPARSE_SIZE*num_elements, dtype=INT)
Mc = np.zeros(data.M_SPARSE_SIZE*num_elements, dtype=INT)
Mv = np.zeros(data.M_SPARSE_SIZE*num_elements, dtype=DOUBLE)
N = DOF*nx*ny
prop = isotropic_plate(thickness=h, E=E, nu=nu, rho=rho)
quads = []
init_k_KC0 = 0
init_k_M = 0
for n1, n2, n3, n4 in zip(n1s, n2s, n3s, n4s):
pos1 = nid_pos[n1]
pos2 = nid_pos[n2]
pos3 = nid_pos[n3]
pos4 = nid_pos[n4]
r1 = ncoords[pos1]
r2 = ncoords[pos2]
r3 = ncoords[pos3]
normal = np.cross(r2 - r1, r3 - r2)[2]
assert normal > 0
quad = Quad4(probe)
quad.n1 = n1
quad.n2 = n2
quad.n3 = n3
quad.n4 = n4
quad.c1 = DOF*nid_pos[n1]
quad.c2 = DOF*nid_pos[n2]
quad.c3 = DOF*nid_pos[n3]
quad.c4 = DOF*nid_pos[n4]
quad.init_k_KC0 = init_k_KC0
quad.init_k_M = init_k_M
quad.K6ROT = 100.
quad.update_rotation_matrix(ncoords_flatten)
quad.update_probe_xe(ncoords_flatten)
quad.update_KC0(KC0r, KC0c, KC0v, prop)
quad.update_M(Mr, Mc, Mv, prop, mtype=mtype)
quads.append(quad)
init_k_KC0 += data.KC0_SPARSE_SIZE
init_k_M += data.M_SPARSE_SIZE
print('elements created')
KC0 = coo_matrix((KC0v, (KC0r, KC0c)), shape=(N, N)).tocsc()
M = coo_matrix((Mv, (Mr, Mc)), shape=(N, N)).tocsc()
print('sparse KC0 and M created')
bk = np.zeros(N, dtype=bool)
check = np.isclose(x, 0.) | np.isclose(x, a) | np.isclose(y, 0) | np.isclose(y, b)
bk[0::DOF] = check
bk[1::DOF] = check
bk[2::DOF] = check
bu = ~bk
KC0uu = KC0[bu, :][:, bu]
Muu = M[bu, :][:, bu]
num_eigenvalues = max(2, mode+1)
print('eig solver begin')
# solves Ax = lambda M x
# we have Ax - lambda M x = 0, with lambda = omegan**2
# NOTE pyfe3d.solver.natural_frequency equilibrates the diagonal
# before calling the eigensolver and returns the circular
# frequencies already sorted
omegan, eigvecsu = natural_frequency(KC0uu, Muu, num_eigvalues=num_eigenvalues, tol=1e-6)
print('eig solver end')
eigvecs = np.zeros((N, eigvecsu.shape[1]), dtype=float)
eigvecs[bu, :] = eigvecsu
u = np.zeros(N)
u[bu] = eigvecsu[:, mode]
# theoretical reference
m = 1
n = 1
D = 2*h**3*E/(3*(1 - nu**2))
wmn = (m**2/a**2 + n**2/b**2)*np.sqrt(D*np.pi**4/(2*rho*h))/2
print('Theoretical omega123', wmn)
print('Numerical omega123', omegan[0:10])
assert np.isclose(wmn, omegan[0], rtol=0.05)
if plot:
import matplotlib.pyplot as plt
plt.clf()
plt.contourf(xmesh, ymesh, u[2::DOF].reshape(nx, ny).T)
plt.show()