Table of Contents
Imagine your worst enemy is playing a constant frequency in the center of her room. You're allowed to put speakers (which don't take up space) wherever you want in the room. The question is, what's the best place to put them and what should they play?
To be fair, this is not the question that I'll be answering in this post, but it was the inspiration. We first simplify the problem into 2D so your enemy is now vibrating a surface with constant frequency. To simulate this problem, we need to solve the wave equation, which I do in this post.
At first, I tried to figure out the finite difference equations, but I got stuck. Then, I looked it up and I found this wonderful blog post. I've rederived the equations to make sure I believed it and I wrote the code in Julia, which you can find it on GitHub. My first attempt at figuring out the finite differences is in the Python notebook, and the final code is in simulation.jl
.
See calculus notation for notation. The wave equation is
Dttโu=c2(Dxxโu+Dyyโu)
First, we figure out what the finite difference wave equation looks like. Since we have a second time derivative, we need to keep 2 past states to calculate the difference in the first derivative. Look at this picture (u0,i,jโ is the newest state)
Let's do the time derivative first.
Dtโu1,i,jโ=dtฮtโu1,i,jโโ=dtu1,i,jโโu2,i,jโโ
So,
Dttโu1,i,jโ=dtฮtโDtโu1,i,jโโ=dtdtu0,i,jโโu1,i,jโโโdtu1,i,jโโu2,i,jโโโ=(dt)2u0,i,jโโ2u1,i,jโ+u2,i,jโโ
Next, we do space derivatives (ignoring c2 for now),
Dxxโu1,i,jโโ+Dyyโu1,i,jโ=dxฮxโDxโu1,i,jโโ+dyฮyโDyโu1,i,jโโ=(dx)2u1,iโ1,jโโ2u1,i,jโ+u1,i+1,jโโ+(dy)2u1,i,jโ1โโ2u1,i,jโ+u1,i,j+1โโโ
Returning to the wave equation,
Dttโu(dt)2u0,i,jโโ2u1,i,jโ+u2,i,jโโu0,i,jโโ=c2(Dxxโu+Dyyโu)=c2((dx)2u1,iโ1,jโโ2u1,i,jโ+u1,i+1,jโโ+(dy)2u1,i,jโ1โโ2u1,i,jโ+u1,i,j+1โโ)=2u1,i,jโโu2,i,jโ+c2(dt)2((dx)2u1,iโ1,jโโ2u1,i,jโ+u1,i+1,jโโ+(dy)2u1,i,jโ1โโ2u1,i,jโ+u1,i,j+1โโ)โ
If we assume dx=dy and ฮฑ=(dx)2c2(dt)2โ then
u0,i,jโ=2u1,i,jโโu2,i,jโ+ฮฑ(u1,iโ1,jโ+u1,i,jโ1โโ4u1,i,jโ+u1,i+1,jโ+u1,i,j+1โ)
There are no u0,_,_โ on the RHS. Thus, every value in the newest level can be calculated by the ones in previous levels.
We deal with the boundary by only calculating u[0,1:lx,1:ly]
, and leaving the rest 0. So shifts are calculated like so: u[1,0:lx-2,1:ly]
.
Note: this is actually Verlet integration (a trick for solving for the next state in terms of the finite differences and the second derivatives).
We can play a frequency by resetting the value of a u0,i,jโ after the step to the value of a sine wave at the time. We can make a water droplet by setting part of the u0,i,jโ layer to a negative gaussian at a certain time step. This is achieved with put_drop!()
and play_noise!()
functions.