Curvature using xTensor

Downloading and Installing xAct

Visit the page: http://www.xact.es
Follow installation instructions on http://www.xact.es/download.html
Linux: 
1. Download the tarball xAct_V.tgz (V is the version number)
2. sudo -i ; cd /usr/share/Mathematica/Applications/; tar xvfz ~/Downloads/xAct_V.tgz
Windows:
1. Download the zip file xAct_V.zip (V is the version number)
2. unzip its contents in C:\Program Files\Wolfram Research\Mathematica\<version>\AddOns\Applications\

Read the documentation:
http://www.xact.es/documentation.html
If you want to use xTensor, you will not avoid reading the full documentation. Better earlier than later:  xTensorDoc.nb
The reference notebook is useful too:  xTensorRefGuide.nb

The documentation is also installed locally, most likely in:
Linux:          /usr/share/Mathematica/Applications/xAct/Documentation/English/
Windows: C:\Program Files\Wolfram Research\Mathematica\<version>\AddOns\Applications\xAct\Documentation\English

Explore the documentation in xTensorDoc.nb.... Make a copy to the notebook, so that you can play with it.
cp /usr/share/Mathematica/Applications/xAct/Documentation/English/xTensorDoc.nb .
cp /usr/share/Mathematica/Applications/xAct/Documentation/English/xTensorRefGuide.nb .

Start an xTensor session

We define a 4-dim manifold M4, with metric g[-μ,-ν] = (Subscript[g, μν]), and a Christoffel connection with covariant derivative CD[-μ][T] = (Subscript[∇, μ])T

Needs["xAct`xTensor`"]

DefManifold[M4,4,{λ,μ,ν,ρ,σ,α,β,γ,δ}];
DefMetric[-1,g[-μ,-ν],CD];

The Riemann tensor

First define some toy tensors (u^μ), (v^μ), (Subscript[ξ, μ]), (Subscript[ω, μ]), (F^(μν)), (Subscript[S, μν])

DefTensor[u[   μ]        ,M4]; DefTensor[v[  μ],M4];DefTensor[w[  μ],M4];
DefTensor[ξ[-μ]        ,M4]; DefTensor[ω[-μ],M4];
DefTensor[F[   μ,  ν],M4, Antisymmetric[{μ,ν}]];
DefTensor[S[-μ,-ν],M4,Symmetric[{-μ,-ν}]];

Monoterm symmetries of the Riemann tensor are built in. To enforce them in an expression you have to act with ToCanonical

Print[
RiemannCD[-μ,-ν,-λ,σ] v[μ] u[ν] w[λ] ω[-σ],"\n",
RiemannCD[-μ,-ν,-λ,σ] v[μ] v[ν] w[λ] ω[-σ]," = ",
RiemannCD[-μ,-ν,-λ,σ] v[μ] v[ν] w[λ] ω[-σ]//ToCanonical
]

Multiterm symmetries of the Riemann tensor are harder to implement. Read section 9.2 of xTensorDoc.nb

eq1 = RiemannCD[-λ,-μ,-ν,σ] +RiemannCD[-ν,-λ,-μ,σ] +RiemannCD[-μ,-ν,-λ,σ] ;
Print[
eq1,"\n",
eq1//ToCanonical,"\n",
eq1//Simplification
]

Antisymmetrize can do the same work:

Print[
3 Antisymmetrize[RiemannCD[-λ,-μ,-ν,σ],{-λ,-μ,-ν}]," = ",
3Antisymmetrize[RiemannCD[-λ,-μ,-ν,σ],{-λ,-μ,-ν}]//Simplification
]

Contravariant Riemann has more symmetries:

Print[
"\n(Subscript[R, μν[ρσ]]) = ",2 Antisymmetrize[RiemannCD[-μ,-ν,-ρ,-σ],{-ρ,-σ}]//ToCanonical,
"\n(Subscript[R, μν (ρσ)]) = ",2         Symmetrize[RiemannCD[-μ,-ν,-ρ,-σ],{-ρ,-σ}]//ToCanonical,
"\n(Subscript[R, [μν] ρσ]) = ",2 Antisymmetrize[RiemannCD[-μ,-ν,-ρ,-σ],{-μ,-ν}]//ToCanonical,
"\n(Subscript[R, (μν) ρσ]) = ",2         Symmetrize[RiemannCD[-μ,-ν,-ρ,-σ],{-μ,-ν}]//ToCanonical,
"\n(Subscript[R, μνρσ])-(Subscript[R, ρσμν])=",RiemannCD[-μ,-ν,-ρ,-σ]-RiemannCD[-ρ,-σ,-μ,-ν]//ToCanonical
]

The last line is zero because:
(Subscript[R, μνλσ ])(ϵ^(μλαβ)) (R^(νσ))=(Subscript[R, λσ μν])(ϵ^(μλαβ)) (R^(νσ)) =-(Subscript[R, λσ μν])(ϵ^(λμαβ)) (R^(νσ)) =-(Subscript[R, μσ λν])(ϵ^(μλαβ)) (R^(νσ))=-(Subscript[R, μν λσ])(ϵ^(μλαβ)) (R^(σν))=-(Subscript[R, μν λσ])(ϵ^(μλαβ)) (R^(νσ))=0

Print[
RiemannCD[-μ,-ν,-λ,-σ]v[μ] u[ν] w[λ] w[σ]," = ",
RiemannCD[-μ,-ν,-λ,-σ]v[μ] u[ν] w[λ] w[σ] //ToCanonical,"\n",
RiemannCD[-μ,   μ,-λ,-σ]," = ",
RiemannCD[-μ,   μ,-λ,-σ]//ToCanonical,"\n",
RiemannCD[-μ,-ν,-λ,   λ]," = ",
RiemannCD[-μ, -ν,-λ,  λ]//ToCanonical,"\n",
RiemannCD[-μ,-ν,-λ,-σ] RicciCD[μ,ν]," = ",
RiemannCD[-μ,-ν,-λ,-σ] RicciCD[μ,ν]//ToCanonical,"\n",
RiemannCD[-μ,-ν,-λ,-σ] EinsteinCD[λ,σ]," = ",
RiemannCD[-μ,-ν,-λ,-σ] EinsteinCD[λ,σ]//ToCanonical,"\n",
RiemannCD[-μ,-ν,-λ,-σ] RicciCD[ν,σ] epsilong[μ,λ,α,β]," = ",
RiemannCD[-μ,-ν,-λ,-σ] RicciCD[ν,σ] epsilong[μ,λ,α,β]//ToCanonical (* you must use (Subscript[R, μνλσ ])= (Subscript[R, λσμν ]) to show that this is 0*)
]

Contractions of the Riemann Tensor:

(Subscript[R, μν])=((Subscript[R, μαν])^α) , R=((Subscript[R, μ])^μ)

Print[
"Ricci tensor:      ",RicciCD[-μ,-ν]," = ", RiemannCD[-μ,-α,-ν,α],"\n",
"Ricci scalar:      ",RicciScalarCD[]," = ",RicciCD[-μ,μ],"\n",
"Einstein tensor:   ",EinsteinCD[-μ,-ν], " = ",
RicciCD[-μ,-ν]-(1/2)g[-μ,-ν] RicciScalarCD[], " = ",
RicciCD[-μ,-ν]-(1/2)g[-μ,-ν] RicciScalarCD[] //RicciToEinstein//ToCanonical, " = ",
EinsteinCD[-μ,-ν] //EinsteinToRicci, "\n",
"Trace free Ricci:  ",TFRicciCD[-μ,-ν], " = ", TFRicciCD[-μ,-ν] //TFRicciToRicci,",    ((Subscript[S, μ])^μ)= ",TFRicciCD[-μ,μ],"\n",
"Weyl  tensor:      ",WeylCD[-μ,-ν,-ρ,-σ]," = ",WeylCD[-μ,-ν,-ρ,-σ]//WeylToRiemann
]

RiemannToChristoffel

((Subscript[R, μνλ])^σ)= -(∂_μ) (Subscript[(Γ^σ), νλ]) + (∂_ν) (Subscript[(Γ^σ), μλ]) - (Subscript[(Γ^σ), μα]) (Subscript[(Γ^α), νλ]) + (Subscript[(Γ^σ), να]) (Subscript[(Γ^α), μλ]) =- (Subscript[(ℛ^σ), λμν]) , where (Subscript[(ℛ^σ), λμν]) is the Riemann tensor defined in Carroll+Hartle’s book

Print[
RiemannCD[-μ,-ν,-λ,σ] , " = ",RiemannCD[-μ,-ν,-λ,σ]                                              //RiemannToChristoffel     //ScreenDollarIndices,"\n",
RicciCD[-μ,-ν]                    ," = ",RicciCD[-μ,-ν]                                                                  //RiemannToChristoffel     //ScreenDollarIndices,"\n",
RicciScalarCD[]                    ," = ",RicciScalarCD[]                                                                 //RiemannToChristoffel     //ScreenDollarIndices,"\n",
EinsteinCD[-μ,-ν]             ," = ",EinsteinCD[-μ,-ν]             //EinsteinToRicci    //RiemannToChristoffel     //ScreenDollarIndices,"\n",
WeylCD[-μ,-ν,-ρ,-σ]    ," = ", WeylCD[-μ,-ν,-ρ,-σ]     //WeylToRiemann        //RiemannToChristoffel     //ScreenDollarIndices
]

Using

Print[
RiemannCD[-μ,-ν,-λ,σ] , " = ",RiemannCD[-μ,-ν,-λ,σ]//RiemannToChristoffel //ChristoffelToGradMetric//ToCanonical//ScreenDollarIndices
]

Commutator of Derivatives

Definition of the Riemann tensor:
[(Subscript[∇, μ]),(Subscript[∇, ν])] (v^σ)= -((Subscript[R, μνρ])^σ)(v^ρ)     [(Subscript[∇, μ]),(Subscript[∇, ν])] (Subscript[ω, σ])= ((Subscript[R, μνσ])^ρ)(Subscript[ω, ρ])

Carroll+Hartle’s definition:
[(Subscript[∇, μ]),(Subscript[∇, ν])] (v^σ)= +(Subscript[(ℛ^σ), ρμν])  (v^ρ), so
(Subscript[(ℛ^σ), ρμν])=-((Subscript[R, μνρ])^σ) => (Subscript[ℛ, σρμν])=-(Subscript[R, μνρσ])=> (Subscript[ℛ, μνσρ])=-(Subscript[R, μνρσ])=>(Subscript[ℛ, μνρσ])=+(Subscript[R, μνρσ])  = (Subscript[ℛ, ρσμν])

Use SortCovDs and CommuteCovDs to change the order of the covariant derivatives:

Print[
"-----------------------------------------------------------\n",
"[(Subscript[∇, μ]),(Subscript[∇, ν])](v^σ)  = ",CD[-μ]@CD[-ν]@v[   σ]       -CD[-ν]@CD[-μ]@v[  σ]         ,"   =  ",CD[-μ]@CD[-ν]@v[   σ]        -CD[-ν]@CD[-μ]@v[  σ]         //SortCovDs//ScreenDollarIndices,"\n",
"[(Subscript[∇, μ]),(Subscript[∇, ν])](Subscript[ω, σ])  = ",CD[-μ]@CD[-ν]@ω[-σ]        -CD[-ν]@CD[-μ]@ω[-σ]        ,"   = "  ,CD[-μ]@CD[-ν]@ω[-σ]        -CD[-ν]@CD[-μ]@ω[-σ]         //SortCovDs//ScreenDollarIndices,"\n",
"[(Subscript[∇, μ]),(Subscript[∇, ν])](Subscript[F, αβ]) = ",CD[-μ]@CD[-ν]@F[-α,-β]-CD[-ν]@CD[-μ]@F[-α,-β],    " = "  ,CD[-μ]@CD[-ν]@F[-α,-β]-CD[-ν]@CD[-μ]@F[-α,-β]//SortCovDs//ScreenDollarIndices,"\n",
"-----------------------------------------------------------\n",
                                    CD[-μ]@CD[-ν]@v[   σ]              ," = ",                            CommuteCovDs[CD[-μ]@CD[-ν]@v[   σ],        CD,{-ν,-μ}]                                                      //ScreenDollarIndices,"\n",
"-----------------------------------------------------------\n"
]

Important property of the Einstein tensor:

Print[
"(Subscript[∇, μ]) (G^(μν)) = ",CD[-μ][EinsteinCD[μ,ν]]
]

Other properties:

Print[
"(Subscript[(R^μ), νμσ]) = ",RiemannCD[  μ,-ν,-μ,-σ],"\n",
"(Subscript[(W^μ), νμσ]) = ",WeylCD      [   μ,-ν,-μ,-σ],"\n",
"((Subscript[S, μ])^μ)  = ",TFRicciCD[-μ,μ]
]

Bianchi Identities

Taken from xTensor_Paris_A.nb, p16 (there they are also shown for covariant derivatives with torsion)

First Bianchi Identity:

Print[
"6 ((Subscript[R, [αβγ]])^δ) = ",
eq1 = 6 Antisymmetrize[RiemannCD[-α,-β,-γ,δ],{-α,-β,-γ}]," = ",
eq1//RiemannToChristoffel //ChristoffelToGradMetric //ToCanonical
]

Second Bianchi identity:

Print[
"6 (Subscript[∇, [α]) ((Subscript[R, βγ] δ])^ν) = ",
eq1 = 6 Antisymmetrize[ CD[-α][RiemannCD[-β,-γ,-δ,ν]],{-α,-β,-γ} ]," = ",
eq1//RiemannToChristoffel//CovDToChristoffel//ToCanonical
]

Assignment/Substitution

Functions: IndexSet, IndexSetDelayed, IndexRule, IndexRuleDelayed

Global assignment, like x = y, using IndexSet

(*UndefTensor[ttmp1];UndefTensor[ttmp2];UndefTensor[ttmp3];*)

DefTensor[ttmp1[-μ],M4,PrintAs-> "t"]; DefTensor[ttmp2[-μ,-ν,ρ],M4,PrintAs-> "T"];DefTensor[ttmp3[-μ],M4,PrintAs-> "q"];

IndexSet[ttmp1[-μ_], F[-μ,-ν] v[ν]];
Print[
ttmp1[μ]//ScreenDollarIndices,"      ", ttmp1[-μ]//ScreenDollarIndices,"\n",
ttmp1[-μ] ttmp1[-ν] g[μ,ν]//ScreenDollarIndices," = ",ttmp1[-μ] ttmp1[-ν] g[μ,ν]//ContractMetric//ScreenDollarIndices,"\n",
ttmp1[-μ] ttmp1[  μ]//ScreenDollarIndices
]

IndexSet[ttmp1[-μ_], F[-μ,-ν] v[ν]]; IndexSet[ttmp1[ μ_], F[ μ,-ν] v[ν]];
Print[
ttmp1[μ]//ScreenDollarIndices,"      ", ttmp1[-μ]//ScreenDollarIndices,"\n",
ttmp1[-μ] ttmp1[-ν] g[μ,ν]//ScreenDollarIndices," = ",ttmp1[-μ] ttmp1[-ν] g[μ,ν]//ContractMetric//ScreenDollarIndices,"\n",
ttmp1[-μ] ttmp1[  μ]//ScreenDollarIndices
]

IndexSetDelayed[ttmp2[-μ_,-ν_,ρ_]    ,              F[-μ, -ν]ttmp3[ρ]]; Print[ttmp2[-μ,-ν,ρ]//ScreenDollarIndices];
 IndexSetDelayed[ttmp3[ μ_]                         ,              S[   μ,-ν]           v[ν]]; Print[ttmp2[-μ,-ν,ρ]//ScreenDollarIndices];
 IndexSetDelayed[ttmp3[ μ_]                         , RicciCD[  μ,-ν]           v[ν]]; Print[ttmp2[-μ,-ν,ρ]//ScreenDollarIndices];

Rules: make substitutions without assignments

{v[   ν], 
v[   ν]                /. IndexRule[  v[  μ_],  g[  μ,  ν]    ω[-ν]]//ScreenDollarIndices,
ω[-ν],
ω[-ν]                /.IndexRule[  ω[-μ_],  g[-μ,-ν]v[  ν]]//ScreenDollarIndices}

The symbol ↦ has been introduced. It is input as ∖[RightTeeArrow],

{ω[μ]/.ω[-μ_]↦ g[-μ,-ν]v[ν],
ω[μ]/.ω[  μ_] ↦ g[  μ,-ν]v[ν]}

Mapping at specific positions: use xAct/ExpressionManipulation.m

<<xAct/ExpressionManipulation.m

eq1 = RiemannCD[-μ,-ν,-ρ,σ] u[μ] v[ν] w[ρ] + F[-μ,-ν] u[μ] RicciCD[ν,σ] +WeylCD[μ,σ]ω[-μ]

eq1 //ColorTerms

We can evaluate a function on given term: (xTensor_Paris_C.nb, page 5)

MapAt[RiemannToChristoffel,eq1,{1}] //ScreenDollarIndices

MapAt[RiemannToChristoffel,eq1,{{1},{2}}]//ScreenDollarIndices

Find a pattern in an expression:

eq1//ColorPositionsOfPattern[_RiemannCD]

MapAt[RiemannToChristoffel,eq1,Position[eq1,_RiemannCD]]//ScreenDollarIndices

Use MakeRule to make rules. Notice that MakeRule does not use patterns

rule1 = MakeRule[{ω[-μ] ,RicciCD[-μ,-ν] u[ν] }];
Print[
ω[-ν] u[ν]          ," = ",ω[-ν] u[ν]          /.rule1                                                          //ScreenDollarIndices
]

rule2 = MakeRule[{   ω[-μ]              ,CD[-ν][F[-μ,-ρ] u[ρ]u[ν]]},MetricOn->All,ContractMetrics->True,UseSymmetries->True];
rule3 = MakeRule[{ CD[-μ][u[ν]],0}];
Print[
ω[-μ] u[μ]                                                                                                                                                                  ," = ",
ω[-μ] u[μ]          /.rule2                                                                             //ScreenDollarIndices       ," = ",
ω[-μ] u[μ]          /.rule2                      //Simplification                //ScreenDollarIndices        ,"   ",
"(simplification notices that (u^μ)(u^β) (Subscript[∇, α])(Subscript[F, μβ]) = 0 due to antisymmetry\n\nWe set (Subscript[∇, μ])(u^ν)->0\n",
ω[-μ] u[μ]          /.rule2 /. rule3                                                         //ScreenDollarIndices       ," = ",
ω[-μ] u[μ]          /.rule2 /. rule3  //Simplification                //ScreenDollarIndices       
]

Geodesic Deviation

Acceleration: (v^ρ)(Subscript[∇, ρ])((v^ν)(Subscript[∇, ν])(u^μ)). 
Rules:
vgeod: (v^ν)(Subscript[∇, ν])(v^μ)->0                           (geodesic)
vu2uv: (v^ν)(Subscript[∇, ν])(u^μ)->(u^ν)(Subscript[∇, ν])(v^μ)              ([v,u]=0)
uv2vu: (u^ν)(Subscript[∇, ν])(v^μ)->(v^ν)(Subscript[∇, ν])(u^μ)
rleib:    (v^ρ)(Subscript[∇, μ])(Subscript[∇, ν])(v^σ)-> (Subscript[∇, μ])((v^ρ)(Subscript[∇, ν])(v^σ))-((Subscript[∇, μ])(v^ρ))((Subscript[∇, ν])(v^σ))                                            (simple Leibniz rule)
gleib:   (v^ρ)(Subscript[∇, μ])(Subscript[∇, ρ])(v^σ)-> (Subscript[∇, μ])((v^ρ)(Subscript[∇, ρ])(v^σ))-((Subscript[∇, μ])(v^ρ))((Subscript[∇, ρ])(v^σ))=-((Subscript[∇, μ])(v^ρ))((Subscript[∇, ρ])(v^σ))         (Leibniz rule + contraction + (v^ρ)(Subscript[∇, ρ])(v^σ)=0)

vgeod = MakeRule[{v[ρ] CD[-ρ] [v[μ]],0                                       },MetricOn->All,ContractMetrics->True,UseSymmetries->True];
vu2uv = MakeRule[{v[ρ] CD[-ρ] [u[μ]],u[ρ] CD[-ρ] [v[μ]]},MetricOn->All,ContractMetrics->True,UseSymmetries->True];
uv2vu = MakeRule[{u[ρ] CD[-ρ] [v[μ]],v[ρ] CD[-ρ] [u[μ]]},MetricOn->All,ContractMetrics->True,UseSymmetries->True];
rleib = MakeRule[{v[ρ]CD[-μ][CD[-ν][v[σ]]],CD[-μ][v[ρ] CD[-ν][v[σ]]]-CD[-μ][v[ρ]] CD[-ν][v[σ]]},MetricOn->All,ContractMetrics->True,UseSymmetries->True];
gleib = MakeRule[{v[ρ]CD[-μ][CD[-ρ][v[σ]]],                                                            -CD[-μ][v[ρ]] CD[-ρ][v[σ]]},MetricOn->All,ContractMetrics->True,UseSymmetries->True];
Print[
" (v^ρ) (Subscript[∇, ρ])((v^ν) (Subscript[∇, ν])(u^μ)) = "                                                                                                                                                                 ,"\n               = ",
tmp1 = v[ρ] CD[-ρ] @(v[ν] CD[-ν]@ u[μ]/.vu2uv)//Expand                    //ScreenDollarIndices  ,"\n               = ",
tmp2 = tmp1 /.vu2uv                                                                                                          //ScreenDollarIndices  ,"\n               = ",
tmp3 = CommuteCovDs[tmp2,CD,{-α,-ρ}]                     //Expand                     //ScreenDollarIndices   ,"\n               = ",
tmp4 = tmp3 /.gleib                                                                                                         //ScreenDollarIndices   ,"\n               = ",
tmp4//ToCanonical,
"\n----------------------------------------------------------------------"
]

Killing Vectors

Show that (Subscript[∇, μ])(Subscript[∇, ν])(Subscript[ξ, λ])= ((Subscript[R, λνμ])^ρ)(Subscript[ξ, ρ])
Difficulty: there is no algorithm at present in xTensor` to canonicalize expressions with multiterm symmetries, like those of the Riemann tensor. See section 9.2 in xTensorDoc.nb

rkill = MakeRule[{CD[-μ] [ξ[-ν]] , - CD[-ν] [ξ[-μ]]   }];
Print[
tmp0  =CD[-μ] @ CD[-ν]@ξ[-λ]," = "                                                                                                                                                               ,"\n       =", (**)
tmp1 =CommuteCovDs[tmp0,CD,{-ν,-μ}]//ScreenDollarIndices                                                                                                       ,"\n       =", (*Commute Derivatives*)
tmp2  = tmp1 /.rkill                                                                                                                                                                                                  ,"\n       =", (*Use Killing Equation*)
tmp3  = CommuteCovDs[tmp2,CD,{-λ,-ν}]//ScreenDollarIndices                                                                                                     ,"\n       =", (*Commute Derivatives*)
tmp4  = tmp3 /. rkill                                                                                                                                                                                                 ,"\n       =", (*Use Killing Equation*)
tmp5  = CommuteCovDs[tmp4,CD,{-μ,-λ}]//ScreenDollarIndices                                                                                                     ,"\n       =", (*Commute Derivatives*)
tmp6  = tmp5 /.rkill                                                                                                                                                                                                  ,"\n       =", (*Use Killing Equation*)
tmp7  = tmp6 //ToCanonical                                                                                                                                                                                    ,"\n       =", (*Put indices in order*)
tmp8  = tmp7 /.   RiemannCD[-λ,-α,-μ,-ν] ->    RiemannCD[-μ,-ν,-λ,-α]                                                                                ,"\n       =", (*Use symmetry (Subscript[R, λαμν])=(Subscript[R, μνλα]). We use simple rule since we want only one term substituted*)
tmp9  = tmp8  /.  RiemannCD[-λ,-μ,-ν,-α] -> -RiemannCD[-ν,-λ,-μ,-α]-RiemannCD[-μ,-ν,-λ,-α]//Expand,"\n       =", (*We use muliterm symmetry (Subscript[R, [λμν]α])=0*)
tmp9//ToCanonical (*Only one index needs to be permuted, ToCanonical knows about it. What remains is the identity that we want to prove*)
]

We have shown that:  (Subscript[∇, μ])(Subscript[∇, ν])(Subscript[ξ, λ]) = 2 (Subscript[R, λνμα])(ξ^α)-(Subscript[∇, μ])(Subscript[∇, ν])(Subscript[ξ, λ])=>     2 (Subscript[∇, μ])(Subscript[∇, ν])(Subscript[ξ, λ]) = 2 (Subscript[R, λνμα])(ξ^α) =>       (Subscript[∇, μ])(Subscript[∇, ν])(Subscript[ξ, λ]) =  (Subscript[R, λνμα])(ξ^α)

Acknowledgements

This notebook has been programmed by Konstantinos Anagnostopoulos, Physics Department, National Technical University of Athens, Greece, while he was an instructor of the 4th year undergraduate course "General Relativity and Cosmology". It was created for fun, but it may turn out to be useful to everyone studying the General Theory of Relativity for the first time.
Email: konstant@mail.ntua.gr
Web:    http://physics.ntua.gr/konstant 
It is offered under a GPL/CC BY 4.0 license (in that order, depending on whether they apply on the programming part or the text part of the notebook).