Introduction

The question to answer in this notebook is “what is the vacuum spacetime geometry outside a static, spherically symmetric, non-spinning black hole?”
​
The Schwarzschild metric is the answer. In this notebook, we derive it ab-initio. The derivation is certainly not novel, but the novel contribution here is advancement of the  calculus, moving its compiler from proof-of-concept to prototype. This metric is demonstrated in another notebook, Interactive black hole dynamics: Schwarzschild orbits via UD calculus. The  calculus automates symbolic operations such as covariant differentiation, tensor quotienting, canonicalizing indices, etc. The  compiler translates between index notations in  and component representations in Wolfram arrays. A script version of this notebook, minus the prose, is available at https://github.com/rebcabin/RelativityToolkit/blob/main/schwarzschild_from_scratch.wl.

Audience

This series of notebooks is targeted at readers with an undergraduate’s STEM education—engineering, computer science, mathematics. Exposure to introductory courses in Classical Mechanics (e.g., Goldstein), Electrodynamics (e.g., Jackson), and Relativity (e.g., Schutz) will help.
◼
  • With the exception of MTW, I do not put standard textbooks in the References section, but instead offer clickable (non-affiliate!) links in the prose of this notebook.
  • Initialize the Relativity Toolkit

    The toolkit is currently implemented as a script rather than as a Package, prioritizing development flexibility and transparency over encapsulation. All symbols, including internal variables in Module constructs, are loaded into the Global namespace. That makes it difficult to find just the intentional exports of the toolkit without reading the code or the reference manual. This inconvenience will be mitigated in future versions.

    Optional: Quit for a Fresh Kernel

    ◼
  • Note, if you call Quit in a notebook, evaluation may hang. If you want to “Evaluate Notebook,” mark the following two cells “Evaluatable” in the Cell
    ->
    Property menu, call Quit and FrontEndTokenExecute once each, then comment out the cells, or mark them unevaluatable again. Alternatively, you can quit the kernel via the Evaluation menu (last item). At that point, you can “Evaluate Notebook” in a clean environment. Otherwise, you can evaluate cells in this notebook individually.
  • In[]:=
    Quit[];
    In[]:=
    FrontEndTokenExecute["DeleteGeneratedCells"]

    Set Download Loci and Load Main Script

    In[]:=
    RTbranch="v1.11.0";​​RTbase="https://raw.githubusercontent.com/rebcabin/RelativityToolkit/"<>RTbranch<>"/";​​RTbust="?t="<>ToString[Round[AbsoluteTime[]]];​​(*Forcefreshdownload*)​​RTrules=RTbase<>"RelativityToolkit.wl"<>RTbust;​​RTtests=RTbase<>"RelativityToolkit_RegressionTests.wl"<>RTbust;​​RTviz=RTbase<>"RelativityToolkit_VisualShowcase.wl"<>RTbust;​​Get[RTrules];
    »
    Relativity Toolkit version :1.11.0
    »
    Relativity Connection Symbol :Γ

    Optional: See all Tests and Results from Prior Works

    Make any of the following cells evaluatable if you wish. They’re unevaluatable by default to keep this notebook trim.
    Get[RTtests];
    In[]:=
    Get[RTviz](*nosemicolon,onpurpose*)
    In[]:=
    Get[RTbase<>"covariant_derivative.wl"]
    In[]:=
    Get[RTbase<>"law_of_gamma.wl"]
    Get[RTbase<>"connections_to_christoffels.wl"]
    Get[RTbase<>"calculate_riemann.wl"]

    Optional: Configure Relativity Connection Symbol

    To properly compose covariant derivatives,  must know the user’s reserved symbols for non-tensorial objects like connection coefficients. In gravitational physics, these force-producing connections are called Γ (capital gamma). Other theories like electrodynamics and Yang-Mills use other connection symbols.
    We’re going with Γ for this relativity/gravitation notebook, it’s the default, so the following cell is optional.
    In[]:=
    SetConnection[Γ];
    Relativity Engine: Connection set to Γ

    Solution Strategy

    The story of Karl Schwarzschild’s solution, while he was enlisted in the Prussian Army in WWI, of the Einstein field equations very soon after publication in 1915 is fascinating. Perhaps Karl followed a strategy like the following:
    1
    .
    Guess at the form of the Schwarzschild metric,
    g
    μν
    : that is, make an ansatz.
    2
    .
    Grind this form through new compiler components to generate (1) Christoffel symbols, (2) the Riemann curvature tensor
    μ
    R
    λνρ
    , and finally (3) the Ricci tensor
    R
    μν
    , which is a specific contraction of Riemann.
    3
    .
    Consult the Einstein field equations (not derived in this notebook):
    R
    μν
    -
    1
    2
    R
    g
    μν
    =
    8πG
    4
    c
    T
    μν
    (
    1
    )
    where
    R
    is the trace of Ricci, namely
    μν
    g
    R
    μν
    .
    4
    .
    Set the Einstein equations equal to zero for the vacuum (
    T
    μν
    =0
    , no stress-energy).
    5
    .
    Contract the Einstein equations against
    μν
    g
    to get the Ricci trace,
    R
    :
    μν
    g
    R
    μν
    -
    1
    2
    R
    μν
    g
    g
    μν
    =R-
    1
    2
    4R=0⟹R=0
    (
    2
    )
    6
    .
    Substitute this back into the Einstein equations to deduce that the all components of Ricci vanish in the vacuum:
    R
    μν
    -
    1
    2
    (0)
    g
    μν
    =0⟹
    R
    μν
    =0
    (
    3
    )
    7
    .
    Derive an ODE for terms in the ansatz from vanishing Ricci components.
    8
    .
    To solve the ODE, invoke boundary conditions at infinity, and invoke a Newtonian far-field approximation to evaluate constants of integration.
    9
    .
    Compare against Wolfram’s stored resource.
    ◼
  • I was fortunate to have Schwarzschild’s son, Martin, as one of my advisors in graduate school.
  • Introducing New Compiler Functions

     has been enhanced with several new functions in service of this task. The following paragraphs sketch their functionality, and the rest of the notebook illustrates their use by example. These functions are not specific to this task, however. In the future, we’ll need them for the Kerr metric (spinning black holes), as well as for other problems in general relativity, gauge theory, and differential geometry.
    In this notebook, “matrix” is a synonym for a Wolfram 2D array. A tensor is either a matrix or a collection of symbolic  expressions that satisfy coordinate-transformation rules.
    MatrixToUDRules
    Converts the non-zero elements of a matrix of components into rules for later application, via Replace and ReplaceAll, to symbolic expressions in .
    ChristoffelsFromMetric
    Computes torsion-free Christoffel symbols from a symmetric metric tensor written in  notation.
    Contract
    Contracts a given bound index in a  expression over a given set of coordinate symbols. Analogous to Wolfram built-in Replace.
    ContractAll
    Contracts all bound indices in a  expression over a given set of coordinate symbols. Analogous to Wolfram built-in ReplaceAll.
    EvaluateUDPartials
    Converts (compiles) partial derivatives  notation into forms that Wolfram can manipulate.
    MakeIndexer
    Makes a function that can index a matrix via symbolic coordinates rather than by Part numbers.
    CalculateRiemannComponent
    Given Christoffel symbols via an indexer, calculate a component of the Riemann curvature tensor, specified via natural coordinate symbols, by contracting over the Christoffel symbols.
    CalculateRicciComponent
    Given a Riemann tensor via an indexer, calculate a component of the Ricci tensor, a particular contraction of the Riemann tensor. Specify the desired component via natural coordinate symbols.
    See the source file RelativityToolkit.wl for implementation.

    Ansatz

    Set up a coordinate system in flat spacetime, at a safe distance from the central black hole, but with origin at the center of the black hole. Measure all events with respect to this coordinate system. Curvature of actual spacetime, as opposed to coordinate system, are accounted for by solving equations of motion with respect to proper time.
    Assume as a guess, or ansatz, the following form for an element of proper distance along a geodesic:
    d
    2
    s
    =-A(r)d
    2
    t
    +B(r)d
    2
    r
    +
    2
    r
    d
    2
    θ
    +
    2
    r
    2
    sin
    (θ)d
    2
    ϕ
    ≡​​
    g
    tt
    d
    2
    t
    +
    g
    rr
    d
    2
    r
    +
    g
    θθ
    d
    2
    θ
    +
    g
    ϕϕ
    d
    2
    ϕ
    (
    4
    )
    with two unknown functions of radius,
    A(r)
    and
    B(r)
    , and with symbolic coordinates,
    t,r,θ,ϕ
    , all assumed to be functions of proper time, τ, the time experienced by a particle in free-fall on a geodesic. Particularly,
    t(τ),r(τ),...,A(r(τ)),B(r(τ))
    are all functions of time. Notice these functions do not depend on the angle coordinates θ and ϕ. That is the meaning of “spherically symmetric.” They also do not depend on coordinate time, and that is the meaning of “static.”

    Covariant Metric Tensor

    Express the covariant metric tensor
    g
    μν
    of this ansatz as a diagonal matrix (Wolfram array); save the coordinates for later:
    In[]:=
    EchoSchwAnsatz=DiagonalMatrix[{-A[r],B[r],
    2
    r
    ,
    2
    r
    2
    Sin[θ]
    }]//MatrixForm,"Schw ansatz :";​​coords={t,r,θ,ϕ};
    »
    Schw ansatz :
    -A[r]
    0
    0
    0
    0
    B[r]
    0
    0
    0
    0
    2
    r
    0
    0
    0
    0
    2
    r
    2
    Sin[θ]
    ◼
  • Coordinate time,
    t
    , is also a function of proper time, i.e., it’s
    t(τ)
    , expressing time dilation.
  • ◼
  • The
    g
    tt
    component of
    g
    μν
    is negative because we adopt the
    (-1,1,1,1)
    sign convention.
  • Metric Rules

    Call the new compiler function, MatrixToUDRules, to create rules that map -indexed components of a given symbol, say gDD, to the components of the ansatz matrix. We’ll need these rules later to concretize abstract expressions.
    In[]:=
    Echo[SchwRules=MatrixToUDRules[SchwAnsatz,gDD,,coords],"Schw rules :"];
    »
    Schw rules :gDD
    ​
    t
    ​
    t
    -A[r],gDD
    ​
    r
    ​
    r
    B[r],gDD
    ​
    θ
    ​
    θ
    
    2
    r
    ,gDD
    ​
    ϕ
    ​
    ϕ
    
    2
    r
    2
    Sin[θ]
    
    ◼
  • For brevity, MatrixToUDRules doesn’t produce rules for zeros in the matrix. We’ll put those in later, by hand, in a concise way that exploits structural knowledge of
    g
    .
  • ◼
  • TODO: Sparse-matrix version of MatrixToUDRules that handles zero elements without manual intervention.
  • Contravariant Metric Tensor and Rules

    The contravariant version,
    μν
    g
    , of
    g
    is just the matrix inverse of the covariant version
    g
    μν
    (because
    μρ
    g
    g
    ρν
    =
    μ
    δ
    ν
    ). Ending with the needed rules for
    gUU
    , we get:
    In[]:=
    SchwAnsatzInv=Inverse[SchwAnsatz];​​Echo[SchwInvRules=MatrixToUDRules[SchwAnsatzInv,gUU,,coords],"Schw Inv rules :"];
    »
    Schw Inv rules :gUU
    t
    ​
    t
    ​
    -
    1
    A[r]
    ,gUU
    r
    ​
    r
    ​
    
    1
    B[r]
    ,gUU
    θ
    ​
    θ
    ​
    
    1
    2
    r
    ,gUU
    ϕ
    ​
    ϕ
    ​
    
    2
    Csc[θ]
    2
    r
    

    Christoffel Symbols

    The functions developed in an earlier notebook, Riemann Curvature in  + Compiler POC, are captured in a new compiler function, ChristoffelsFromMetric, which computes the required commutator of covariant derivatives. See it exposed here as an unevaluated contraction over a bound Unique index:
    In[]:=
    Echo[ChristoffelsFromMetric[gDD,gUU,σ,μ,ν],"Unevaluated Contraction :"];
    »
    Unevaluated Contraction :
    1
    2
    gUU
    σ
    ​
    λ.11
    ​
    
    gDD
    ​
    λ.11
    ​
    μ
    ,ν
    +
    gDD
    ​
    λ.11
    ​
    ν
    ,μ
    -
    gDD
    ​
    μ
    ​
    ν
    ,λ.11
    
    The new compiler function ContractAll expands a contraction as a sum over all coordinates. The up-down index pairs are now coordinate symbols rather than summation indices. Each term in the following exhibit is a product of concrete components, not a further (recursive) contraction. We can tell not to contract when a coordinate symbol, rather than a (usually Greek) index appears in up-down, paired index positions.

    Inspect One Component

    Now we can see clearly the reason for the rules above:

    Kill Off-Diagonal Elements

    Here are the promised extra rules to zero-out the off-diagonal components:
    ◼
  • Recall that MatrixToUDRules does not currently do this, for practical reasons.
  • Notice, again, the unevaluated derivatives.  doesn’t evaluate derivatives until you tell it to do so.
    ◼
  • Killing the off-diagonal terms is not strictly necessary in this step because the next function, EvaluateUDPartials, would have zeroed them out anyway. But it’s conceptually cleaner this way.
  • Evaluate  Partial Derivatives

    All Γ Components

    Now that we’re happy with a reduction strategy, apply it to all components of Γ in a new 3D matrix, one dimension for each index:
    ◼
  • Here we really needed to kill the off-diagonal terms manually to produce a visually tolerable result.
  • Γ Indexer

    To reduce opportunities for human error, we really want to index objects with coordinate symbols rather than with numerical Part or levelspec numbers. We’d rather write Γget[r,t,t] than Γ〚1,0,0〛. MakeIndexer is the new compiler function that makes a function that maps symbolic coordinates to Part expressions. Test Γget on an arbitrary component:

    The Riemann Ansatz in Full

    (Optional) Visualization

    ◼
  • Please tolerate some imprecision in this section for the sake of brevity.
  • Top-left towers represent tidal forces (boosts caused by boosts). Bottom-right towers represent spatial curvature. Off-diagonal towers in the Kerr metric indicate that the black hole’s rotation entwines these two sectors.

    Riemann Indexer

    As with Γ, index Riemann via coordinate symbols. Look at a couple of components and check a famous antisymmetry condition:

    The Ricci Ansatz

    Build Ricci via another new compiler function, CalculateRicciComponent, which contracts Riemann over its single up index and its second down index. Pass in Rget and the coords, and iterate over the remaining 16 index values. This is big but inspectable.

    The Einstein Vacuum Equations

    Of course, we’ll need a getter for Ricci.

    Final Equation and Solution

    Constant of Integration

    Demand the Newtonian limit: that the black hole behave like a normal Newtonian star when we are far away from it.

    Validation Against Wolfram Function Repository

    Pulling together all results:
    and comparing against the WFR resource
    we see a perfect match, except for WFR’s convention of formal symbols (with underdots).
    In this notebook, we derived the Schwarzschild metric for a static, spherically symmetric vacuum spacetime via , the symbolic tensor calculator and compiler.
    The primary contribution of this work is advancing  for deeper calculations in general relativity. Compiler components in the toolkit translate a symbolic layer in index notation to a matrix layer for processing by existing Wolfram resources. Additional compiler functions support indexing by coordinate symbols rather than by row number and column number, for improved readability.
    Validation points include:
    ◼
  • Symbolic Integrity:  handles index valence, canonicalization, and contraction.
  • ◼
  • Algebraic Simplification:  generates the Riemann and Ricci tensors and decouples the vacuum field equations.
  • ◼
  • Ground Truth Verification: The final derived solution matches the standard result in the Wolfram Function Repository exactly.
  • References

    1
    .
    Beckman, Brian. From the steam age to quantum fields: a unified approach via UD tensorial calculus. Wolfram Community post.
    2
    .
    Beckman, Brian. General relativity in the UD calculus. Wolfram Community post.
    3
    .
    Beckman, Brian. Formal Differential Geometry in the UD Calculus: Part 1. Wolfram Community post.
    4
    .
    Beckman, Brian. Covariant Derivatives and Connections in the  Calculus. Wolfram Community post.
    5
    .
    Beckman, Brian. Riemann Curvature in  + Compiler POC. Wolfram Community post.
    6
    .
    Beckman, Brian. Schwarzschild Orbits in the  Calculus. Wolfram Community post.
    7
    .
    Beckman, Brian, A Wizard's Quest: Spinning Black Holes in UD. Wolfram Community post.
    8
    .
    Wolfram Function Repository. MetricTensor. For ground truth.
    9
    .
    Wolfram Function Repository. ChristoffelSymbol. For ground truth.
    10
    .
    [MTW] Misner, Charles W.; Thorne, Kip S.; Wheeler, John Archibald. Gravitation. Princeton University Press, 2017.
    11
    .
    Petrov, A. Z. Classification of spaces defining gravitational fields, Scientific Transactions of Kazan State University, 114, 55 (1954)

    CITE THIS NOTEBOOK

    Schwarzschild metric from scratch via  compilation​
    by Brian Beckman​
    Wolfram Community, STAFF PICKS, February 16, 2026
    ​https://community.wolfram.com/groups/-/m/t/3640287​
    ​