Compute covariant derivative in Mathematica
Here is some code I wrote a while back:
ClearAll["Global`*"]
SetAttributes[Rs, Constant]
$Assumptions = Rs > 0;
Coordinates = {t, r, \[Theta], \[Phi]};
dim = Length[Coordinates];
MetricTensorLL = {{(1 - Rs/r), 0, 0, 0}, {0, -(1 - Rs/r)^-1, 0,
0}, {0, 0, -r^2, 0}, {0, 0, 0, -r^2 Sin[\[Theta]]^2}};
MetricTensorUU := MetricTensorUU = Simplify[Inverse[MetricTensorLL]];
ChristoffelSymbolsULL :=
ChristoffelSymbolsULL =
Simplify@
Array[1/2 Sum[
MetricTensorUU[[#1, \[Lambda]]] (D[
MetricTensorLL[[#3, \[Lambda]]], Coordinates[[#2]]] +
D[MetricTensorLL[[\[Lambda], #2]], Coordinates[[#3]]] -
D[MetricTensorLL[[#2, #3]],
Coordinates[[\[Lambda]]]]), {\[Lambda], dim}] &, {dim,
dim, dim}];
ChristoffelSymbolsLLL :=
ChristoffelSymbolsLLL =
Simplify@
Array[1/2 (D[MetricTensorLL[[#1, #2]], Coordinates[[#3]]] +
D[MetricTensorLL[[#1, #3]], Coordinates[[#2]]] -
D[MetricTensorLL[[#2, #3]], Coordinates[[#1]]]) &, {dim, dim,
dim}];
RiemannCurvatureTensorULLL :=
RiemannCurvatureTensorULLL =
Simplify@
Array[D[ChristoffelSymbolsULL[[#1, #2, #4]], Coordinates[[#3]]] -
D[ChristoffelSymbolsULL[[#1, #2, #3]], Coordinates[[#4]]] +
Sum[ChristoffelSymbolsULL[[#1, #3, \[Epsilon]]] \
ChristoffelSymbolsULL[[\[Epsilon], #2, #4]], {\[Epsilon], dim}] -
Sum[ChristoffelSymbolsULL[[#1, #4, \[Epsilon]]] \
ChristoffelSymbolsULL[[\[Epsilon], #2, #3]], {\[Epsilon],
dim}] &, {dim, dim, dim, dim}];
RiemannCurvatureTensorLLLL :=
RiemannCurvatureTensorLLLL =
Simplify@
Array[Sum[
MetricTensorLL[[#1, \[Tau]]] \
RiemannCurvatureTensorULLL[[\[Tau], #2, #3, #4]], {\[Tau],
dim}] &, {dim, dim, dim, dim}];
RiemannCurvatureTensorUUUU :=
RiemannCurvatureTensorUUUU =
Simplify@
Array[Sum[
MetricTensorUU[[#2, \[Alpha]]] MetricTensorUU[[#3, \[Beta]]] \
MetricTensorUU[[#4, \[Gamma]]] RiemannCurvatureTensorULLL[[#1, \
\[Alpha], \[Beta], \[Gamma]]], {\[Alpha], dim}, {\[Beta],
dim}, {\[Gamma], dim}] &, {dim, dim, dim, dim}];
RiemannCurvatureTensorLL :=
RiemannCurvatureTensorLL =
Simplify@
Array[Sum[
RiemannCurvatureTensorULLL[[\[Lambda], #1, \[Lambda], #2]], {\
\[Lambda], dim}] &, {dim, dim}];
RiemannCurvatureTensorUL :=
RiemannCurvatureTensorUL =
Simplify@
Array[Sum[
MetricTensorUU[[#1, \[Lambda]]] RiemannCurvatureTensorLL[[\
\[Lambda], #2]], {\[Lambda], dim}] &, {dim, dim}];
ScalarCurvature := ScalarCurvature = Tr[RiemannCurvatureTensorUL];
KretschmannScalar :=
KretschmannScalar =
Simplify@
Sum[RiemannCurvatureTensorLLLL[[\[Alpha], \[Beta], \[Gamma], \
\[Delta]]] RiemannCurvatureTensorUUUU[[\[Alpha], \[Beta], \[Gamma], \
\[Delta]]], {\[Alpha], dim}, {\[Beta], dim}, {\[Gamma],
dim}, {\[Delta], dim}];
WeylCurvatureTensorLLLL :=
WeylCurvatureTensorLLLL =
Simplify@
Array[If[dim > 3,
RiemannCurvatureTensorLLLL[[#1, #2, #3, #4]] -
1/(
dim - 2) (MetricTensorLL[[#1, #3]] \
RiemannCurvatureTensorLL[[#4, #2]] +
MetricTensorLL[[#2, #4]] RiemannCurvatureTensorLL[[#3, \
#1]] - MetricTensorLL[[#1, #4]] RiemannCurvatureTensorLL[[#3, #2]] - MetricTensorLL[[#2, #3]] RiemannCurvatureTensorLL[[#4, \
#1]]) + ScalarCurvature/((dim - 1) (dim - 2)) (MetricTensorLL[[#1, #3]] MetricTensorLL[[#4, #2]] -
MetricTensorLL[[#1, #4]] MetricTensorLL[[#3, #2]]),
0] &, {dim, dim, dim, dim}];
EinsteinTensor :=
EinsteinTensor =
Simplify[
RiemannCurvatureTensorLL - 1/2 MetricTensorLL ScalarCurvature];
ConformallyFlatSpaceQ :=
ConformallyFlatSpaceQ =
Simplify[Equal[Sequence @@ Flatten@WeylCurvatureTensorLLLL, 0]];
MaximallySymmetricSpaceQ :=
MaximallySymmetricSpaceQ =
Simplify[
And @@ Flatten@
Map[# == 0 &,
RiemannCurvatureTensorLLLL -
Array[ScalarCurvature/(
dim (dim -
1)) (MetricTensorLL[[#1, #3]] MetricTensorLL[[#2, #4]] -
MetricTensorLL[[#1, #4]] MetricTensorLL[[#2, #3]]) &, \
{dim, dim, dim, dim}], {4}]];
It is hopefully self-explanatory.
In this particular example I am using the Schwarzschild metric. The first line declares Rs
, the Schwarzschild radius, to be a constant. We then define the coordinates of the manifold, in this case, $t,r,\theta,\phi$. After that, we input the components of the metric tensor, with its indices lowered (as indicated by the LL
tag, meaning "lower-lower").
The rest of tensors are calculated by MMA. For example, ChristoffelSymbolsULL
are the components of the Christoffel symbols, with one upper and two lower indices. Similarly, ChristoffelSymbolsLLL
are the components of the Christoffel symbols with all indices lowered.
The code also computes the Riemann tensor, the Weyl tensor, and several scalars (Ricci and Kretschmann). Finally, there is a check for whether the manifold is conformally flat and/or maximally symmetric.
To compute covariant derivatives, you can use the known value of the Christoffel symbols, or the expression
Sum[1/Sqrt[Det[MetricTensorLL]] D[Sqrt[Det[MetricTensorLL]] MetricTensorUU[[\[Mu] + 1, \[Nu] + 1]] D[f @@ Coordinates, Coordinates[[\[Nu] + 1]]], Coordinates[[\[Mu] + 1]]], {\[Mu], 0, dim - 1}, {\[Nu], 0, dim - 1}]
where f
is an arbitrary scalar function. For higher rank tensor fields, you'll have to make some small modifications to the code.
There is undocumented functionality in the SymbolicTensors package, which underlies CoordinateChartData
, CoordinateTransformData
, and the coordinate-system awareness of Grad
, etc. You could add the context to $ContextPath
save needing to type everywhere, though I won't do that in the example below.
First, you want to define a "patch". This can be done in various ways, but for a diagonal metric tensor the easiest way is scale factors. Here I'll use Minkowski in spherical coordinates as my example:
vars = {t, r, \[Theta], \[Phi]};
patch = SymbolicTensors`ScaleFactorGeometryPatch[{-1, 1, r, r Sin[\[Theta]]}, vars];
This patch is the equivalent of CoordinateChartData
for your custom metric. Take a look at patch["Properties"]
for some interesting things it will spit out.
Next, we need to define the two vector fields in the tensor language of the package. I'm just using the SlotSequence
notation to save myself some typing, The evaluated form would work just as well for input. Below, TangentBasis
is a representation of the coordinated basis for vector fields. There is a CotangentBasis
for the space of one-forms. If you wish to use, e.g., an orthonormal basis, the TangentBasis
with TransformedBasis
and supply the change of basis matrix in the second argument.
v = SymbolicTensors`Tensor[{vt[##], vr[##], v\[Theta][##],
v\[CurlyPhi][##]}, {SymbolicTensors`TangentBasis[{##}]}] & @@ vars
u = SymbolicTensors`Tensor[{ut[##], ur[##], u\[Theta][##],
u\[CurlyPhi][##]}, {SymbolicTensors`TangentBasis[{##}]}] & @@ vars
Finally, we want to do an actual computation. For this there is a CovariantD
which returns $\nabla_b v^a$, using the abstract index notation. CovariantD
has the same sytnax as Grad
and friends, and all of these take a patch in the third argument. (Note that Grad
returns the raised derivative $\nabla^bv^a$.) The covariant derivative along a vector fields is simply $u^b\nabla_bv^a$. Since all differentiation functions in M- add the new slot at the end, this then is simply:
SymbolicTensors`CovariantD[v, vars, patch] . u
If you don't like the fact u comes at the end, you could write this more verbosely as
TensorContract[
TensorProduct[u, SymbolicTensors`CovariantD[v, vars, patch]],
{1, 3}
]