Einstein's equations behave like wave equations, $\partial_{tt} \phi = \nabla^2 \phi$.
Use first order form
$$ \frac{\partial}{\partial t} \begin{pmatrix} \phi \\ \phi_t \\ \phi_x \end{pmatrix} = \begin{pmatrix} \phi_t \\ 0 \\ 0 \end{pmatrix} + \frac{\partial}{\partial x} \begin{pmatrix} 0 \\ \phi_x \\ \phi_t \end{pmatrix}. $$
In [1]:
import numpy
from matplotlib import pyplot
%matplotlib inline
In [2]:
def RHS(U, dx):
"""
RHS term.
Parameters
----------
U : array
contains [phi, phi_t, phi_x] at each point
dx : double
grid spacing
Returns
-------
dUdt : array
contains the required time derivatives
"""
phi = U[0, :]
phi_t = U[1, :]
phi_x = U[2, :]
dUdt = numpy.zeros_like(U)
dUdt[0, :] = phi_t
dUdt[1, 1:-1] = 1.0 / (2.0*dx)*(phi_x[2:] - phi_x[:-2])
dUdt[2, 1:-1] = 1.0 / (2.0*dx)*(phi_t[2:] - phi_t[:-2])
return dUdt
In [3]:
def grid(Npoints):
dx = 2.0 / Npoints
return dx, numpy.linspace(-1.0-dx/2.0,
1.0+dx/2.0, Npoints+2)
pyplot.figure()
dx, x = grid(4)
pyplot.plot(x, numpy.ones_like(x), 'kx', ms=12, mew=2)
pyplot.show()
In [4]:
def initial_data(x):
U = numpy.zeros((3, len(x)))
U[0, :] = numpy.exp(-20.0 * x**2)
U[2, :] = -40.0*x*numpy.exp(-20.0 * x**2)
return U
pyplot.figure()
dx, x = grid(100)
U = initial_data(x)
pyplot.plot(x, U[0, :], 'b-')
pyplot.xlim(-1, 1)
pyplot.show()
In [5]:
def apply_boundaries(dUdt):
"""
Periodic boundaries applied to the RHS directly.
"""
dUdt[:, 0] = dUdt[:, -2]
dUdt[:, -1] = dUdt[:, 1]
return dUdt
def RK2_step(U, RHS, apply_boundaries, dt, dx):
rhs = apply_boundaries(RHS(U, dx))
Up = U + dt * rhs
rhs_p = apply_boundaries(RHS(Up, dx))
Unew = 0.5 * (U + Up + dt * rhs_p)
return Unew
In [6]:
Npoints = 50
dx, x = grid(Npoints)
dt = dx / 4
U0 = initial_data(x)
U = initial_data(x)
Nsteps = int(1.0 / dt)
for n in range(Nsteps):
U = RK2_step(U, RHS, apply_boundaries, dt, dx)
pyplot.figure()
pyplot.plot(x, U0[0, :], 'b--', label="Initial data")
pyplot.plot(x, U[0, :], 'k-', label=r"$t=1$")
pyplot.xlabel(r"$x$")
pyplot.ylabel(r"$\phi$")
pyplot.xlim(-1, 1)
pyplot.legend()
pyplot.show()
In [7]:
U = initial_data(x)
Nsteps = int(2.0 / dt)
for n in range(Nsteps):
U = RK2_step(U, RHS, apply_boundaries, dt, dx)
pyplot.figure()
pyplot.plot(x, U0[0, :], 'b--', label="Initial data")
pyplot.plot(x, U[0, :], 'k-', label=r"$t=2$")
pyplot.xlabel(r"$x$")
pyplot.ylabel(r"$\phi$")
pyplot.xlim(-1, 1)
pyplot.legend()
pyplot.show()
In [8]:
def error_norms(U, U_initial):
"""
Error norms (1, 2, infinity)
"""
N = len(U)
error_1 = numpy.sum(numpy.abs(U - U_initial))/N
error_2 = numpy.sqrt(numpy.sum((U - U_initial)**2)/N)
error_inf = numpy.max(numpy.abs(U - U_initial))
return error_1, error_2, error_inf
In [9]:
Npoints_all = 50 * 2**(numpy.arange(0, 6))
dxs = numpy.zeros((len(Npoints_all,)))
wave_errors = numpy.zeros((3, len(Npoints_all)))
for i, Npoints in enumerate(Npoints_all):
dx, x = grid(Npoints)
dt = dx / 4
U0 = initial_data(x)
U = initial_data(x)
Nsteps = int(2.0 / dt)
for n in range(Nsteps):
U = RK2_step(U, RHS, apply_boundaries, dt, dx)
dxs[i] = dx
wave_errors[:, i] = error_norms(U[0, :], U0[0, :])
pyplot.figure()
pyplot.loglog(dxs, wave_errors[0, :], 'bx', label=r"${\cal E}_1$")
pyplot.loglog(dxs, wave_errors[1, :], 'go', label=r"${\cal E}_2$")
pyplot.loglog(dxs, wave_errors[2, :], 'r+', label=r"${\cal E}_{\infty}$")
pyplot.loglog(dxs, wave_errors[1, 0]*(dxs/dxs[0])**4, 'k-', label=r"$p=4$")
pyplot.xlabel(r"$\Delta x$")
pyplot.ylabel("Error norm")
pyplot.legend(loc="lower right")
pyplot.show()
In [10]:
def initial_data_asymmetric(x):
U = numpy.zeros((3, len(x)))
U[0, :] = numpy.sin(numpy.pi*x)*(1-x)**2*(1+x)**3
U[2, :] = numpy.pi*numpy.cos(numpy.pi*x)*(1-x)**2*(1+x)**3 + \
numpy.sin(numpy.pi*x)*(2.0*(1-x)*(1+x)**3 + \
3.0*(1-x)**2*(1+x)**2)
return U
pyplot.figure()
dx, x = grid(100)
U = initial_data_asymmetric(x)
pyplot.plot(x, U[0, :], 'b-')
pyplot.xlim(-1, 1)
pyplot.show()
In [11]:
Npoints_all = 50 * 2**(numpy.arange(0, 6))
dxs = numpy.zeros((len(Npoints_all,)))
wave_errors = numpy.zeros((3, len(Npoints_all)))
for i, Npoints in enumerate(Npoints_all):
dx, x = grid(Npoints)
dt = dx / 4
U0 = initial_data_asymmetric(x)
U = initial_data_asymmetric(x)
Nsteps = int(2.0 / dt)
for n in range(Nsteps):
U = RK2_step(U, RHS, apply_boundaries, dt, dx)
dxs[i] = dx
wave_errors[:, i] = error_norms(U[0, :], U0[0, :])
pyplot.figure()
pyplot.loglog(dxs, wave_errors[0, :], 'bx', label=r"${\cal E}_1$")
pyplot.loglog(dxs, wave_errors[1, :], 'go', label=r"${\cal E}_2$")
pyplot.loglog(dxs, wave_errors[2, :], 'r+', label=r"${\cal E}_{\infty}$")
pyplot.loglog(dxs, wave_errors[1, 0]*(dxs/dxs[0])**2, 'k-', label=r"$p=2$")
pyplot.xlabel(r"$\Delta x$")
pyplot.ylabel("Error norm")
pyplot.legend(loc="lower right")
pyplot.show()
In [12]:
Npoints = 50
dx, x = grid(Npoints)
dt = dx # This is the crucial line
U0 = initial_data(x)
U = initial_data(x)
Nsteps = int(2.0/dt)
for n in range(Nsteps):
U = RK2_step(U, RHS, apply_boundaries, dt, dx)
pyplot.figure()
pyplot.plot(x, U0[0, :], 'b--', label="Initial data")
pyplot.plot(x, U[0, :], 'k-', label=r"$t=2$")
pyplot.xlabel(r"$x$")
pyplot.ylabel(r"$\phi$")
pyplot.xlim(-1, 1)
pyplot.legend()
pyplot.show()
Hyperbolic PDEs have a Courant limit: $\Delta t$ too large is unstable. This depends on physics and numerics. Typically a maximum limit is
$$ \sigma < \frac{1}{\sqrt{D} \lambda_{\text{max}}} $$($D$ is number of spatial dimensions, $\lambda_{\text{max}}$ the maximum characteristic speed).