A vibrating string

Consider the motion of a frictionless vibrating string tethered at $x=0$ and $x=L$. The motion is given by the differential equation $$ \frac{\partial^2 u}{\partial t^2} c^2 = \frac{\partial^2 u}{\partial x^2} $$ where $u$ is the displacement perpendicular to $x$ and $c$ is a characteristic velocity that depends on the tension and mass per unit length of the string. This secound order differential equation can be solved by seperation of variables, and for a given initial waveform $f(x)$ solutions can be written as $$ u(x,t)=\sum_n A_n \phi(x)\cos(\omega t) $$ where $n=1,2,3,\ldots$, $\omega=n\pi c /L$, $$ \phi(x)=\sin(n\pi x/L) $$ and $$ A_n = \frac{2}{L}\int_0^L f(x)\phi_n(x)dx. $$ The tethered of the string dictates that $f(0)=f(L)=0$.

Slowmotion video of a violin string

Bowed violin string in slow motion

In [1]:
# Import modules
import numpy as np
import matplotlib.pyplot as plt
import IPython.display as ipd

# Initialize global variables
L=0.65 # m
c=2*220*L # 220 is the frequency of the first harmonic 
print('L =',L,'m ;','c =',c,'m/s')

dx=L/1000
x=np.arange(0, L, dx)

f=x**4*(L-x)
f/=np.max(f)

plt.plot(x,f)
plt.title('Initial waveform')
plt.xlabel('x [m]')
plt.ylabel('f=u(x,t=0)')
plt.show()
L = 0.65 m ; c = 286.0 m/s
<matplotlib.figure.Figure at 0x7fe3aff08f60>
In [2]:
# Return the "standing wave" solutions
def phi(n=1):
    return np.sin(n*np.pi*x/L)

An=[]
nmax=30
for n in range(1,nmax+1):
    An.append(2*np.trapz(f*phi(n=n)/L,x))

print('An =',An)
plt.plot(An)
plt.title("$A_n$ expansion coefficients")
plt.show()
An = [0.5550120401769395, -0.48821828055517213, 0.20966269378678962, -0.08907472538600418, 0.048554905489873207, -0.027931465414884254, 0.018023034869436967, -0.012010823164268315, 0.0085435017731059805, -0.0062033925304357841, 0.0046969614953562095, -0.0036068547132234001, 0.0028516790601695275, -0.0022777999995716516, 0.0018588439366134242, -0.0015287429356194045, 0.001278089104876643, -0.0010750308007301257, 0.00091605420726765564, -0.00078439919925618606, 0.00067877141947391361, -0.00058972081206217462, 0.00051682983983811128, -0.00045446370551167382, 0.00040255591901209534, -0.00035758788284556871, 0.00031962846181962483, -0.00028639372795585642, 0.00025799729007079794, -0.00023290698130371858]
In [3]:
def wave(t=0):
    u=0*x;
    n=0
    for A in An:
        n+=1
        omega=n*np.pi*c/L
        u += A*phi(n=n)*np.cos(omega*t)
    return u

# Show wave at different times
for i in range(17):
    rate=10000
    t=i/rate
    plt.plot(x,wave(t=t),label=' t=' + str(t) +' s')

plt.xlabel('x [m]')
plt.ylabel('u')
#plt.legend()
plt.show()
In [4]:
# Save animation as movie
import matplotlib.animation as animation

#fig, ax = plt.subplots(dpi=150)
fig, ax = plt.subplots()

line, = ax.plot(x, wave(t=0))

plt.xlabel('x [m]')
plt.ylabel('u')
plt.ylim(-1,1)

def init():  # only required for blitting to give a clean slate.
    line.set_ydata([np.nan] * len(x))
    return line,


def animate(i):
    line.set_ydata(wave(t=i/22000))  # update the data.
    return line,

ani = animation.FuncAnimation(fig, animate, init_func=init, interval=1, blit=True, save_count=300)

from matplotlib.animation import FFMpegWriter
writer = FFMpegWriter(fps=30, metadata=dict(artist='Ulf R. Pedersen (http://urp.dk)'), bitrate=3000)
ani.save("string.mp4", writer=writer)

ipd.Video("string.mp4")
Out[4]:
In [5]:
# Write audiofile with sound
import scipy.io.wavfile
rate=44100
secounds=10.0
t=np.arange(0,secounds,1/rate)
sound=0*t

n=0
for A in An:
    n=n+1
    omega=n*np.pi*c/L
    sound += A*np.sin(omega*t)
sound/=1.2*np.max(sound)
scipy.io.wavfile.write('sound.wav',rate=rate,data=sound)
ipd.Audio('sound.wav')
Out[5]:
In [11]:
ipd.Video(data="./string.webm")
Out[11]: