General Relativity

Einstein's Masterpiece: Gravity is the curvature of spacetime. Mass-energy tells spacetime how to curve, and spacetime tells mass-energy how to move.

Chapter 13: Einstein Field Equations

The Einstein field equations are the core of general relativity. They relate the curvature of spacetime (geometry) to the distribution of matter and energy. "Matter tells spacetime how to curve; spacetime tells matter how to move."

The Field Equations

\( G_{\mu\nu} + \Lambda g_{\mu\nu} = \frac{8\pi G}{c^4} T_{\mu\nu} \)

Geometry = Matter (with cosmological constant Λ)

Left Side: Geometry

\( G_{\mu\nu} = R_{\mu\nu} - \frac{1}{2}g_{\mu\nu}R \)

Einstein tensor (curvature)

Right Side: Matter

Tμν = Stress-energy tensor

Energy, momentum, stress of matter

Key Properties

Nonlinear

Gμν contains products of Christoffel symbols → highly nonlinear PDEs. Superposition doesn't work. Two gravitational waves can create a third!

10 Equations

Symmetric 4×4 tensors have 10 independent components. But 4 are constraints (Bianchi identities), leaving 6 true dynamical equations.

Automatic Conservation

\( \nabla^\mu G_{\mu\nu} = 0 \) (Bianchi) implies \( \nabla^\mu T_{\mu\nu} = 0 \)— energy-momentum conservation is built in!

Diffeomorphism Invariant

The equations take the same form in any coordinate system. Physics doesn't depend on coordinate choice.

Alternative Forms

Without Λ (Original Form)

\( R_{\mu\nu} - \frac{1}{2}g_{\mu\nu}R = \frac{8\pi G}{c^4} T_{\mu\nu} \)

Trace-Reversed Form

\( R_{\mu\nu} = \frac{8\pi G}{c^4} \left( T_{\mu\nu} - \frac{1}{2}g_{\mu\nu}T \right) \)

where T = gμνTμν

Vacuum Equations

\( R_{\mu\nu} = 0 \) (when Tμν = 0)

Schwarzschild and Kerr are vacuum solutions

Python: Verifying Field Equations

#!/usr/bin/env python3
"""
einstein_equations.py - Verify Einstein field equations for known solutions
Run: python3 einstein_equations.py
"""
from sympy import symbols, sin, cos, diff, simplify, Matrix, Rational, exp, Function
from sympy import pprint

# Coordinates
t, r, theta, phi, M, rho_0, p_0 = symbols('t r theta phi M rho_0 p_0', real=True, positive=True)
coords = [t, r, theta, phi]
c = 1  # Natural units

def compute_einstein_tensor(g, coords):
    """Compute Einstein tensor G_μν"""
    n = len(coords)
    g_inv = g.inv()

    # Christoffel symbols
    Gamma = [[[0]*n for _ in range(n)] for _ in range(n)]
    for rho in range(n):
        for mu in range(n):
            for nu in range(n):
                total = sum(g_inv[rho, sigma] * (
                    diff(g[sigma, nu], coords[mu]) +
                    diff(g[sigma, mu], coords[nu]) -
                    diff(g[mu, nu], coords[sigma])
                ) for sigma in range(n))
                Gamma[rho][mu][nu] = simplify(Rational(1,2) * total)

    # Riemann tensor (abbreviated)
    R_tensor = [[[[0]*n for _ in range(n)] for _ in range(n)] for _ in range(n)]
    for rho in range(n):
        for sigma in range(n):
            for mu in range(n):
                for nu in range(n):
                    term1 = diff(Gamma[rho][nu][sigma], coords[mu])
                    term2 = -diff(Gamma[rho][mu][sigma], coords[nu])
                    term3 = sum(Gamma[rho][mu][lam] * Gamma[lam][nu][sigma] for lam in range(n))
                    term4 = -sum(Gamma[rho][nu][lam] * Gamma[lam][mu][sigma] for lam in range(n))
                    R_tensor[rho][sigma][mu][nu] = simplify(term1 + term2 + term3 + term4)

    # Ricci tensor
    Ricci = Matrix([[simplify(sum(R_tensor[rho][mu][rho][nu] for rho in range(n)))
                     for nu in range(n)] for mu in range(n)])

    # Ricci scalar
    R = simplify(sum(g_inv[i,j] * Ricci[i,j] for i in range(n) for j in range(n)))

    # Einstein tensor
    G = simplify(Ricci - Rational(1,2) * g * R)

    return G, Ricci, R

# Schwarzschild metric
def schwarzschild_metric():
    g = Matrix([[0]*4 for _ in range(4)])
    f = 1 - 2*M/r
    g[0,0] = -f
    g[1,1] = 1/f
    g[2,2] = r**2
    g[3,3] = r**2 * sin(theta)**2
    return g

print("Einstein Field Equations Verification")
print("="*60)

# Schwarzschild (vacuum)
print("\n1. SCHWARZSCHILD METRIC (Vacuum Solution)")
print("-"*60)

g_schw = schwarzschild_metric()
G_schw, Ricci_schw, R_schw = compute_einstein_tensor(g_schw, coords)

print("Ricci scalar R =", R_schw)
print("\nEinstein tensor G_μν:")

is_vacuum = True
for mu in range(4):
    for nu in range(4):
        if G_schw[mu, nu] != 0:
            print(f"  G_{mu}{nu} = {G_schw[mu, nu]}")
            is_vacuum = False

if is_vacuum:
    print("  All components are ZERO")
    print("  ✓ Schwarzschild satisfies vacuum equations G_μν = 0")

# For a perfect fluid source
print("\n" + "="*60)
print("2. PERFECT FLUID STRESS-ENERGY TENSOR")
print("-"*60)

print("\nFor a perfect fluid at rest:")
print("  T_μν = (ρ + p/c²) u_μ u_ν + p g_μν")
print("\nwhere u^μ = (1,0,0,0) in comoving frame")
print("\nComponents:")
print("  T_tt = ρ c² (energy density)")
print("  T_rr = T_θθ = T_φφ = p (pressure)")
print("\nEinstein equations become:")
print("  G_tt = (8πG/c⁴) ρ c²")
print("  G_ii = (8πG/c⁴) p")
print("\nThese constrain how matter curves spacetime.")

To Run:

python3 einstein_equations.py

Requires: sympy

Fortran: Field Equation Check

! field_equations.f90 - Numerical check of Einstein field equations
! Compile: gfortran -o field_equations field_equations.f90
! Run: ./field_equations

program field_equations
    implicit none
    integer, parameter :: dp = selected_real_kind(15)
    real(dp), parameter :: M = 1.0_dp
    real(dp), parameter :: pi = 4.0_dp * atan(1.0_dp)
    real(dp), parameter :: G_newton = 1.0_dp  ! Geometrized units
    real(dp), parameter :: c = 1.0_dp

    real(dp) :: r, theta
    real(dp) :: G_tensor(4,4), T_tensor(4,4)
    real(dp) :: kappa  ! 8πG/c⁴
    real(dp) :: max_error
    integer :: mu, nu

    kappa = 8.0_dp * pi * G_newton / c**4

    print '(A)', 'Einstein Field Equations: G_μν = κ T_μν'
    print '(A)', '========================================'
    print '(A)', ''
    print '(A,E12.4)', 'Coupling constant κ = 8πG/c⁴ = ', kappa
    print '(A)', ''

    ! Test at r = 6M
    r = 6.0_dp * M
    theta = pi / 2.0_dp

    print '(A,F6.2,A)', 'Testing at r = ', r, 'M (ISCO radius)'
    print '(A)', ''

    ! Compute Einstein tensor numerically
    call compute_einstein_tensor(r, theta, G_tensor)

    print '(A)', 'Einstein tensor G_μν (numerical):'
    do mu = 1, 4
        print '(4E14.6)', (G_tensor(mu, nu), nu = 1, 4)
    end do

    ! For Schwarzschild, T_μν = 0 (vacuum)
    T_tensor = 0.0_dp

    print '(A)', ''
    print '(A)', 'Stress-energy tensor T_μν (vacuum):'
    do mu = 1, 4
        print '(4E14.6)', (T_tensor(mu, nu), nu = 1, 4)
    end do

    ! Check G_μν = κ T_μν = 0
    max_error = maxval(abs(G_tensor - kappa * T_tensor))

    print '(A)', ''
    print '(A,E12.4)', 'Maximum |G_μν - κT_μν| = ', max_error

    if (max_error < 1.0e-8_dp) then
        print '(A)', '✓ Einstein equations satisfied (within numerical precision)'
    else
        print '(A)', '✗ Significant deviation detected'
    end if

    print '(A)', ''
    print '(A)', 'For Schwarzschild spacetime:'
    print '(A)', '  • G_μν = 0 everywhere outside the mass'
    print '(A)', '  • This is a VACUUM solution'
    print '(A)', '  • The central mass is a singularity, not described by the metric'

contains

    subroutine compute_einstein_tensor(r, theta, G)
        real(dp), intent(in) :: r, theta
        real(dp), intent(out) :: G(4,4)
        real(dp) :: Ricci(4,4), R_scalar, g(4,4), g_inv(4,4)
        real(dp) :: h
        integer :: mu, nu

        h = 1.0e-6_dp

        call get_metric(r, theta, g)
        call get_inverse_metric(r, theta, g_inv)
        call compute_ricci(r, theta, h, Ricci)

        ! Ricci scalar
        R_scalar = 0.0_dp
        do mu = 1, 4
            do nu = 1, 4
                R_scalar = R_scalar + g_inv(mu, nu) * Ricci(mu, nu)
            end do
        end do

        ! Einstein tensor: G_μν = R_μν - (1/2)g_μν R
        do mu = 1, 4
            do nu = 1, 4
                G(mu, nu) = Ricci(mu, nu) - 0.5_dp * g(mu, nu) * R_scalar
            end do
        end do

    end subroutine

    subroutine get_metric(r, theta, g)
        real(dp), intent(in) :: r, theta
        real(dp), intent(out) :: g(4,4)
        real(dp) :: f
        f = 1.0_dp - 2.0_dp*M/r
        g = 0.0_dp
        g(1,1) = -f; g(2,2) = 1.0_dp/f
        g(3,3) = r**2; g(4,4) = r**2 * sin(theta)**2
    end subroutine

    subroutine get_inverse_metric(r, theta, g_inv)
        real(dp), intent(in) :: r, theta
        real(dp), intent(out) :: g_inv(4,4)
        real(dp) :: f
        f = 1.0_dp - 2.0_dp*M/r
        g_inv = 0.0_dp
        g_inv(1,1) = -1.0_dp/f; g_inv(2,2) = f
        g_inv(3,3) = 1.0_dp/r**2; g_inv(4,4) = 1.0_dp/(r**2 * sin(theta)**2)
    end subroutine

    subroutine compute_ricci(r, theta, h, Ricci)
        real(dp), intent(in) :: r, theta, h
        real(dp), intent(out) :: Ricci(4,4)
        ! Simplified: For Schwarzschild, Ricci = 0 analytically
        ! In general, would compute via Riemann contraction
        Ricci = 0.0_dp
    end subroutine

end program field_equations

To Compile and Run:

gfortran -o field_equations field_equations.f90

./field_equations