Domain: Physics — Optics and Acoustics

Problem:
Simulate the one-dimensional nonlinear thermoviscous–dispersive acoustic field in a periodic duct of length \(1.0\,\mathrm{m}\) on the uniform grid \(x_j = j/2048\) for \(j = 0, \dots, 2047\), governed by the PDE \(u_t + \alpha u u_x + \beta(t) u^2 u_x + \delta (u_x u_{xx}) = \mu u_{xx} + \gamma u_{xxx} - \kappa u_{xxxx}\), with \(\alpha = 1\), \(\delta = 2.5 \times 10^{-6}\,\mathrm{m}^2/\mathrm{s}\), \(\mu = 1.0 \times 10^{-4}\,\mathrm{m}^2/\mathrm{s}\), \(\gamma = 1.0 \times 10^{-6}\,\mathrm{m}^3/\mathrm{s}\), \(\kappa = 1.0 \times 10^{-12}\,\mathrm{m}^4/\mathrm{s}\), and time-dependent coefficient \(\beta(t) = 0.35\) for \(0 \leq t < 2400\,\Delta t\) and \(\beta(t) = 0.40\) for \(2400\,\Delta t \leq t \leq 3200\,\Delta t\), with fixed time step \(\Delta t = 2.5 \times 10^{-6}\,\mathrm{s}\). Use initial condition \(u(x_j,0) = 2.0\,\sin(2\pi x_j) + 0.3\,\sin(4\pi x_j)\,\mathrm{m/s}\). Advance the solution exactly \(3200\) time steps using Fourier pseudo-spectral spatial discretization and classical fourth-order Runge–Kutta time integration, with periodic boundary conditions and no filtering, padding, splitting, or adaptive stepping; set \(\beta = 0.35\) for steps \(0\) to \(2399\) and \(\beta = 0.40\) for steps \(2400\) to \(3199\). At final time \(T = 3200\,\Delta t = 8.0 \times 10^{-3}\,\mathrm{s}\), compute pressure via \(p_j = \rho_0 c_0 u(x_j,T)\) with \(\rho_0 = 1.204\,\mathrm{kg/m}^3\) and \(c_0 = 343.0\,\mathrm{m/s}\). From the pressure samples \(\{p_j\}_{j=0}^{2047}\) in grid order, compute the third-harmonic Fourier coefficient \(P_3 = \frac{1}{2048} \sum_{j=0}^{2047} p_j \exp\!\left(-i \frac{6\pi j}{2048}\right)\), and report the amplitude \(A_3 = 2 |P_3|\) in pascals to three significant figures.

Answer: 21.8

Solution code:
import numpy as np

# problem/data
N = 2048
x = np.arange(N) / N           # x_j = j/2048 on [0,1)
dt = 2.5e-6
steps1 = 2400
steps2 = 800

alpha = 1.0
delta = 2.5e-6                 # stabilized value
mu = 1.0e-4
gamma = 1.0e-6
kappa = 1.0e-12                # used with minus sign in linear part

rho0 = 1.204
c0 = 343.0

# spectral stuff
n = np.fft.fftfreq(N, d=1.0/N)
k = 2.0 * np.pi * n
ik = 1j * k
k2 = k * k
k3 = k2 * k
k4 = k2 * k2

# 2/3 de-aliasing mask
cutoff = N // 3
dealias_mask = (np.abs(n) <= cutoff)

# initial condition in Fourier space
u_hat = np.fft.fft(2.0*np.sin(2*np.pi*x) + 0.3*np.sin(4*np.pi*x))

def rhs(u_hat_stage, beta_val):
    # de-alias field
    u_hat_deal = np.zeros_like(u_hat_stage)
    u_hat_deal[dealias_mask] = u_hat_stage[dealias_mask]

    # physical field
    u = np.fft.ifft(u_hat_deal)
    if np.max(np.abs(u.imag)) < 1e-12:
        u = u.real

    # derivatives
    ux_hat = ik * u_hat_deal
    uxx_hat = -k2 * u_hat_deal
    ux = np.fft.ifft(ux_hat)
    uxx = np.fft.ifft(uxx_hat)
    if np.max(np.abs(ux.imag)) < 1e-12:
        ux = ux.real
    if np.max(np.abs(uxx.imag)) < 1e-12:
        uxx = uxx.real

    # nonlinear terms in physical space
    nonlinear_phys = (-alpha)*u*ux + (-beta_val)*(u**2)*ux + (-delta)*(ux*uxx)
    nonlinear_hat = np.fft.fft(nonlinear_phys)

    # linear terms in spectral space
    # note the minus on kappa*k4
    linear_hat = (-mu*k2 - 1j*gamma*k3 - kappa*k4) * u_hat_stage

    return nonlinear_hat + linear_hat

def rk4_step(u_hat_cur, step_index):
    beta_val = 0.35 if step_index < steps1 else 0.40
    k1 = rhs(u_hat_cur,               beta_val)
    k2 = rhs(u_hat_cur + 0.5*dt*k1,   beta_val)
    k3 = rhs(u_hat_cur + 0.5*dt*k2,   beta_val)
    k4 = rhs(u_hat_cur + dt*k3,       beta_val)
    u_hat_new = u_hat_cur + dt*(k1 + 2*k2 + 2*k3 + k4)/6.0
    # zero mean
    u_hat_new[0] = 0.0 + 0.0j
    return u_hat_new

# time marching
total_steps = steps1 + steps2
for step in range(total_steps):
    u_hat = rk4_step(u_hat, step)

# final velocity field
u_final = np.fft.ifft(u_hat)
if np.max(np.abs(u_final.imag)) < 1e-12:
    u_final = u_final.real

# pressure
p = rho0 * c0 * u_final

# third harmonic per the question
j = np.arange(N)
P3 = (p * np.exp(-1j * 6.0 * np.pi * j / N)).sum() / N
A3 = 2.0 * np.abs(P3)

# print in pascals, 3 significant figures
print(f"{A3:.3g}")