Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Fourier series are a method we can use to solve inhomogeneous 2nd-order ODEs of the form

y+p(t)y+q(t)y=r(t)  ,y^{\prime\prime} + p(t) y^{\prime} + q(t) y = r(t) \;,

where the forcing function r(t)r(t) is periodic. This means looking like one of these examples:

# import libraries for numerical functions and plotting
import numpy as np
import matplotlib.pyplot as plt
# these lines are only for helping improve the display
import matplotlib_inline.backend_inline
matplotlib_inline.backend_inline.set_matplotlib_formats('pdf', 'png')
plt.rcParams['figure.dpi']= 300
plt.rcParams['savefig.dpi'] = 300
fig, axes = plt.subplots(3, 1)

# periodic square wave
square_wave = [1, 1, 1, 1, 0, 0, 0, 0]*10
time = np.linspace(0, 1, len(square_wave))
axes[0].plot(time, square_wave)
axes[0].grid(True)

# sin wave
time = np.linspace(0, 1, 100)
axes[1].plot(time, np.sin(time*10*np.pi))
axes[1].grid(True)

# sawtooth
y_vals = ((np.fmod(time, 2*np.pi/40)/(np.pi*2/40))*2)-1
axes[2].plot(time, y_vals)
axes[2].grid(True)

plt.tight_layout()
plt.show()
#plot(t, y); ylim([-1.5, 1.5]);
Loading...

(Actually, as we’ll see later, we can use a Fourier series to represent generic forcing function!)

Fourier series have been around a while, ever since in 1790 Jean-Baptiste Joseph Fourier found that generic periodic functions could be represented by a sum of series of sin and cos functions, harmonically related by frequency.

In general, a Fourier series represents a function f(t)f(t) by

f(t)=a0+n=1ancos(nωt)+n=1bnsin(nωt)f(t) = a_0 + \sum_{n=1}^{\infty} a_n \cos (n \omega t) + \sum_{n=1}^{\infty} b_n \sin (n \omega t)

where a0a_0, ana_n, and bnb_n are the Fourier coefficients, ω=2πT\omega = \frac{2\pi}{T} is the frequency of the function f(t)f(t), and TT is the period. nn is an integer used as an index.

Properties of Fourier Series

Considering that nn is an integer, and the sine and cosine components of a Fourier series share the same fundamental frequency ω\omega, Fourier series have some useful properties:

  1. The integral of each component trigonometric function over the period is zero:

0Tsin(nωt)dt=0=0Tcos(nωt)dt\int_0^T \sin (n \omega t) dt = 0 = \int_0^T cos (n \omega t) dt
  1. The component trigonometric functions are orthogonal over their period:

0Tcos(nωt)sin(mωt)dt=0\int_0^T \cos(n \omega t) \sin (m \omega t) dt = 0

for all values of n,m=1,2,,n, m = 1, 2, \ldots, \infty.

  1. The component trigonometric functions are also orthogonal with themselves over their period:

0Tcos(nωt)cos(mωt)dt={0mnT2m=n0Tsin(nωt)sin(mωt)dt={0mnT2m=n\begin{align} \int_0^T \cos (n \omega t) \cos (m \omega t) dt &= \begin{cases}0 \quad m \neq n \\ \frac{T}{2} \quad m = n \end{cases} \\ % \int_0^T \sin (n \omega t) \sin (m \omega t) dt &= \begin{cases}0 \quad m \neq n \\ \frac{T}{2} \quad m = n \end{cases} \end{align}

for all values of n,m=1,2,,n, m = 1, 2, \ldots, \infty.

Fourier coefficients

We can use the above properties to calculate the Fourier coefficients, given a periodic function f(t)f(t). First, recall

f(t)=a0+n=1ancos(nωt)+n=1bnsin(nωt)f(t) = a_0 + \sum_{n=1}^{\infty} a_n \cos (n \omega t) + \sum_{n=1}^{\infty} b_n \sin (n \omega t)
  1. To calculate a0a_0, integrate both sides of the equation over the period:

0Tf(t)dt=0Ta0dt+0T(n=1ancos(nωt))dt+0T(n=1bnsin(nωt))dt\int_0^T f(t) dt = \int_0^T a_0 dt + \int_0^T \left( \sum_{n=1}^{\infty} a_n \cos (n \omega t) \right)dt + \int_0^T \left( \sum_{n=1}^{\infty} b_n \sin (n \omega t) \right) dt

For the integrals of the infinite sums, recall that the integral of the sum of some functions is the same as the sum of the integrals of the functions: (a+b+c)=a+b+c\int (a + b + c) = \int a + \int b + \int c. That means that

0T(n=1ancos(nωt))dt=0Ta1cos(ωt)dt+0Ta2cos(2ωt)dt+=0  , and0T(n=1bnsin(nωt))dt=0Tb1sin(ωt)dt+0Tb2sin(2ωt)dt+=0  ,\begin{align} \int_0^T \left( \sum_{n=1}^{\infty} a_n \cos (n \omega t) \right)dt = \int_0^T a_1 \cos (\omega t) dt + \int_0^T a_2 \cos (2 \omega t) dt + \ldots &= 0 \;, \text{ and} \\ \int_0^T \left( \sum_{n=1}^{\infty} b_n \sin (n \omega t) \right)dt = \int_0^T b_1 \sin (\omega t) dt + \int_0^T b_2 \sin (2 \omega t) dt + \ldots &= 0 \;, \end{align}

since the integrals of the trigonometric functions are all zero over the period. Thus,

a0=1T0Tf(t)dta_0 = \frac{1}{T} \int_0^T f(t) dt
  1. To calculate ana_n, multiply both sides of the equation by cos(mωt)\cos(m \omega t) and integrate over the period:

0Tf(t)cos(mωt)dt=a00Tcos(mωt)dt+0T(n=1ancos(nωt)cos(mωt))dt+0T(n=1bnsin(nωt)cos(mωt))dt\int_0^T f(t) \cos(m \omega t) dt = a_0 \int_0^T \cos(m \omega t) dt + \int_0^T \left( \sum_{n=1}^{\infty} a_n \cos (n \omega t) \cos(m \omega t) \right)dt + \int_0^T \left( \sum_{n=1}^{\infty} b_n \sin (n \omega t) \cos(m \omega t) \right) dt

Let’s take a look at each of the three integrals on the right-hand side. First,

a00Tcos(mωt)dt=0a_0 \int_0^T \cos(m \omega t) dt = 0

because it just integrates cosine over the period. Skipping to the last term,

0T(n=1bnsin(nωt)cos(mωt))dt=b10Tsin(ωt)cos(mωt)dt+b20Tsin(2ωt)cos(mωt)dt+=0\int_0^T \left( \sum_{n=1}^{\infty} b_n \sin (n \omega t) \cos(m \omega t) \right) dt = b_1 \int_0^T sin(\omega t) \cos (m \omega t) dt + b_2 \int_0^T \sin (2 \omega t) \cos (m \omega t) dt + \ldots = 0

due to orthogonality. We are just left with the middle integral; let’s expand a few terms to see what that looks like:

0T(n=1ancos(nωt)cos(mωt))dt=a10Tcos(ωt)cos(mωt)dt+a20Tcos(2ωt)cos(mωt)dt+\int_0^T \left( \sum_{n=1}^{\infty} a_n \cos (n \omega t) \cos(m \omega t) \right)dt = a_1 \int_0^T \cos (\omega t) \cos (m \omega t) dt + a_2 \int_0^T \cos (2 \omega t) \cos (m \omega t) dt + \ldots

Again, due to orthogonality, all of the terms of this infinite sum of integrals will be zero, except for the term where n=mn = m. As a result, we are left with

0Tf(t)cos(mωt)dt=0Tamcos2(mωt)dt=amT2an=am=2T0Tf(t)cos(nωt)dt\begin{align} \int_0^T f(t) \cos(m \omega t) dt &= \int_0^T a_m \cos^2 (m \omega t) dt = a_m \frac{T}{2} \\ a_n = a_m &= \frac{2}{T} \int_0^T f(t) \cos (n \omega t) dt \end{align}
  1. We can find bnb_n in the same way, multiplying the equation by sin(mωt)\sin (m \omega t) and integrating over the period. The work is the same, so let’s skip that:

bn=2T0Tf(t)sin(nωt)dtb_n = \frac{2}{T} \int_0^T f(t) \sin (n \omega t) dt

Example: periodic rectangular wave

Let’s find the Fourier series for representing this periodic function f(t)f(t):

x = [0, 1, 1, 2, 2, 3, 3, 4]
y = [2, 2, 1, 1, 2, 2, 1, 1]
plt.plot(x, y)
plt.ylabel('f(t)')
plt.xlabel('t')
plt.ylim([0, 2.5])
plt.show()
Loading...

First, we need to identify the fundamental period and frequency: T=2T = 2 and then ω=2πT=π\omega = \frac{2\pi}{T} = \pi. Our work is then to calculate the Fourier coefficients. Since our periodic function f(t)f(t) is not easily expressed with a function–hence the need for a Fourier series—we’ll use piecewise integration.

First, calculate a0a_0:

a0=1T0Tf(t)dt=1202f(t)dt=12(012dt+121dt)=12(2×1+1×1)a0=32\begin{align} a_0 =& \frac{1}{T} \int_0^T f(t) dt \\ &= \frac{1}{2} \int_0^2 f(t) dt = \frac{1}{2}\left( \int_0^1 2dt + \int_1^2 1dt \right) = \frac{1}{2} (2\times 1 + 1 \times 1) \\ a_0 &= \frac{3}{2} \end{align}

Then, get ana_n:

an=2T0Tf(t)cos(nωt)dt=2202f(t)cos(nπt)dt=(012cos(nπt)dt+121cos(nπt)dt)=2nπsin(nπt)01+1nπsin(nπt)12=2nπ(sin(nπ)sin(0))+1nπ(sin(2nπ)sin(nπ))an=0\begin{align} a_n &= \frac{2}{T} \int_0^T f(t) \cos (n \omega t) dt \\ &= \frac{2}{2} \int_0^2 f(t) \cos (n \pi t) dt = \left( \int_0^1 2 \cos (n \pi t) dt + \int_1^2 1 \cos (n \pi t)dt \right) \\ &= \frac{2}{n \pi} \sin(n \pi t)\Big|_0^1 + \frac{1}{n\pi} \sin(n \pi t)\Big|_1^2 \\ &= \frac{2}{n \pi}\left(\sin(n\pi) - \sin(0)\right) + \frac{1}{n\pi}\left( sin(2n\pi) - \sin(n\pi)\right) \\ a_n &= 0 \end{align}

Finally, we can calculate bnb_n:

bn=2T0Tf(t)sin(nωt)dt=2202f(t)sin(nπt)dt=(012sin(nπt)dt+121sin(nπt)dt)=2nπcos(nπt)011nπcos(nπt)12=2nπ(cos(nπ)cos(0))1nπ(cos(2nπ)cos(nπ))bn=2nπ(cos(nπ)1)1nπ(1cos(nπ))=1nπ(cos(nπ)1)\begin{align} b_n &= \frac{2}{T} \int_0^T f(t) \sin (n \omega t) dt \\ &= \frac{2}{2} \int_0^2 f(t) \sin (n \pi t) dt = \left( \int_0^1 2 \sin (n \pi t) dt + \int_1^2 1 \sin (n \pi t)dt \right) \\ &= -\frac{2}{n \pi} \cos(n \pi t)\Big|_0^1 - \frac{1}{n\pi} \cos(n \pi t)\Big|_1^2 \\ &= -\frac{2}{n \pi}\left(\cos(n\pi) - \cos(0)\right) - \frac{1}{n\pi}\left( cos(2n\pi) - \cos(n\pi)\right) \\ b_n &= -\frac{2}{n \pi}\left(\cos(n\pi) - 1\right) - \frac{1}{n\pi}\left( 1 - \cos(n\pi)\right) = -\frac{1}{n\pi}\left( \cos(n\pi) - 1\right) \end{align}

but recall that n=1,2,,n = 1, 2, \ldots, \infty. Then,

bn=1nπ×{2 if n odd0 if n evenbn=2nπn=odd\begin{align} b_n &= -\frac{1}{n\pi} \times \begin{cases} -2 \text{ if } n \text{ odd} \\0 \text{ if } n \text{ even}\end{cases} \\ \rightarrow b_n &= \frac{2}{n\pi} \quad n = \text{odd} \end{align}

Then, our Fourier series representation for the function shown above is

f(t)=32+n=1n=odd2nπsin(nπt)f(t) = \frac{3}{2} + \sum_{\substack{n = 1 \\n = \text{odd}}}^{\infty} \frac{2}{n\pi} \sin (n \pi t)

Now, let’s see how whether this actually works! Let’s start with one term of the infinite sum, then gradually increase.

time = np.linspace(0, 4)

# maximum number of terms
num_max = [1, 2, 3, 5, 10, 25, 50, 250, 500]
fig, axes = plt.subplots(3, 3)

for idx, num in enumerate(num_max):
    func = np.ones_like(time) * 3/2
    for n in range(1, 2*num)[::2]:
        func += (2 / (n*np.pi)) * np.sin(n * np.pi * time)
    idy, idz = np.unravel_index(idx, (3,3))
    axes[idy,idz].plot(time, func)
    axes[idy,idz].set_title(f'{num} terms')

for ax in fig.get_axes():
    ax.label_outer()
plt.tight_layout()
plt.show()
Loading...

As we increase the number of terms, adding higher-frequency sine waves, we are better able to match the original rectangular wave. Notice the discrepancies that remain near the sharp corners even after the rest of the series closely resembles the function: these are known as Gibbs phenomena, caused by the Fourier series overshooting or undershooting (or “ringing”) near discontinuities.

Even and Odd Functions

We can simplify our work generating a Fourier series if we can identify the given periodic function f(t)f(t) as an even function or an odd function.

Even functions are those where f(x)=f(x)f(-x) = f(x).

Odd functions are those where f(x)=f(x)f(-x) = -f(x).

fig, axes = plt.subplots(2, 1)

y = [-1, 0, 1, 0, -1]
x = [-2, -1, 0, 1, 2]
axes[0].plot(x, y)
axes[0].set_title('Even function')
# sets axis lines at origin
axes[0].spines['top'].set_color('none')
axes[0].spines['left'].set_position('zero')
axes[0].spines['right'].set_color('none')
axes[0].spines['bottom'].set_position('zero')

y = [0, -1, 0, 1, 0]
axes[1].plot(x,y)
axes[1].set_title('Odd function')
# sets axis lines at origin
axes[1].spines['top'].set_color('none')
axes[1].spines['left'].set_position('zero')
axes[1].spines['right'].set_color('none')
axes[1].spines['bottom'].set_position('zero')

plt.tight_layout()
plt.show()
Loading...

An even function’s Fourier series simplifies to a Fourier cosine series:

f(x)=a0+n=1ancos(nωx)dxa0=2T0T/2f(x)dxan=4T0T/2f(x)cos(nωx)dx\begin{align} f(x) &= a_0 + \sum_{n=1}^{\infty} a_n \cos (n \omega x) dx \\ a_0 &= \frac{2}{T} \int_0^{T/2} f(x) dx \\ a_n &= \frac{4}{T} \int_0^{T/2} f(x) \cos(n \omega x) dx \end{align}

An odd function’s Fourier series simplifies to a Fourier sine series:

f(x)=n=1bnsin(nωx)dxbn=4T0T/2f(x)sin(nωx)dx\begin{align} f(x) &= \sum_{n=1}^{\infty} b_n \sin (n \omega x) dx \\ b_n &= \frac{4}{T} \int_0^{T/2} f(x) \sin(n \omega x) dx \end{align}

Note: not all periodic functions can be considered an even or an odd function.

Application: Inhomogeneous 2nd-order ODE

One way we might use a Fourier series is to solve an inhomogeneous 2nd-order ODE, where the forcing term is given by a periodic function not easily expressed using our typical functions.

Undamped mass-spring system

For example, let’s consider an undamped mass-spring system, where the forcing is given by a periodic rectangular wave:

y+4y=f(t)y^{\prime\prime} + 4y = f(t)

where the forcing function f(t)f(t) is

time = [0, 0, 1, 1, 2, 2, 3, 3]
y = [0, 1, 1, -1, -1, 1, 1, 0]
plt.plot(time, y)
plt.title('Periodic rectangular forcing function')

ax = plt.gca()
ax.spines['top'].set_color('none')
ax.spines['bottom'].set_position('zero')
plt.show()
Loading...

Using recognizing this as an odd function, we could construct a Fourier sine series to represent the forcing function:

f(t)=n=1n=odd4nπsin(nπt)f(t) = \sum_{\substack{n=1\\n=\text{odd}}}^{\infty} \frac{4}{n\pi} \sin(n \pi t)

Let’s confirm this works:

time = np.linspace(0, 3, 500)
func = 0
for n in range(1,1000)[::2]:
    func += (4 / (n*np.pi)) * np.sin(n * np.pi * time)
plt.plot(time, func)
plt.show()
Loading...

Looks good!

To find the exact solution for our displacement y(t)y(t), we can follow our usual analytical solution approach: find the homogeneous solution yHy_H, then find the inhomogeneous solution yIHy_{IH}; the overall solution is then y(t)=yH+yIHy(t) = y_H + y_{IH}. The homogeneous solution is

yH=c1sin(2t)+c2cos(2t)y_H = c_1 \sin (2t) + c_2 \cos (2t)

We then find the inhomogeneous solution using

y+4y=4nπsin(nπt)n=1,3,,y^{\prime\prime} + 4y = \frac{4}{n\pi} \sin (n \pi t) \quad n = 1, 3, \ldots, \infty

Solving this will give us a specific yIH,ny_{IH, n}; the complete inhomogeneous solution is then

yIH=n=1n=oddyIH,n  .y_{IH} = \sum_{\substack{n=1\\n=\text{odd}}}^{\infty} y_{IH, n} \;.

Recognizing that our forcing function is sinusoidal, we should use the method of undetermined coefficients:

yIH,n=K1sin(nπt)+K2cos(nπt)y_{IH, n} = K_1 \sin (n \pi t) + K_2 \cos (n \pi t)

Inserting this into the above ODE gives

K1=4nπ(4n2π2)K2=0\begin{align} K_1 &= \frac{4}{n \pi (4 - n^2 \pi^2)} \\ K_2 &= 0 \end{align}

Thus, the overall solution is

y(t)=c1sin(2t)+c2cos(2t)+n=1n=odd4nπ(4n2π2)sin(nπt)y(t) = c_1 \sin(2t) + c_2 \cos(2t) + \sum_{\substack{n=1\\n=\text{odd}}}^{\infty} \frac{4}{n \pi (4 - n^2 \pi^2)} \sin (n \pi t)

Damped mass-spring system

What about a damped mass-spring system? Recall that the homogeneous solution could take one of these three forms:

yH=c1eλ1t+c2eλ2tyH=c1eλ1t+c2teλ2t oryH=eαt(c1sin(βt)+c2cos(βt))\begin{align} y_H &= c_1 e^{-\lambda_1 t} + c_2 e^{-\lambda_2 t} \\ y_H &= c_1 e^{-\lambda_1 t} + c_2 t e^{-\lambda_2 t} \text{ or} \\ y_H &= e^{-\alpha t} (c_1 \sin(\beta t) + c_2 \cos(\beta t)) \end{align}

while the inhomogeneous solution, given a Fourier series forcing function, will take the form

yIH=K1sin()+K2cos()y_{IH} = K_1 \sin() + K_2 \cos()

The overall solution combines the homogenenous and inhomogeneous solutions. But, the homogeneous solution in this case is transient, because it decays to zero. On the other hand, the inhomogeneous solution remains, and is the steady-state solution.