Universidade Federal do Rio Grande do Sul (UFRGS)
Programa de Pós-Graduação em Engenharia Civil (PPGEC)
1. Natural vibration modes and frequency
1.1. The general solution for free vibration
1.2. Natural vibration modes and frequencies
1.3. Orthogonality of vibration modes
2. Examples of modal properties assessment
2.1. Example 1: steel plane truss
2.2. Example 2: beam element
2.3. Example 3: experimental 3-dof model
3. Structural response to initial conditions
4. Assignment
Prof. Marcelo M. Rocha, Dr.techn. (ORCID)
Porto Alegre, RS, Brazil
# Importing Python modules required for this notebook
# (this cell must be executed with "shift+enter" before any other Python cell)
import numpy as np
import matplotlib.pyplot as plt
import pickle as pk
import scipy.linalg as sc
from MRPy import MRPy
# Load matrices generated in Class 10 (that notebook must be run firstly!)
with open('resources/data/sample_KM.pk', 'rb') as target:
K1, M1, K2, M2, K3, M3 = pk.load(target)
#target = open('resources/data/sample_KM.pk', 'rb')
#K1, M1, K2, M2, K3, M3 = pk.load(target)
#close(target)
Once the stiffness and mass matrices are defined for a given structure, the undamped equilibrium matrix equation results to be a set of coupled equilibrium equations, each one for one of the degrees of freedom. In matrix forms it reads:
M¨→u+K→u=→F(t)where →F(t) is the (time dependent) external loads vector. In case of free vibration we have:
M¨→u+K→u=→0Let us now assume that there is a solution →uk(t) such that:
→uk(t)=uk(t)→φkwhere →φk is not time dependent. This assumption may be understood as a separation of time and space dependence of →uk(t). Now the acceleration vector results to be:
¨→uk(t)=¨uk(t)→φkand the free vibration equation becomes:
¨uk(t)M→φk+uk(t)K→φk=→0Premultiplying this equation by K−1 and dividing by uk(t) results:
¨uk(t)uk(t)D→φk+I→φk=→0where I is the identity matrix and D=K−1M is called the system dynamic matrix. Recalling that →φk is not time dependent implies that the equation above is only valid if the coefficient of matrix D is constant in time. We denote this constant quotient as −ω2k and the condition becomes:
¨uk(t)+ω2kuk(t)=0The solution for this equation has the general form:
uk(t)=uk0sin(ωkt+θk)which is the same form found for a single degree of freedom system. However, the time function uk(t) is only part of the solution for →u(t), corresponding to its time dependent amplitude. There is still the need of finding the time independent vector, →φk, and the free vibration frequency, ωk.
The general amplitude solution above implies that the acceleration vector is:
¨→uk(t)=−ω2kuk0sin(ωkt+θk)→φkReplacing this result in the free vibration equation and simplifying gives:
K→φk=ω2kM→φkor, alternativelly, with the dynamic matrix:
D→φk=λk→φkwith λk=1/ω2k. The two equations above represent an eigenvalue-eigenvector problem, which has as many solutions as the matrices order, N, which is also the number of system degrees of freedom. Each solution is a pair (ωk,→φk) or, alternatively, (λk,→φk) if the dynamic matrix is used.
The eigenvalues ωk are called the natural vibration frequencies of the strutural system, while the eigenvectors →φk are called the vibration modes, or modal shapes. It is very important to keep in mind that the modal shapes have no prespecified scale, what means that they can be multiplied by any scale factor, α, and still remain solutions for the eigenproblem:
K(α→φk)=ω2kM(α→φk)Numerical algorithms for solving this eigenproblem are available in many
environments, including the best models of HP handheld calculators.
In Python, they are available in scipy.linalg
module and will be
used in section 2 for the three examples provided in the previous class.
The eigenvectors →φk presents the important property of orthogonality with respect to the stiffness and to the mass matrix. This is a direct consequence of their symmetry, as shown in the following. Let us start by considering two vibration modes i and j that are solutions for the eigenproblem:
M→φi=λiK→φiM→φj=λjK→φjTransposing the equation for mode i above and recognizing that M=M⊺ and K=K⊺ gives:
→φ⊺iM=λi→φ⊺iKNow, postmultiplying by →φj gives:
→φ⊺iM→φj=λi→φ⊺iK→φjOn the other hand, the eigenproblem for mode j above can be premultiplied by →φ⊺i to give:
→φ⊺iM→φj=λj→φ⊺iK→φjSubtracting this last equation from the previous one results in:
(λi−λj)→φ⊺iK→φj=0This condition can be satisfied if and only if:
→φ⊺iK→φj=0,fori≠jStarting again this demonstration with the j eigenproblem solution leads also to:
→φ⊺iM→φj=0,fori≠jThese are the two orthogonality conditions for the eigenvectors →φk. In the next class they will be used to decouple the matrix equilibrium equation into a set of scalar equations, one for each vibration mode. Once this orthogonality condition has been stated, we observe that the eigenvectors →φk constitutes a base of independent vectors (of a linear vector space) that can be linearly combined to represent the complete system response as:
→u(t)=N∑k=1uk(t)→φk=Φ→uk(t)where Φ is the modal matrix, whose columns are the the eigenvectors →φk.
In the following sections, each of the three examples presented in the last class are subjected to a modal analysis and the corresponding solutions, for both natural frequencies and associated vibration modes, are plotted for visualization.
The eigenvalues and eigenvectors are solved with scipy
function eig
from module linalg
. We define a general function to return natural
vibration frequencies, and the associated vibration modes, in ascending order:
def vibration_modes(K, M):
# Uses scipy to solve the standard eigenvalue problem
w2, Phi = sc.eig(K, M)
# Ensure ascending order of eigenvalues
iw = w2.argsort()
w2 = w2[iw]
Phi = Phi[:,iw]
# Eigenvalues to vibration frequencies
wk = np.sqrt(np.real(w2))
fk = wk/2/np.pi
return fk, wk, Phi
For the steel truss presented last class this is done as follows:
fk1, wk1, Phi1 = vibration_modes(K1, 2*M1)
The script below shows the results as nodal displacements at the truss top.
f1 = plt.figure(1, figsize=(12,10), clear=True)
x = np.arange(0, 14, 2)
for k in range(5):
qk = np.zeros(7)
qk[1:-1] = Phi1[:,k]
qk /= np.max(np.abs(qk)) # adjust scale for unity amplitude
plt.subplot(5,1,k+1)
plt.plot(x, qk)
plt.xlim( 0.0, 12.0);
plt.ylim(-1.5, 1.5); plt.ylabel(str(k+1));
plt.text(10, 1, 'fk = {0:3.1f}Hz'.format(fk1[k]));
plt.grid(True)
plt.xlabel('x');
The interpolation functions could not be dumped with pickle
, so we must re-create
them for visualizing the modal shapes:
# Beam length discretization
L = 1
# Defining a list of lambda functions
phi = []
phi.append(lambda xi: 1 - 3*xi*xi + 2*xi*xi*xi)
phi.append(lambda xi: L*(xi - 2*xi*xi + xi*xi*xi))
phi.append(lambda xi: 3*xi*xi - 2*xi*xi*xi)
phi.append(lambda xi: L*(-xi*xi + xi*xi*xi ))
Furthermore, the stiffness matrix is not positive definite, for no boundary conditions have been applied so far (it is a bar "floating in space"). It is necessary to restrain at least two degrees of freedom to suppress a free body motion. For instance, to model a cantilever beam we can restrain u1=0 and u2=0, what implies that the first two rows and two columns of K and M can be removed:
K2 = K2[2:,2:]
M2 = M2[2:,2:]
Now the eigenvalues problem can be solved:
fk2, wk2, Phi2 = vibration_modes(K2, M2)
print(Phi2)
[[-0.58747258 0.13007598] [-0.80924407 0.99150403]]
For the visualization below, the vibration modes are a linear combination of the interpolation functions (for the remaining degrees of freedom), each one multiplied by the resulting eingenvector coordinate.
f2 = plt.figure(2, figsize=(12,6))
x = np.linspace(0, 1, 200)
for k in range(2):
qk = Phi2[:,k]
px = np.zeros(x.shape)
for km in range(2):
px += qk[km]*phi[km+2](x) # superpose interpolations
px /= np.max(np.abs(px)) # adjust scale for unity amplitude
plt.subplot(2,1,k+1)
plt.plot(x, px)
plt.xlim( 0.0, 1.0);
plt.ylim(-1.5, 1.5); plt.ylabel(str(k+1));
plt.grid(True)
plt.text(0.8, 1.0, 'fk = {0:4.2f}Hz'.format(fk2[k]));
plt.xlabel('x');
For the experimental model we get:
fk3, wk3, Phi3 = vibration_modes(K3, M3)
D3 = np.matmul(np.linalg.inv(K3),M3)
print(D3)
print(Phi3)
[[0.00051329 0.0003422 0.0001711 ] [0.0003422 0.0003422 0.0001711 ] [0.0001711 0.0001711 0.0001711 ]] [[ 0.73697623 0.59100905 -0.32798528] [ 0.59100905 -0.32798528 0.73697623] [ 0.32798528 -0.73697623 -0.59100905]]
f3 = plt.figure(3, figsize=(12,8))
x = np.arange(4)
for k in range(3):
qk = np.zeros(4)
qk[1:] = Phi3[::-1,k]
qk /= np.max(np.abs(qk)) # adjust scale for unity amplitude
plt.subplot(1,3,k+1)
plt.plot(qk, x, 'bo')
plt.plot(qk, x)
plt.xlim(-1.5, 1.5); plt.ylabel(str(k+1));
plt.ylim( 0.0, 3.5);
plt.title('fk = {0:3.1f}Hz'.format(fk3[k]));
plt.grid(True)
![]() |
![]() |
![]() |
f1=5.4Hz | f2=15.2Hz | f3=22.9Hz |
Let us recall the free vibration solution for mode k, previously stated:
uk(t)=uk0sin(ωkt+θk)As stated before, the complete solution will be a superposition of solutions for all modes:
→u(t)=N∑k=1uk(t)→φk=N∑k=1uk0sin(ωkt+θk)→φkwhere N is the number of degrees of freedom (length of vector →φk). Deriving the equation above with respect to time gives the corresponding instantaneous velocity:
˙→u(t)=N∑k=1uk0ωkcos(ωkt+θk)→φkNow we provide the initial conditions →u0 and →v0 for time t=0:
→u0=→u(0)=N∑k=1uk0sin(θk)→φk→v0=˙→u(0)=N∑k=1uk0ωkcos(θk)→φkTo separate the conditions equation for each mode, we create the following scalar quantities:
→φ⊺iM→u0=→φ⊺iMN∑k=1uk0sin(θk)→φk=ui0sin(θi)→φ⊺iM→φi→φ⊺iM→v0=→φ⊺iMN∑k=1uk0ωkcos(θk)→φk=ui0ωicos(θi)→φ⊺iM→φiDividing the two expressions above yields the phase angle of each modal solution, θi,
tan(θi)=ωi(→φ⊺iM→u0→φ⊺iM→v0)which can be used to calculate the corresponding amplitudes ui0:
ui0sin(θi)=(→φ⊺iM→u0→φ⊺iM→φi)ou:
ui0cos(θi)=1ωi(→φ⊺iM→v0→φ⊺iM→φi)We recall that the scalar quantities →φ⊺iM→φi, in the equations above, are the so-called modal masses, Mi. Furthermore, observe that special care must be taken for zero initial velocity, what gives infinity for tan(θi) implying that θi=π/2.
As an example, let us calculate the free vibration response of the 3-dof experimental model subjected to a small displacement of 5mm in the top mass only. We start by calculating the modal masses, Mi and the scalar quantities →φ⊺iM→u0 and →φ⊺iM→v0:
-Phi3[:,0]/10
array([0.07369762, 0.0591009 , 0.03279853])
u0 = np.array([[0.000, 0.000, 0.000]]).T # column vector with the initial displacements
v0 = np.array([[1.000, 0.000, 0.000]]).T # column vector with the initial velocities
Mi = np.dot(np.dot(Phi3.T, M3), Phi3) # modal mass
Mi = np.diag(Mi)
print(Mi)
qMu0 = np.dot(np.dot(Phi3.T, M3), u0)
qMv0 = np.dot(np.dot(Phi3.T, M3), v0)
[0.33 0.33 0.33]
Then we calculate the free vibration properties ui0 and θi:
thi = np.zeros_like(Mi)
u0i = np.zeros_like(Mi)
for k in range(3):
# If there are initial displacements only
# thi[k] = -np.pi/2
# u0i[k] = qMu0[k]/Mi[k]/np.sin(thi[k])
# If there are initial velocities only
thi[k] = np.arctan(wk3[k]*qMu0[k]/qMv0[k])
u0i[k] = qMv0[k]/Mi[k]/np.cos(thi[k])/wk3[k]
print('Mode {0} with phase {1:7.4f}rad and amplitude {2:6.2f}mm'.format(k+1, thi[k], 1000*u0i[k]))
Mode 1 with phase 0.0000rad and amplitude 21.66mm Mode 2 with phase 0.0000rad and amplitude 6.20mm Mode 3 with phase -0.0000rad and amplitude -2.38mm
Finally we superpose the modal responses and get the nodal displacements in free vibration:
# Build the modal responses as harmonic functions with given properties
uk = MRPy.harmonic(NX=3, N=2048, fs=512, X0=u0i, f0=fk3, phi=thi)
f5 = uk.plot_time(5, figsize=(8,6), axis_t=(0, 1, -0.04, 0.04))
#uk.plot_time(4, figsize=(8,6), axis_t=(0, 1, -0.005, 0.005))
# Calculate the NODAL responses superposing all modal responses
uN = MRPy(np.dot(Phi3, uk), fs=512)
f4 = uN.plot_time(4, figsize=(8,6), axis_t=(0, 1, -0.04, 0.04))
We can see that the system response contains all natural 3 system natural frequencies, as can be confirmed by taking a look at the periodograms:
f5 = uN.plot_freq(5, figsize=(8,6), axis_f=(0, 30, 0.0, 5e-4))
print(fk3)
[ 5.41499969 15.17249196 21.92488612]
Prazo: 10 de maio de 2021.
data = (wk1, Phi1, wk2, Phi2, wk3, Phi3)
with open('resources/data/sample_VM.pk', 'wb') as target:
pk.dump(data, target)
#with open('resources/data/sample_VM.pk','rb') as target:
# wk1, Phi1, wk2, Phi2, wk3, Phi3 = pk.load(target)