Processing math: 100%

Fourier Method for the 1D Wave Equation: Fixed String

In this post we are going to explore the Fourier method for solving the 1D wave equation. The method is more known under the name of the method of separation of variables. For the 1D wave equation we are going to show the application of the method to a fixed string. We are also going to attempt to outline some of the physical interpretations of the fixed string.

1D Wave Equation

The 1D wave equation is of the form

utt=a2uxx.

Fixed String

First, let's take a look at the model of a string with length l which is also fixed at both ends:

{utt=a2uxx,u(x,0)=φ1(x),ut(x,0)=φ2(x),u(0,t)=u(l,t)=0.

A visualisation of the string can be seen in the figure below.

Fixed String

We start solving the equation by taking into account only the boundary conditions u(0,t)=u(l,t)=0. The idea is to find solution u(x,t) of the form

u(x,t)=X(x)T(t).

We substitute this form of the solution into the wave equation and get

1a2T(t)X(x)=T(t)X(x),

further divding by X(x)T(t) leads to

1a2T(t)T(t)=X(x)X(x).

We have two functions of independent variables which are equal. This is only possible if they are equal to the same constant. Therefore, let

1a2T(t)T(t)=X(x)X(x)=λ,

producing the following two equeations:

T(t)+a2λT(t)=0

and

X(x)+λX(x)=0.

Let's begin with solving the second equation. The boundary conditions give

X(0)T(t)=0andX(l)T(t)=0.

Because we are interested only in non-trivial solutions and thus T0, we have

X(0)=0andX(l)=0.

Now, we have to find the non-trivial solutions for X(x) satisfying

{X(x)+λX(x)=0,X(0)=0,X(l)=0.

The above problem is an example of the so called Sturm-Liouville problem. In order to find the general solution of the second order linear homogeneous differential equation with constant coefficients (*) we should solve its characteristic equation

r2+λ=0.
  • If λ<0, then r1,2=±λ, hence the general solution is

    X(x)=c1eλx+c2eλx
    for some constants c1 and c2. In order to determine the constants we substitute the above solution into the boundary conditions (**) and get the system
    {c1+c2=0,c1eλl+c2eλl=0.
    This results in c1=c2=0, meaning our Sturm-Liouville problem doesn't have a non-zero solution for λ<0.

  • If λ=0, then r1=r2=0 and the general solution is

    X(x)=c1+c2x.
    Substituing it into the boundary conditions (**) again lead to c1=c2=0, hence no non-zero solutions of our Sturm-Liouville problem for λ0.

  • If λ>0, then r1,2=±iλ, and the general solution becomes

    X(x)=c1cos(λx)+c2sin(λx).
    Substituting into the boundary conditions (**) results in
    {c1=0,c2sin(λl)=0
    If c2=0, then X(x)0 which is a trivial solution. Therefore, we set c20 and hence
    sin(λl)=0,
    giving λl=kπ, k=±1,±2,.... Theerfore,
    λ=λk=(kπl)2,
    meaning eigenvalues exist when λ>0. The eigenfunctions corresponding to the above eigenvalues are
    Xk(x)=sin(kπxl),k>0,kN.

Going back to T(t)+a2λT(t)=0, solving in analogical way, when λ=λk the solution becomes

Tk(t)=Akcos(akπlt)+Bksin(akπlt)

for some constants Ak and Bk. Hence,

uk(x,t)=Xk(x)Tk(t)=(Akcos(akπlt)+Bksin(akπlt))sin(kπxl),k>0,kN

are solutions to our wave equation, also satisfying the boundary conditions. Since our equation is linear, forming a linear system with its conditions, the principle of superposition is valid. In other words, if u1,u2,...,un are solutions of our system, then

α1u1+α2u2+...+αnun

for some constants α1,α2,...,αn is also a solution of the system. But in our case we have an infinite number of functions u1,u2,... which satisfy the linear system. Therefore, we need the generalised superposition principle stating that in such case

u=nαnun

for some arbitrary constants αn is a solution to the system if the series converges uniformly and is twice differentiable termwise. This generalisation is a Lemma and should be prooved. The proof can be found in ...

Assuming we have prooved the said Lemma, we can state that our system has a solution of the form

u(x,t)=k=1uk(x,t)=k=1(Akcos(akπlt)+Bksin(akπlt))sin(kπxl).

The next task we have to tackle is to determine the coefficients Ak and Bk. We can achieve this by using the initial conditions

u(x,0)=φ1(x),andut(x,0)=φ2(x).

We get

u(x,0)=k=1Aksin(kπxl)=φ1(x)

and

ut(x,0)=k=1akπlBksin(kπxl)=φ2(x).

Now, we have to expand both φ1(x) and φ2(x) into series in terms of sines only (why?). We have

φ1(x)=k=1φ(1)ksin(kπlx)

and

φ2(x)=k=1φ(2)ksin(kπlx).

By the Fourier series theroem of uniqueness, we get

Ak=φ(1)kandBk=lakπφ(2)k,

or (why?)

Ak=2ll0φ1(x)sin(kπlx)dx

and

Bk=2akπl0φ2(x)sin(kπlx)dx.

We are left with the task of the covergence of the infinite series. We have to explore the following series

|u(x,t)|k=1(|Ak|+|Bk|),
|ut(x,t)|k=1akπl(|Ak|+|Bk|),
|ux(x,t)|k=1kπl(|Ak|+|Bk|),
|utt(x,t)|k=1a2k2π2l2(|Ak|+|Bk|),
|uxx(x,t)|k=1k2π2l2(|Ak|+|Bk|).

If the series on the right side (majorizing series) converge then the series on the left would also converge and the needed differentiation would exist. It is enough (why?) for the following series to converge

k=1kj(|φ(1)k|+2akπ|φ(2)k|),j=0,1,2.

This is possible only if

{k=1kj|φ(1)k|,k=1kj1|φ(2)k|,j=0,1,2.

converge. From Calculus we know (theorem) that if φ(x) is m-times differentiable then

k=1km1|φk|

convergres. Therefore, in order for all the majorzing series to converge it is enough φ1(x) to be 3-times differentiable, and φ2(x) to be 2-times differentiable.

Finally, we should note a few things about the expansion of φ1(x) and φ2(x) into sine series. We have to note that in order to do that the function needs to be continued as an odd function which my lead to loss of the regularity of the lower derivatives. Let ˜φ1(x) be the continuation of φ1(x) as an odd function (see the Figure below) defined as

Odd continuation of a function

˜φ1(x)={φ1(x),0xl,φ1(x),lx0.

Hence, in order for it to be continous and continously differentibale we need to enforce the following condition

φ1(0)=φ1(l)=0.

To summarise, in order for k=1φ(1)ksin(kπlx) to converge in [0,l] it is necessary to enforce the above conditions to have zero values at both ends of the interval. As for the second derivative, if it exists it would be continuous as well. Similarly, for the third derivative to exist we enforce

φ1(0)=φ1(l)=0

and obtain the corresponding necessary condition

φ2(0)=φ2(l)=0.

Finally, after these enforced conditions we can conclude that (tehorem)

u(x,t)=k=1(Akcos(akπlt)+Bksin(akπlt))sin(kπxl)

is a regular solution of the problem.

Physical interpretation:

If we go back to the eigenfunction

uk(x,t)=(Akcos(akπlt)+Bksin(akπlt))sin(kπlx),k>0,kN

we can rewrite it as

uk(x,t)=A2k+B2ksin(kπlx)sin(akπlt+ϕk),kN,

where

tan(ϕk)=AkBk.

We can translate this as the points of the string to oscillate at the frequency ωk=akπl with phase ϕk. The amplitude is dependant on x and is given by

Fk=A2k+B2ksin(kπlx).

The uk(x,t) waves are called standing-waves. Depending on the values of k we have the following scenarios:

  • When k=1 there are 2 motionless points which are the ends of the (fixed) string
  • When k=2 a third moitonless point x=l2 is added

These motionless points are called nodes of the standing wave. In general, uk(x,t) has (k+1) nodes located ate 0,1kl,2kl,...,k1kl,l. The maximum amplitude is achieved in the middle points between two nodes. These points are called crests. The fundamental tone, or the lowest tone, has frequency of ω1=aπl. The frequencies ωk are called harmonics, while the higher tones corresponding to ωk, k=2,3,... are called overtones. It is quite natural to notice that the higher the value of k the rapidly lower the amplitude of uk(x,t) becomes. Meaning, the effect from the higher harmonics all combined influences the quality of the sound. The below figure shows the harmonics for k=1,2,3.

Fixed Strings

Example

Here, we are going to show an example of a fixed string. We are going to show an animated solution with the help of Python. The fixed string problem is given by

{utt=(23)2uxx,u(x,0)={sin3(πx),1x3,0,xR[1,3],,ut(x,0)=0,u(0,t)=u(π5,t)=0.

Using the 100-th partial Fourier sum, below is shown the animated solution for t[0,30].

Click to expand code
import numpy as np
import pandas as pd
import plotly.express as px

# Define constants
L = np.pi * np.sqrt(5)
a = 2 / 3
tmax = 30
x = np.linspace(0, L, 101)
t = np.linspace(0, tmax, 31)  # Fewer points for smoother interaction


# Define the initial condition phi(x)
def phi(x):
    y = np.zeros_like(x)
    y[(1 < x) & (x < 3)] = np.sin(np.pi * x[(1 < x) & (x < 3)]) ** 3
    return y


# Define the initial velocity psi(x)
def psi(x):
    return np.zeros_like(x)


# Define the Fourier solution for u(x, t)
def fourier_u(x, t):
    y = np.zeros_like(x)
    for k in range(1, 101):
        Xk = np.sin(k * np.pi * x / L)
        Ak = (2 / L) * np.trapezoid(phi(x) * Xk, x)
        Bk = (2 / (a * k * np.pi)) * np.trapezoid(psi(x) * Xk, x)
        Tk = Ak * np.cos(a * k * np.pi * t / L) + Bk * np.sin(a * k * np.pi * t / L)
        y += Tk * Xk
    return y


# Create animation data
data = []
for t_val in t:
    y = fourier_u(x, t_val)
    for x_val, y_val in zip(x, y):
        data.append({"x": x_val, "y": y_val, "t": f"t = {t_val:.2f}"})

# Create DataFrame and plot
df = pd.DataFrame(data)
fig = px.line(
    df,
    x="x",
    y="y",
    animation_frame="t",
    # title="String Motion",
    labels={"x": "x", "y": "u(x, t)"},
    range_x=[-0.06, L + 0.06],
    range_y=[-1.1, 1.1],
    color_discrete_sequence=["red"],
)

# Add fixed points as black dots
fixed_points = pd.DataFrame(
    {
        "x": [0, L],
        "y": [0, 0],
    }
)

fig.add_scatter(
    x=fixed_points["x"],
    y=fixed_points["y"],
    mode="markers",
    marker=dict(color="black", size=10),
    showlegend=False,
)

fig.update_layout(
    showlegend=False,
    height=280,
    margin=dict(l=10, r=30, t=30, b=10),
    updatemenus=[
        {
            "buttons": [
                {
                    "args": [
                        None,
                        {
                            "frame": {"duration": 200, "redraw": True},
                            "fromcurrent": True,
                            "mode": "immediate",
                            "transition": {"duration": 0},
                        },
                    ],
                    "label": "Play",
                    "method": "animate",
                },
                {
                    "args": [
                        [None],
                        {
                            "frame": {"duration": 0, "redraw": True},
                            "mode": "immediate",
                            "transition": {"duration": 0},
                        },
                    ],
                    "label": "Pause",
                    "method": "animate",
                },
            ],
            "type": "buttons",
            "direction": "left",
            "showactive": True,
            "x": 0.2,
            "y": 0.3,
            "xanchor": "right",
            "yanchor": "top",
        }
    ],
    sliders=[
        {
            "active": 0,
            "yanchor": "top",
            "xanchor": "left",
            "currentvalue": {"font": {"size": 16}, "visible": True, "xanchor": "right"},
            "transition": {"duration": 500, "easing": "cubic-in-out"},
            "pad": {"b": 10, "t": 50},
            "len": 1,
            "x": 0,
            "y": 0,
            "steps": [
                {
                    "args": [
                        [f"t = {t:.2f}"],
                        {
                            "frame": {
                                "duration": 500,
                                "easing": "cubic-in-out",
                                "redraw": True,
                            },
                            "mode": "immediate",
                            "transition": {"duration": 0},
                        },
                    ],
                    "label": f"{t:.0f}",
                    "method": "animate",
                }
                for t in t
            ],
        }
    ],
)

config = {
    "displayModeBar": True,  # Show the toolbar
    # "modeBarButtonsToRemove": ["lasso2d", "select2d"],  # Remove unused buttons
    "displaylogo": False,
    "toImageButtonOptions": {"height": 500, "width": 800},  # Image export size
}
fig.write_html(
    "content/code/2025-01-04-fourier-method-fixed-string/fixed_string_animation.html",
    include_plotlyjs=True,
    full_html=True,
    auto_play=False,
    config=config,
)

links

social