xAct`xCoba`

xCoba` is the xAct` package for tensor component computations. These are computations in which tensors are expressed as collections of scalar components (or coefficients) in a pre-defined basis of vector fields (or tensor products of them). Those components can be numbers or functions of the scalar fields of a given coordinate system or chart. It is, therefore, complementary to the abstract (aka geometric) approach of xTensor`, where all computations are basis-free and coordinate-free. In xCoba` the word "basis" is fully equivalent to the word "frame".

xCoba` allows the user to define bases on one or more vector bundles and to handle basis vectors, using basis indices notation. It performs component calculations such as expanding a tensor in a specified basis, changing the basis of an expression or tracing the contraction of basis dummies. xCoba` stores and handles component values efficiently, making full use of tensor symmetries. The package knows how to express derivatives and brackets of basis vectors in terms of Christoffel and torsion tensors and how to assign values to the components of a tensor. And many other things.

xCoba` is fully compatible with xTensor`. In fact, many computations in xCoba` are performed using the xTensor` functions. Note, however, that xTensor` is  independent of xCoba`.

A crucial ingredient is the formalism of marked indices and particularly the treatment of frame dependent objects. Perhaps the most conspicuous example is the covariant parallel derivative, which extends the concept of ordinary derivative as described on Wald's General Relativity. This reference has been the main inspiration for much of the notation, and the user is advised to keep it in mind when dealing with some of the non standard ideas employed by this package.

There are several ways of storing the components of a tensor, and xCoba` implements three of them:

1) Explicit linear combinations of Basis tensors.

2) A matrix (or array) based approach, using CTensor[...].

3) Implicit storage of components of an abstract tensor.

Each method has pros and cons, and you'll need to decide which method is most convenient for your computation. In general, the CTensor[...] approach is the easiest for simple manipulations, like computing all curvature tensors of a metric field expressed in a given coordinate system. The implicit storage method can be useful when working with computations that still have a strongly abstract flavor, or a high degree of symmetry. The explicit linear combinations can be useful for typesetting results. xCoba` provides functions to work in each of these representations, and also to change from one to another. This notebook contains descriptions of the three approaches and their relations.

Historically (in the development of xAct`), method 1) was developed first, then method 3) and finally method 2) above. In hindsight, the CTensor[...] method 2) is the most natural and simplest of them, and we recommend you to start with it if you are new to xCoba`. You can right now go to section 3 of this document.

Load the package

This loads the package from the default directory, for example $HOME/.Mathematica/AddOns/Applications/xAct/ for a single-user installation under Linux. xTensor` and xPerm` are automatically loaded. See these [Installation notes] for help with installation.

<<xAct`xCoba`

Hide dollar-indices, as usual (see xTensor` documentation for more details on this):

$PrePrint=ScreenDollarIndices;

0. xCoba` essentials

A session with xCoba` begins just like one with xTensor`, defining one or more manifolds and vector bundles and the objects living on them.

Define a 3d manifold M3, with indices a...f:

DefManifold[M3, 3, {a,b,c,d,e,f}]

Define a complex vector bundle (the warnings are purely informative and harmless):

DefVBundle[InnerC,M3,2,{A,B,C,D,F,G},Dagger->Complex]

Define a contravariant vector v and a covariant symmetric tensor T:

DefTensor[v[a],M3]
DefTensor[T[-a,-b],M3,Antisymmetric[{-a,-b}]]

Define a Riemann symmetric tensor and a covariant vector on InnerC:

DefTensor[R[-A,-B,-C,-D],M3,RiemannSymmetric[{1,2,3,4}],Dagger->Complex]

DefTensor[u[-A],M3,Dagger->Complex]

Remember that in xTensor` we use abstract indices:

T[-a,-b]v[b]

A basis (or frame) is a set of independent vector fields on a vbundle, with an associated dual basis of vector fields. We only declare the name of the basis and the associated cnumbers ("component numbers"), integers that will identify the individual vectors/covectors. xCoba` then constructs a number of geometric objects associated to the basis. We will review them in detail further down.

Now xCoba`  lets us define a basis, called fred. We have to specify its vector bundle and cnumbers. Several related objects are also defined (parallel derivative and associated tensors and densities):

DefBasis[fred,TangentM3,{0,1,2}]

These are the basis vector fields. We use covariant C-indices ("component indices") marked with their basis, with form {cnumber, -basis}:

basis={Basis[{0,-fred},a],Basis[{1,-fred},a],Basis[{2,-fred},a]}

BasisArray[fred][a]

And these are the basis covector fields. We use contravariant C-indices of the form {cnumber, basis}:

cobasis={Basis[-a,{0,fred}],Basis[-a,{1,fred}],Basis[-a,{2,fred}]}

BasisArray[fred][-a]

The so-called parallel derivative of the fred basis (denoted PDfred) is a covariant derivative operator with the fundamental property of giving zero on all elements of the basis and its cobasis:

PDfred[-b]/@basis

PDfred[-b]/@cobasis

As any other covariant derivative operator, it has curvature and torsion. Parallel derivatives always have zero curvature, but they have torsion if they are associated to non-coordinate (or anholonomic) bases. In xCoba` the defined bases are non-coordinate by default:

Riemann[PDfred][-a,-b,-c,d]

Torsion[PDfred][a,-b,-c]

Each basis has a color (red by default), used to identify its associated objects in StandardForm. Define another basis, colored blue:

DefBasis[blues,TangentM3,{0,1,2},BasisColor->Blue]

For example, these are the parallel derivatives of the bases acting on a given tensor field:

PDfred[-a][T[-b,-c]]

PDblues[-a][T[-b,-c]]

We now define bases on the complex vector bundle. This generates additional "inner" objects for its parallel derivative (FRiemann and AChristoffel):

DefBasis[fcomp,InnerC,{4,5},Dagger->Complex,BasisColor->Green]

Once we have defined bases, the first task is to construct tensors. The simplest way is to build linear combinations of basis vectors with constant coefficients. xCoba`  also offers a matrix/array representation using the head CTensor.

Linear combination of the basis vector fields:

{Sqrt[3],4/5,-2}.basis

Find a particular component using the dual basis:

% Basis[-a,{1,fred}]

Contraction of basis elements is not automatic and needs to be forced with ContractBasis:

%//ContractBasis

We can do the same thing with abstract tensors. This is the 1-component of the vector v:

v[a]Basis[-a,{1,fred}]

%//ContractBasis

Such object is a scalar, and not a vector. It is a scalar component of the vector v, and hence has no free index:

%//IndicesOf[Free]

These are all the components of a tensor in given bases:

ComponentArray[T[{-a,-blues},{-b,-blues}]]

Notice that T is a symmetric tensor, so this can be simplified

Simplification[%]//MatrixForm

These are all contractions of the basis and cobasis vectors:

Outer[Times,basis,cobasis]//MatrixForm

%//ContractBasis

Our example vector can also be represented in CTensor notation. The fact that these are components in the polar basis is represented by the red vertical bars:

w=CTensor[{Sqrt[3],4/5,-2},{fred}]

w[a]

Convert to an explicit linear combination:

%//ToBasisExpand

CTensor objects can be manipulated like tensor heads in xTensor`.

xTensorQ[w]

w[a]w[b]

Basis vectors and transition matrices are represented by the two-index object Basis. Without a metric, only the down-up configuration is allowed:

Basis[{4,-fcomp},A]

Catch@Basis[A,{4,-fcomp}]

Basis[{-a,-fred},{b,blues}]

Hence, this is the vector w in a different basis:

ToCTensor[w,{blues}]

Hover over the "blobs" to see the vector components. Note the new color of the vertical bars:

%[a]

ContractBasis is quite general and allows the user to specify which indices should be acted upon. The default is total contraction:

expr=Basis[{1,-fred},a]Basis[-c,{1,blues}]v[-a,-b,c,{d,fred}]Basis[{-d,-fred},e]

ContractBasis[expr]

ContractBasis[expr,fred]

ContractBasis[expr,a]

The inverse function is SeparateBasis, also quite general. It can undo the action of ContractBasis and also change the basis of the whole expression

cexpr=ContractBasis[expr]

SeparateBasis[AIndex][cexpr]

SeparateBasis[blues][cexpr]

Another important features is the ability to trace basis contractions,

TraceBasisDummy[v[{a,blues}]T[{-a,-blues},-b]]

Finally, we will have a quick look at the third method to store components, which is useful for partially-abstract computations, or cases with a high degree of symmetry.

We can assign rules to the components of a tensor taking the symmetries into account

values=Table[i-j,{i,3},{j,3}];

AllComponentValues[T[{-a,-fred},{-b,-fred}], values]

The FoldedRule notation represents a way of storing nested rules:

ColumnForm/@TensorValues[T]

As we can see, dependent and independent components are stored in separate sublists. We can change basis:

$CVVerbose=False;

ChangeComponents[T[-{a,blues},-{b,blues}],T[-{a,fred},-{b,fred}]]

ColumnForm/@TensorValues[T, {{-blues, -blues}}]

Clean up:

DeleteTensorValues[T]

$CVVerbose=True;

UndefBasis/@{fred,blues};

Removing a complex basis also removes its complex conjugate:

UndefBasis[fcomp];

UndefTensor/@{R,T,u,v};

UndefVBundle[InnerC]

UndefManifold[M3]

Remove[basis,cobasis,expr,cexpr,values,i,j,w]

1. Algebra and bases

1.1. Bases

1.1.1. DefBasis

This section will explain how to define and work with new bases, without assuming that they have an underlying coordinate chart. We will focus on the algebraic manipulations, ignoring the fact that our basic objects are actually fields, except for a few examples involving differentiation. Later on in this notebook, we will show how to work with charts, and how to perform derivatives of several kinds.

Definition of a basis.

Declare a manifold and some tensors:

DefManifold[M3,3,{a,b,c,d,e,f}]

DefTensor[v[a],M3]

DefTensor[T[-a,-b],M3,Antisymmetric[{1,2}]]

Declare a complex vector bundle and a vector field on it:

DefVBundle[InnerC,M3,2,{A,B,C,D,F,G},Dagger->Complex]

DefTensor[u[-A],M3,Dagger->Complex]

To define a basis we only have to provide a name, a vector bundle and a list of cnumbers ("component numbers"), whose length must be the dimension of the vector bundle. The cnumbers can include 0 and even negative integers:

DefBasis[fred,TangentM3,{0,1,2}]

Several other objects are automatically defined: the parallel derivative and its torsion, Christoffel, Riemann and Ricci tensors. We shall say more about them later on. This is the name of the parallel derivative of the defined basis:

PDOfBasis[fred]

Each basis has a color, used to identify its associated indices and objects in StandardForm. The default for new bases is red, but we can specify a different color:

DefBasis[blues,TangentM3,{0,1,2},BasisColor->Blue]

A basis has several associated definitions and UpValues:

?fred

This is the list of bases we have defined so far:

$Bases

1.1.2. Basis objects

Now that we have bases, we can use basis vectors and work with tensor components. Two new kinds of indices are needed:
	 {a, basisname} :  Basis index (BIndex),  a is an abstract index.
	 {i, basisname} :  Component index (CIndex), i is a valid component number.
When we define a basis in the tangent bundle, its dual in the cotangent bundle is also automatically defined. This dual basis is called -basisname and has the same cnumbers as the original. The notation for covariant bc-indices is 
	{-a, -basisname} :  Basis index (BIndex),  a is an abstract index.
	{i,  -basisname} :  Component index (CIndex), i is a valid coordinate number.
Notice how a covariant b-index is the negative of the corresponding contravariant index, but this is not true for c-indices. This apparent inconsistency is a conscious choice: it allows us to use zero and negative cnumbers (the latter being common in treatments of angular momentum, tensor harmonics, etc.)
NOTE: In versions 0.3 and 0.4 we used b-indices like {a, polar}, {-a, polar} and c-indices like {1, polar} and {-1, polar}. This notational change is not backwards compatible. We apologize to those users of xCoba` who now need to change lots of signs in previously developed notebooks, but we feel that the use of this new notation will pay off.

Several functions are provided to check validity:

IndicesOfVBundle[TangentM3]

indexlist={{a,fred},{-a,-blues},{w,fred},{A,fred},{1,fred},a,{1,-blues},{5,fred},{-1,-fred},{a,other}};

$Bases

BIndexQ/@ indexlist

CIndexQ /@ indexlist

An extra function selects both basis and component indices:

BCIndexQ /@ indexlist

On occasion, it is interesting to know whether an index is either an AIndex or a BIndex. Those are the only types that may be contracted, which makes them important for functions such as ContractBasis. We will call them `contractible indices'.

ABIndexQ /@ indexlist

Notice that we can get a False output for several reasons: The basis, abstract index or cnumber might not exist or they might belong to different vbundles.

If we use these indices inside of tensors, Mathematica will display them according to BasisColor,

T[{1,-blues},{2,-blues}] v[{a,fred}]

The Basis tensor is formally xTensor's delta tensor, but we prefer to separate Basis in xCoba`. Depending on its indices, the meaning will change, and in particular when both its indices are abstract, it will autoconvert into delta:
	- Basis[ AIndex, BCIndex] :	Basis vector (or dual basis covector).
	- Basis[ AIndex, AIndex]: 	Identity tensor in the corresponding vbundle (turns into δ).
	- Basis[ BCIndex, BCIndex]:	If the indices belong to different bases, it represents a basis change.
						If they belong to the same basis, we have a Kronecker delta (products of the elements of dual bases).

Basis is output as an e, except when it converts into delta:

{Basis[{1,-fred},a], Basis[-a,b], Basis[{-a,-blues},{b,fred}],Basis[{-a,-blues},{b,blues}],  Basis[{1,-fred},{1,fred}], Basis[{1,-fred},{2,fred}]}

A Basis object with two indices of the same character (covariant or contravariant) is converted into a metric tensor. If we  have defined more than one metric, the first one is used (see xTensorDoc.nb for details).

Catch@Basis[-a,-b]

DefMetric[-1,metricg[-a,-b],CD,SymbolOfCovD->{";","▽"},PrintAs->"g"]

{Basis[-a,-b], Basis[{a,blues},{b,blues}], Basis[{a,blues},{b,fred}]}

Now other index configurations are allowed, but transformed automatically into the original down-up configuration:

{Basis[a,-b],Basis[{a,fred},{-b,-blues}]}

When a vector bundle has a metric, delta and Basis inherit the symmetry of that metric.

SymmetryGroupOfTensor[Basis[{1,fred},{-2,blues}]]

UndefMetric[metricg]

More on Basis contraction.

An important issue is when to contract Basis objects with other tensors and with one another:

ContractBasis/@{
Basis[-a,{b,fred}]Basis[{-c,-blues},a],
Basis[{1,-fred},a]Basis[-a,{1,fred}],
Basis[{-b,-fred},a]Basis[-c,{b,fred}]}

It would be desirable to automatize this behaviour sometimes. We can do it with the command AutomaticBasisContractionStart[]:

Contraction of Basis objects.

AutomaticBasisContractionStart[]

{Basis[-a,{b,fred}]Basis[{-c,-blues},a],
Basis[{1,-fred},a]Basis[-a,{1,fred}],
Basis[{-b,-fred},a]Basis[-c,{b,fred}]}

AutomaticBasisContractionStop[]

When the contraction is between a Basis object and a different tensor, it is only performed if the former is a formal Kronecker delta or an identity tensor,

{Basis[-a,b]T[-b,-c],
Basis[{-a,-fred},{b,fred}] v[{-b,-fred}],
Basis[{-a,-fred},b]v[-b],
Basis[{-a,-blues},{b,fred}]v[{-b,-fred}]}

To force the contraction in the last two cases we need a new function (next section). Notice how the third contraction is equivalent to expressing a tensor in its components and the fourth represents a basis change.

Basis formatting. We can define a special name  for the elements of one particular basis.

Specify the format of a given basis vector object:

FormatBasis[{1,fred},"n"]

{Basis[-a,{1,fred}],Basis[{1,-fred},a],Basis[-a,{2,fred}],Basis[{1,-blues},{1,fred}],Basis[{1,-blues},{2,fred}]}

FormatBasis[{2,fred},"l"]

{Basis[-a,{1,fred}],Basis[{1,-fred},a],Basis[-a,{2,fred}],Basis[{1,-blues},{1,fred}],Basis[{1,-blues},{2,fred}]}

We remove the special names:

FormatBasis[{1,fred}]
FormatBasis[{2,fred}]

Two more details.

A bookkeeping detail: Basis is considered to be a tensor whose master is Symbol, so it cannot be undefined:

xTensorQ[Basis]

MasterOf[Basis]

Basis and Dir act as inverses of each other

Dir[Basis[-a,{b,fred}]]

Basis[Dir[v[a]],{1,fred}]

1.1.3. Bases on inner bundles and complex bases

Starting with version 0.9, xTensor` is capable of dealing with complex tensors and vector bundles. The function DefBasis has been extended accordingly to allow the user to define complex bases, through the option Dagger, taking the values Real or Complex.

A basis on a real vbundle is real by default:

Dagger[fred]

But let us see what happens when we define a basis on a complex bundle:

$Bases

DefBasis[fcomp,InnerC,{-1,+1},Dagger->Complex,BasisColor-> Green]

$Bases

A conjugate basis has also appeared on the conjugate manifold:

Dagger/@{fcomp,fcomp†}

VBundleOfBasis/@{fcomp,fcomp†}

Notice how negative cnumbers do not denote covariant indices:

{Basis[-a,{-1,fcomp}],Basis[-a,{-1,fcomp†}]}

The basis objects of both bases look the same, because

Dagger[Basis]

Dagger[Basis[-a,{-1,fcomp}]]

InputForm[%]

and their cnumbers are also defined to be the same. This can be changed, with the new function DaggerBCIndex. Remember how in xTensor` the conjugates of indices were given by

DaggerIndex/@{a,b,A,B}

But we cannot change the definition associated to DaggerIndex[{1,fcomp}],  because complex is too deep to be given an upvalue. Let us see how we can use  DaggerCIndex to solve this with an example. We want to complexify a real basis:

DefBasis[test,TangentM3,{1,2,3}]

?DaggerCIndex

test/:DaggerCIndex[test,{1,test}]={2,test};
test/:DaggerCIndex[test,{2,test}]={1,test};
test/:DaggerCIndex[test,{1,-test}]={2,-test};
test/:DaggerCIndex[test,{2,-test}]={1,-test};

And now:

Dagger/@{Basis[-a,{1,test}],Basis[{2,-test},a]}

If we define a complex tensor on M3 we can see that this works

DefTensor[J[a],M3,Dagger-> Complex]

Dagger[J[{2,test}]]

We can even establish more complicated relations, if we remember that Basis and Dir act as inverses of each other

test/:DaggerCIndex[test,{1,test}]=Dir[I Basis[-a,{2,test}]];
test/:DaggerCIndex[test,{2,test}]=Dir[I Basis[-a,{1,test}]];

Dagger[J[{2,test}]]

UndefBasis[test];

1.1.4. BasisArray

Listing basis objects

The function BasisArray returns a list of all basis vectors of a given basis:

BasisArray[blues][a]

BasisArray[fcomp][A]

If we give several indices, the product basis is displayed:

BasisArray[fred,fred][a,b]

We can mix bases:

BasisArray[fred,blues,fcomp][a,c,-B]//MatrixForm

1.2. Basis indices

1.2.1. IndicesOf

In xTensor`, the main user level function to find indices in an expression was IndicesOf[selectors][expression]. Let us start by reviewin that function.

Define several tensors:

DefTensor[TT[-a,-b,c,d,-e],M3]
DefTensor[U[-a,-b,c,d],M3]
DefTensor[S[-A,B],M3,Dagger->Complex]

Construct an expression containing them:

expr=TT[{1,-fred},-b,{2,blues},c,-a]Basis[{1,-fred},a]+Basis[{1,-fred},a]Basis[-d,{2,blues}]U[-a,-b,d,{e,fred}]Basis[{-e,-fred},c]S[{-A,-fcomp},B]S[-B,{A,fcomp}]

Find different types of indices (See xTensorDoc.nb, section 9.1):

IndicesOf[Free][expr]

IndicesOf[TangentM3][expr]

IndicesOf[InnerC][expr]

IndicesOf[U,Free][expr]

IndicesOf[Free,U][expr]

IndicesOf[{U,Free}][expr]

xCoba` adds more possibilities. We can now use the types BIndex and CIndex as selectors, or search for a particular basis

IndicesOf[{BIndex,CIndex}][expr]

IndicesOf[fred][expr]

Consider now the following example

IndicesOf[Basis,fred][expr]

IndicesOf[Basis[fred]][expr]

The first selects all the indices appearing in Basis objects and then selects from those only the ones that belong to polar. The second selects only those indices that belong to a Basis object of the polar basis, but returns the pair, not only the BCIndex.

IndicesOf[Basis[fred]][Basis[{-a,-fred},{b,blues}]]

This function will be used heavily to control the behaviour of ContractBasis and SeparateBasis.

1.2.2. ContractBasis

ContractBasis is responsible for all contractions of Basis with any other object.

Contraction of Basis objects with tensors.

We have complete control over which dummy indices should be eliminated. We can select them in several ways, all of which are eventually converted into a list of indices with head IndexList:
	- IndexList[...],		
be careful not to use the normal Mathematica List, which could be mistaken for a BCIndex. IndicesOf[selectors] as a second argument is treated as IndicesOf[selectors][expr] and gives great flexibility, but it is sometimes too long for simple cases. To handle them, several shorthands are defined: 
	- ContractBasis[expr,basis] 	= ContractBasis[expr,IndicesOf[basis]],
	- ContractBasis[expr,vbundle]	= ContractBasis[expr,IndicesOf[vbundle]],
	- ContractBasis[expr,tensor]	= ContractBasis[expr,IndicesOf[tensor]],
	- ContractBasis[expr,index]	= ContractBasis[expr,IndexList[index]] .
It is important to keep in mind that the second argument gives the list of indices to be contracted, not their pairs in the Basis object. 
All of this is best illustrated through some examples.

Take again example expressions:

expr

expr2=Basis[{-e,-blues},b]Basis[-A,{1,fcomp}] S[-B,A]v[{e,blues}]

The function called with just one argument performs all the possible contractions:

ContractBasis[expr]

If we choose to give the indices directly, we can specifiy either element of the pair:

{{ContractBasis[expr2, A], ContractBasis[expr2,-A]},
{ContractBasis[expr2,{-e,-blues}], ContractBasis[expr2,{e,blues}]}}//TableForm

Nothing happens if we give an invalid index:

ContractBasis[expr2,C]

More simple examples:

ContractBasis[expr,IndicesOf[AIndex]]

ContractBasis[expr,IndicesOf[TT,U]]

ContractBasis[expr,IndicesOf[{TT,U}]]

ContractBasis[expr2,InnerC]

ContractBasis[expr,IndexList[a,-d]]

ContractBasis[expr,fred]

Notice how only the index {-e, -bas} has been supressed. Nothing has been done to a, even though its pair in the Basis object belongs to polar.  If we want to act on that index, we can use the following to contract everything that appears in a Basis object that has at least one polar index:

ContractBasis[expr, IndicesOf[Basis[fred]]]

Or this to contract only abstract indices whose pair in Basis belongs to bas:

ContractBasis[expr, IndicesOf[Basis[fred],AIndex]]

Notice that some specifications can be redundant; IndicesOf[Basis, x] is equivalent to IndicesOf[x], because all the indices that can be contracted by this function already have to appear in Basis.

Contraction of Basis objects can be performed with ContractBasis, or automatized:

Basis[-a,{b,blues}]Basis[{-c,-fred},a]

ContractBasis[%]

AutomaticBasisContractionStart[]

Basis[-a,{b,blues}]Basis[{-c,-fred},a]

AutomaticBasisContractionStop[]

The default behaviour of ContractBasis disregards contractions involving derivatives. We must use the option OverDerivatives:

DefCovD[CD[-a],SymbolOfCovD->{"#","D"}]

Basis[-b,{1,blues}]CD[-a][S[c,b]]

ContractBasis[%]

ContractBasis[%,OverDerivatives-> True]

A new Christoffel tensor is automatically defined. We shall see more about the interplay between bases and derivatives in Section 5. Any type of derivate exhibits the same behaviour:

lieexpr=LieD[v[a]][T[-a,-b]]Basis[{1,-fred},a]Basis[{3,-fred},b]

{ContractBasis[lieexpr],ContractBasis[lieexpr,OverDerivatives->True]}//TableForm

paramexpr=OverDot[T[-a,-b]]Basis[{1,-fred},a]Basis[{3,-fred},b]

{ContractBasis[paramexpr],ContractBasis[paramexpr,OverDerivatives->True]}//TableForm

expr2=.
lieexpr=.
paramexpr=.

UndefCovD[CD]

The input expression is automatically expanded (with Expand) before applying ContractBasis. It would be better, in principle, to contract the bases only locally, but this is not yet implemented.
At the moment, the user cannot specify in which order the indices should be contracted. An option ContractFirst is planned but not yet coded.

1.2.3. SeparateBasis

Expansion of an expression in a given basis.

SeparateBasis works in a similar way to ContractBasis, but now we have two brackets. In the second one we can specify which indices should be `separated' from their original tensor. In the first one we say what should remain in their place (either AIndex or a basis).

Take another example expression:

expr=TT[{1,-fred},-b,{2,blues},c,-a]Basis[{1,-fred},a]+Basis[{1,-fred},a]Basis[-d,{2,blues}]U[-a,-b,d,{e,fred}]Basis[{-e,-fred},c]

cexpr=ContractBasis[expr]

SeparateBasis[AIndex][cexpr]

Simplification[%]

But we may also want to perform a change of basis. This is done as follows

SeparateBasis[blues][cexpr]

Simplification[%]

Now all tensor indices belong to the specified basis.

We have the same syntax as in ContractBasis for the second argument:

SeparateBasis[AIndex][cexpr,{4,blues}]

SeparateBasis[AIndex][cexpr,IndicesOf[blues]]

SeparateBasis[AIndex][cexpr,IndicesOf[CIndex]]

When separating component indices, the function selects only those with valid coordinate numbers:

v[{1,fred}]v[{2,fred}]v[{7,fred}]//SeparateBasis[AIndex]

We must be careful when working with scalar functions of scalars, lest we get meaningless outputs, such as powers of tensor products:

v[{1,fred}]//SeparateBasis[AIndex]

v[{1,fred}]v[{1,fred}]//SeparateBasis[AIndex]

%% ^2

The safe way to manipulate such expressions is via the Scalar head:

Scalar[v[{1,fred}]]Scalar[v[{1,fred}]]

%//SeparateBasis[blues]

%/.Scalar[scalar_]:> Scalar[SeparateBasis[blues][scalar]]

If a component index is repeated, we may need to apply SeparateBasis several times:

expr4=T[{1,-fred},{1,-fred}]

%//SeparateBasis[AIndex]

It works, but if we specify the index:

SeparateBasis[AIndex][expr4,{1,-fred}]

{1, -fred} appears twice in the same tensor, so we need to reapply SeparateBasis (or pass it twice to the function). TODO: Is this correct?:

SeparateBasis[AIndex][%,{1,-fred}]

SeparateBasis[AIndex][expr4,IndexList[{1,-fred},{1,-fred}]]

1.2.4. ToBasis

Safe replacement of basis indices.

We may want to replace the indices in an expression by indices in a different basis.  Sometimes this is easy enough:

SeparateBasis[fred][v[a]v[-a]]

%//ContractBasis

And it may seem that the ReplaceIndex function could take care of the more difficult situations. But we must watch for pitfalls such as:

ReplaceIndex[v[a]PD[-b][v[-a]],{a->{a,fred},-a->{-a,-fred}}]

This is wrong, because
	(v^b)(Subscript[∇, a]) (Subscript[v, b]) = ((v^c)(Subscript[(e^( b)), c])) (Subscript[∇, a])(((Subscript[e, b])^d)(Subscript[v, d])) ≠ (v^b)(Subscript[∇, a]) (Subscript[v, b])

We can arrive at the correct result via a roundabout way, with Contract and SeparateBasis:

SeparateBasis[fred][v[a]PD[-b][v[-a]]]

ContractBasis[%]

The functions *ToBasis automate this process:

DummyToBasis[fred][v[a]PD[-b][v[-a]]]//Simplification

Notice how the derivative of the Basis object has been replaced by the appropriate Christoffel. Further examples:

expr=S[-A,B]v[a]v[c]v[-c]

FreeToBasis[blues][%]

ToBasis[blues][%]

FreeToBasis[fcomp][%]

ToBasis[fred][%,{a,blues}]

Many times we have expressions such as:

T[{-a,-fred},{-b,-fred}]v[{a,fred}]v[{b,fred}]

where we had defined (Subscript[T, ab]) to be antisymmetric. Therefore this is zero:

Simplification[%]

However, if we have:

v[-a]v[a]-v[{-a,-fred}]v[{a,fred}]

Simplification[%]

we have to change the whole expression to a single basis first:

ToBasis[AIndex][%]

1.3. Tracing contractions

One of the most important functions of xCoba`  is TraceBasisDummy. We already know how to express a tensor on a given basis, as a contraction with Basis objects. With this new function, we can expand that contraction as a sum of tensor components.

Tracing basis contractions.

Again, there is complete control over which dummies should be expanded. The complete syntax is TraceBasisDummy[expr, indices], where indices is one of the following:
	- A single BIndex.
	- A list of basis indices with head Indices.
	- A vbundle (all basis dummies belonging to it will we expanded).
	- A basis

This is the simplest example:

TraceBasisDummy[v[{a,blues}]v[{-a,-blues}]]

Further examples with a more complicated expression:

traceexpr=S[{-A,-fcomp},{A,fcomp}]T[{-a,-fred},{-b,-blues}]v[{a,fred}]v[{b,blues}]

TraceBasisDummy[traceexpr]//Simplify

TraceBasisDummy[traceexpr,TangentM3]//Simplify

TraceBasisDummy[traceexpr,fred]//Simplify

TraceBasisDummy[traceexpr,blues]//Simplify

With derivatives:

TraceBasisDummy[PD[{-b,-fred}][v[{b,fred}]]]

(u[B]+S[{1,-fcomp},{A,fcomp}]S[{-A,-fcomp},B])+ PD[{A,fcomp}][S[{-A,-fcomp},B]](u[{C,fcomp}]+S[{-D,-fcomp},{C,fcomp}]u[{D,fcomp}])u[{-C,-fcomp}]

TraceBasisDummy@%

Simplify[%]

Another example:

TraceBasisDummy[PDfred[{-a,-blues}][v[{a,blues}]]]

SeparateBasis[AIndex][%]//Expand

1.4. Arrays of components

1.4.1. ComponentArray and TableOfComponents

Arrays of components

The commands described in this subsection essentially generate lists of c-indices from free b-indices (or free a-indices through ToBasis). The basic command is ComponentArray,.

Construct the list of components of a vector in a given basis:

ComponentArray[v[{a,fred}]]

NOTE: Up to version 0.6, the role of ComponentArray was played by a different function, called Components.

If the object to expand is a tensor (and only in this case), we can use a shorter notation:

ComponentArray[T,{-fred,-fred}]

We can mix bases:

ComponentArray[T,{-fred,-blues}]

Component indices are not expanded:

ComponentArray[T[{-a,-fred},{1,-fred}]]

We can specify which indices to expand:

ComponentArray[T[-{a,fred},-{b,fred}] v[{c,blues}],IndexList[{-a,-fred},{c,blues}]]

ComponentArray[T[-{a,fred},-{b,fred}] v[{c,blues}],IndicesOf[T]]

But c-indices are never expanded:

ComponentArray[v[{1,fred}],IndexList[{1,fred}]]

The order of indices matters:

ComponentArray[v[{a,fred},{b,fred}]]

ComponentArray[v[{b,fred},{a,fred}]]

ComponentArray avoids clashes with dummy indices (notice the use of Scalar):

ComponentArray[Scalar[v[{a,fred}]v[{-a,-fred}]]v[{-a,-fred}]]

If we want more flexibility, we can use TableOfComponents. This function, whose syntax mimics that of the standard Mathematica Table, is much more flexible, but also slower. The reason for this is that TableOfComponents uses ToBasis internally, which performs many checks.  Now we specifiy `iterators' on basis indices as additional arguments. If the specified index {a, basis} was already in the expression, this just tells the function when to expand it (i.e. in what order with respect to the remaining indices). If it was not present but a appeared as an abstract index, the system first turns it into a basis index and then expands it:

Components of a tensor:

TableOfComponents[T[-{a,fred},-{b,fred}],-{a,fred},-{b,fred}]

TableOfComponents[T[-a,-b],-{a,fred},-{b,fred}]

Notice how basis indices mimic the behaviour of Mathematica iterators. We have been able to expand an expression which contained only abstract indices. This is equivalent to doing:

ToBasis[fred][T[-a,-b]]

ComponentArray[%]

However, we cannot change the basis of a BIndex already in the expression:

TableOfComponents[T[{-a,-fred},-b],{-a,-blues}]

If we do not specify any index, the argument is returned unchanged (compare with ComponentArray):

TableOfComponents[v[{a,fred}] ]

ComponentArray[v[{a,fred}]]

The order is again important:

TableOfComponents[T[a,b],{a,fred},{b,fred}]

TableOfComponents[T[a,b],{b,fred},{a,fred}]

As ToBasis is able to handle derivatives, we can include them in the arguments of TableOfComponents:

TableOfComponents[PD[-a][v[b]],-{a,fred},{b,blues}]//ScreenDollarIndices//MatrixForm

TraceBasisDummy[%]

If we wanted to obtain this result from (∂_a) (v^b) using ComponentArray, we would have to be much more careful.

Several vbundles:

TableOfComponents[v[a] u[A], {A,fcomp},{a,blues}]

These functions do not take the symmetries into account automatically, but we can simplify:

ComponentArray[T[{a,fred},{b,fred}]]//ToCanonical//MatrixForm

Currently there is no function to construct a single component, because the notation must be nearly as long as the direct construction itself! Coba can be used to do that, in case the same component must be constructed for several different tensors.

1.4.2. ThreadComponentArray. TODOcument

1.5. Tensor densities; η and ϵ tensors and determinants

1.5.1. The η tensors and the Jacobian

Definition of a basis.

The η tensors represent the totally antisymmetric products of all basis (co)vectors:
		((OverTilde[η])^((Subscript[a, 1])⋯  (Subscript[a, n])))= n! (Subscript[(e^([(Subscript[a, 1]))), 1]) ⋯ (Subscript[(e^((Subscript[a, n])])), n]), 		(Subscript[(Underscript[η, ~]), (Subscript[a, 1])⋯  (Subscript[a, n])])= n! ((Subscript[e, [(Subscript[a, 1])])^1) ⋯  ((Subscript[e, (Subscript[a, n])]])^n)
In xCoba`, these objects are automatically defined for each basis.

Totally antisymmetric densities for a given basis:

{etaUpfred[a,b,c],etaDownfred[-a,-b,-c]}

The basis is marked with an over or undertilde, whose significance we shall see below. Their components in the natural basis are very simple:

{etaUpfred[{1,fred},{2,fred},{3,fred}],
etaUpfred[{1,fred},{3,fred},{2,fred}],
etaUpfred[{3,fred},{1,fred},{2,fred}],
etaUpfred[{1,fred},{1,fred},{1,fred}]}

And they are related to the generalised Kronecker delta:

etaDownfred[-a,-b,-c]etaUpfred[d,e,f]

The presence of a metric provides additional relations, as we shall see later. Each basis has a different pair of η tensors but they are all related through the Jacobians of the transformations. Because of this, η are generally considered not tensors, but tensor densities with weight = ± basis. In xCoba` we can also consider them tensors, but dependent on a reference basis. If we change the reference basis (e.g., from etaUppolar to etaUpcartesian), we have to include Jacobians in the transformation. However, if we don't change the reference basis (e.g., from etaUppolar[{a,polar},{b, polar}, {c,polar}] to etaUppolar[{a,cartesian}, {b,cartesian}, {c, cartesian}]) they transform as tensors. This is analogous to our treatment of Christoffel tensors.

Consider this example:

etaUpfred[a,b,c] etaDownblues[-d,-e,-f]

This product is a density of weight +1 if we change the basis polar and of weight -1 if we change cartesian. In order to convey all this information, we represent weigths as linear combinations of the names of the bases:

WeightOf[%]

If we worked with a single basis, a weight n *basisname could be understood as the traditional integer weight n

WeightOf[etaUpfred[a,b,c] etaUpfred[d,e,f]]

The Jacobian is represented as

Jacobian[blues,fred][]

It is a scalar with weight +1 with respect to cartesian and -1 with respect to polar, so it has two tildes of different colours.

WeightOf[%]

Jacobian[blues,fred][]*Jacobian[fred,blues][]

xCoba`  knows how to compute derivatives of Jacobians

PD[-a][Jacobian[blues,fred][]]//ScreenDollarIndices

LieD[v[a]][Jacobian[blues,fred][]]//ScreenDollarIndices

ContractBasis[%,OverDerivatives->True]//Simplify

1.5.2. Determinants

Determinants

We can define the determinant of a tensor in any given basis through the η tensors:

Determinant[T[a,b],fred]

WeightOf[%]

Notice how the determinant depends on the basis, unless there are the same number of contravariant and covariant indices:

DefTensor[T2[a,-b],M3,PrintAs-> "T"]

Determinant[T2[a,-b],fred]

WeightOf[%]

If a tensor has an odd number of indices, its determinant is zero:

DefTensor[T3[a,b,c],M3,PrintAs-> "T"]

Determinant[T3[a,b,c],blues]

ToCanonical[%]

DefTensor[T4[a,b,c,-d],M3,PrintAs-> "T"]

Determinant[T4[a,b,c,-d],blues]//ToCanonical

Even if we know that the determinant of a given object is going to be basis independent, we must provide some basis:

{Determinant[delta[a,-b]], Determinant[delta[a,-b],fred]}

UndefTensor /@ {T2,T3,T4};

1.5.3. The determinant of the metric and the ϵ tensor

Determinant of the metric; relation between η and ϵ tensors

The determinant of the metric is a weight 2 density. When a metric is defined, its determinant in the priviledged basis AIndex is also defined. We also parametrize the orientation of the volume form of the metric with respect to the eta tensors of a basis:

DefMetric[-1,metric[-a,-b], cd,{"|","D"},PrintAs-> "g",epsilonOrientationInBasis->{blues,σ}]

Detmetric[]

In any other basis:

Determinant[metric,fred][]

Once we have a metric, we can build new tensors (with zero weight) from the etaUp and etaDown tensors. These new ϵ tensors depend only on the metric:

epsilonmetric[a,b,c]

WeightOf[%]

etaUp[blues][a,b,c]/.etaUpToepsilon[blues,metric]

%/.epsilonToetaDown[metric,blues]

%%/.epsilonToetaUp[metric,blues]

The relative orientation of the volume form with respect to the eta tensors of a basis is stored in the following function:

epsilonOrientation[metric,blues]

UndefMetric[metric]

2. Differentiation and charts

2.1. Parallel derivatives and Christoffel tensors

2.1.1. Partial derivatives and parallel derivatives

xTensor` performs abstract (coordinate-free) computations, so there is no real notion of chart in xTensor`. However, xTensor` has a PD operator, mainly to be able to convert covariant derivatives of tensors into "partial derivatives" of tensors plus Christoffel terms. Following Wald, we interpret PD as a arbitrary (but fixed) choice of a flat (zero curvature) and torsionless covariant derivative. Under such conditions, it is possible to find (at least locally) a coordinate system in which such PD covariant derivative corresponds directly to the partial derivatives with respect to the scalar fields of the chart. In that sense, the arbitrary choice of PD represents an arbitrary choice of chart, but we never specify any other chart in xTensor`, so there is no notion of "coordinate change". xTensor` is (by design) very limited regarding coordinate systems, precisely to make sure that all your computations are coordinate free.

xCoba` now allows you to declare any number of charts on each given manifold, and transition functions among them. Again, this is actually only a local statement, and the Manifold objects of xTensor are just open submanifold; xAct is not yet able to perform global computations by itself. Associated to each chart we will define a "partial derivate", which will be implemented as a flat and torsionless covariant derivative as well.

xCoba` goes a bit beyond that and generalizes the concept of "partial derivative" associated to a coordinate system or frame. We will introduce the concept of "parallel derivative" to any basis of vector fields, both coordinated or not. In the former case the concept of "parallel derivative" collapses to that of "partial derivative". In the latter case the "parallel derivative" is still flat, but now has torsion. We will see that such torsion is equivalent to the Lie brackets of the basis vectors.

Finally, any pair of covariant derivatives is related by a Christoffel tensor, so in xCoba we will handle routinely Christoffel tensors relating two parallel derivatives. These will be Christoffel tensors with two different colored derivatives.

We use PD... both to mean "Partial Derivative" and "Parallel Derivative", depending on the context. We find this amusing, and hope that doesn't lead to confusion.

We begin by remembering that each basis has its own parallel derivative

{PDfred[-a][v[b]],PDblues[-a][T[-b,-c]]}

defined so that it vanishes on every vector field of that basis (this is the origin of the notion of "parallelism"):

PDfred[-a][Basis[-c,{b,fred}]]

This derivative is always flat, with vanishing Riemann :

RiemannPDfred[-a,-b,-c,-d]

But it has torsion, unless the basis is coordinated:

TorsionPDfred[a,-b,-c]

This torsion tensor is equivalent to the Lie brackets of the basis fields:

Bracket[Basis[{-b,-fred},b],Basis[{-c,-fred},b]][a]

In xTensor` we can define the Christoffel tensor connecting any two covariant derivatives (cf. Section 6 of xTensorDoc.nb). In particular, this works with the parallel derivatives of our bases:

Christoffel[PDblues,PDfred][a,-b,-c]

%//InputForm

As usual, Christoffel is antisymmetric in its derivative arguments, and automatically reverse them for canonicalization:

Christoffel[PDfred,PDblues][a,-b,-c]

%//InputForm

Notice how the Christoffel connecting any covariant derivative to the fiducial PD is defined with the derivative, in our case by DefBasis.

Christoffel[PDfred,PD][a,-b,-c]

Any derivative of any Basis object can be translated into a component of Christoffel tensors relating that derivative to the PDs of the bases involved. This is one of the fundamental building blocks of xCoba`:

PD[-a][Basis[-b,{1,fred}]]

PD[-a][Basis[{-b,-fred},{c,blues}]]

With another parallel derivative:

PDfred[-a][Basis[{1,-fred},b]]

PDfred[-a][Basis[{1,-blues},b]]

PDblues[-a][Basis[-b,{1,fred}]]

PDblues[-a][Basis[{-b,-fred},{c,blues}]]

2.1.2. Parallel derivatives of complex bases

Complex bases have FRiemann and AChristoffel tensors, as explained in xTensorDoc, section 6.8:

?AChristoffelPDfcomp

The FRiemann is also zero

FRiemannPDfcomp[-A,-B,-C,-D]

Derivatives of complex bases are also replaced by the corresponding Christoffels

PD[-a][Basis[{-A,-fcomp},C]]

PD[-a][Basis[{-A†,-fcomp},C†]]

PD[-a][Basis[{-c,-fcomp},b]]

PDfred[-b][Basis[{-A,-fcomp},C]]

Both basis indices must belong to the same vbundle:

Catch@PD[-a][Basis[{-c,-fcomp},B]]

2.1.3. Ricci rotation coefficients

Ricci rotation coefficients are not supported as separate objects, because they do not add any new information. xCoba`  limits itself to converting  RicciRotation[covd,basis] into Christoffel[covd, PDofBasis[basis]].

Ricci rotation coefficients of a covariant derivative in a given (coordinated or not) frame:

RicciRotation[PDfred,blues][a,-b,-c]

RicciRotation[PDblues][a,-b,-c]

2.2. Charts

2.2.1. Define a chart

A chart (or coordinate system) in xCoba` is just a particular type of basis: a coordinated basis. The main difference is that together with the frame we also declare a number of scalar fields, the coordinate scalar fields of the chart, whose gradients are aligned with the frame. Any other scalar field will be expressible as a scalar function (in the sense of the ScalarFunction type of xTensor`) of those coordinate scalar fields.

Definition of a chart.

Declare a coordinate system called csys, whose scalar fields as x[], y[] and z[]. As usual in xAct, we need the empty pairs of brackets to denote scalar fields:

DefChart[csys,M3,{1,2,3},{x[],y[],z[]},ChartColor->Orange]

The frame or basis of the chart has the same name. Its partial derivative is:

PDOfBasis[csys]

Notice that now the torsion tensor vanishes and the Christoffel is symmetric.

Torsion[PDcsys][a,-b,-c]

Bracket[Basis[{1,-csys},b],Basis[{2,-csys},b]][a]

TODO: Why is csys repeated?

ChartsOfManifold[M3]

ChartQ /@ {csys,fred}

BasisQ /@ {csys,fred}

The derivative of a scalar field is a (coordinated) basis vector field:

PD[-a][z[]]

It does not matter which derivative do we actually use, because all covariant derivatives coincide on scalars:

PDcsys[-a][z[]]

It is still typeset using the standard typesetting of frame fields. Undefine the chart and define it again, with a more "coordinated" formatting:

UndefChart[csys]

DefChart[csys,M3,{1,2,3},{x[],y[],z[]},ChartColor->Orange,FormatBasis->{"Partials","Differentials"}]

Now these are the coordinate vector fields of the frame, in a typesetting that reminds us of their "directional derivative" meaning:

basis=BasisArray[csys][a]

And these are the coordinate covector fields of the frame:

cobasis=BasisArray[csys][-a]

The cobasis can be obtained with any derivative operator:

PD[-a]/@{x[],y[],z[]}

Take any scalar function and compute its gradient covector:

DefScalarFunction[func]

grad=PDcsys[-a][func[x[],y[],z[]]]

2.2.2. Coordinate expressions

xCoba` follows xTensor` in the fact that all expressions are full tensor fields, and not function representations in a given chart. That is, we will always work with functions of scalar fields, like Sin[x[]], and not just functions of variables, like Sin[x]. It is very important to pay attention to this, because it is a frequent mistake to forget the empty pairs of brackets when working with xCoba`.

xCoba` provides the general scalar function object InChart to convert a scalar field into a scalar function in a given chart. Using InChart is not really required in standard explicit computations. See many examples further down in subsection Curvature computations of the section CTensor framework, none of which uses InChart. Essentially, InChart is a composition operator, composing a scalar field with a chart, producing a function of coordinates (a "scalar function" in xTensor's terminology).

This is a scalar field on M3:

u[]

This is an equivalent field, expressing the field u[] as a scalar function of the scalars of a chart on M3. Note the underlining:

%//InChart[csys]

%//InputForm

u and InChart[u[], csys] are different types of objects:

xTensorQ/@{u,InChart[u[],csys]}

ScalarFunctionQ/@{u,InChart[u[],csys]}

The field u[] could never be replaced by the function Function[{x, y, z}, Sin[x+y+z]], but the scalar function InChart[u[], csys] can be replaced by such function.

InChart can be used when one wants to perform chart computations with symbolic tensor components, but it is not needed with explict functions.

CTensor of components of a tensor field v:

vector=ToCTensor[v,{csys}][a]

Differentiating with the parallel derivative of the frame is equivalent to differentiating the component fields:

PDcsys[-b][vector]

Using any other derivative, including the parallel derivative of any other frame, introduces Christoffel terms:

$LargeComponentSize=3000;

PDblues[-b][vector]

$LargeComponentSize=1000;

We can reproduce the same computations using InChart scalar functions:

ivector=vector//ToInChart[csys]

PDcsys[-b][ivector]

$LargeComponentSize=4000;

PDblues[-b][ivector]

%//ToInChart[csys]

$LargeComponentSize=1000;

2.3. Clean up

UndefChart[csys]

TODO: It should not be necessary to remove explicitly this object:

UndefTensor[Jacobianbluesfred]

UndefBasis/@{fred,blues,fcomp};

UndefTensor/@{u,v,S,T,TT,U,J};

UndefVBundle[InnerC]

UndefManifold[M3]

Remove[expr,expr2,expr4,cexpr,lieexpr,paramexpr,traceexpr,indexlist,S,scalar,u,w,σ,other,$Chars]

3. CTensor framework

3.1. Basic ideas

3.1.1. Setup

Here we start again from scratch, even loading xCoba` again, so that this section of the documentation is fully independent from the rest.

Load xCoba`, which automatically loads xTensor` and xPerm`:

Needs["xAct`xCoba`"]

Deactivate def/undef messages:

$PrePrint=ScreenDollarIndices;
$DefInfoQ=False;
$UndefInfoQ=False;

Prepare a manifold:

DefManifold[M,4,IndexRange[a,n]]

and two bases in its tangent bundle, named after their respective colors:

DefBasis[red,TangentM,{0,1,2,3},BasisColor->Red]

DefBasis[blue,TangentM,{0,1,2,3},BasisColor->Blue]

3.1.2. Algebra

The basic construction in xTensor is a tensor field of the form head[indices]. xCoba now accepts the same structure, but the head can be an expression containing a tensor described by an array of components in a given list of bases.

This a vector field. An additional argument is automatically added, the added tensor weight, zero by default:

v=CTensor[{1,2,3,4},{red}]

xTensorQ[v]

By default the object is not formatted. It is typeset when we add the index, as usual in xTensor:

v[a]

The red vertical bars indicate that components correspond to the red basis, whose color is Red:

BasisColor[red]

This is a 2-tensor:

SeedRandom[100];
S=CTensor[RandomInteger[10,{4,4}],{red, -red}]

S[a,-b]

Note that the standard representation of a matrix {{1}, {2}} and a vector {1, 2} coincide in MatrixForm, but not in this CTensor formatting:

{MatrixForm[{{1},{2}}],MatrixForm[{1,2}]}

{CTensor[{{1},{2}},{red,blue}][a,b],CTensor[{1,2},{red}][a]}

The vertical bars correspond to the first index and the horizontal bars correspond to the second index. If more indices are present then we nest the colored bars, as MatrixForm does. Use the variable $CTensorRepresentation to switch between typesetting forms:

$CTensorRepresentation="MatrixForm";
CTensor[{{{1},{2}},{{3},{4}}},{red,red,blue}][a,b,c]

$CTensorRepresentation="ColorBasisForm";
CTensor[{{{1},{2}},{{3},{4}}},{red,red,blue}][a,b,c]

Operations with c-tensors proceed as usual in xAct, using indices to indicate contractions or transpositions.

Compute the trace of a tensor:

S[a,-a]

Contract two tensors:

S[a,-b]v[b]

Multiply two tensors (their tensor product):

T[a_,b_]=v[a]v[b]

This is a symmetric tensor:

T[a,b]==T[b,a]

%//ToCanonical

In sums there is automatic reordering of indices:

T[a,b]-T[b,a]

Note that the previous expression returned 0, and not a CTensor with an array of zeros. This is to follow the standard behaviour of xTensor:

v[a]v[b]-v[b]v[a]

The CTensor notation is an alternative to the “linear combination notation” in xCoba. It is possible to change between both notations.

Expand a CTensor into a linear combination of bases objects, and then back:

v[a]

BasisExpand[%,{red}]

%%//ToBasisExpand

FromBasisExpand[%]

3.1.3. A metric field

This subsection will use a different chart and metric, namely Schwarzschild.

Define a coordinate system:

DefChart[ch,M,{0,1,2,3},{t[],r[],θ[],ϕ[]},ChartColor->Brown]

Now define a metric field, the Schwarzschild metric:

DefConstantSymbol[mass,PrintAs->"M"]

met=CTensor[DiagonalMatrix[{-1+2mass/r[],1/(1-2mass/r[]),r[]^2,r[]^2Sin[θ[]]^2}],{-ch,-ch}];

met[-a,-b]

That tensor does not have any information about being a metric. In particular we cannot yet use it to raise and lower indices:

met[a,b]//Catch

This is a contravariant vector, because we are using basis ch, rather than basis -ch for a covariant vector:

v=CTensor[{0,r[],0,0},{ch}]

v[a]

v[-a]//Catch

Of course, you can manually use the metric to lower indices:

met[-a,-b]v[b]

We now declare the met is the metric on the vbundle of the coordinated frame of ch (this is roughly the equivalent of DefMetric in xTensor). In some cases SetCMetric can unambiguously compute the signature from the matrix of components, but in general it cannot, so we need to specify the signature as a triple {number of +1s, number of -1s, number of 0s}:

SetCMetric[met,ch,SignatureOfMetric->{3,1,0}]

Now that we have a metric, raising and lowering indices automatically produces the expected result:

{v[a],v[-a]}

This is a contraction:

v[a]v[-a]

If we raise one index of the metric we get the identity:

met[-a,b]

This is the inverse metric:

met[a,b]

Without using SetCMetric, it could have also been computed in this form:

Inv[met]

Some properties of the metric.

The determinant is a density in a given basis, so one needs to specify the basis in which you want that density:

Determinant[met,ch]

This is the signature of the metric, as was specified (or computed) in input:

SignatureOfMetric[met]

The sign of the metric is obtained from the signature, not from the expression of the determinant itself:

SignDetOfMetric[met]

3.1.4. Covariant derivatives

In the same way that an abstract tensor T has a "component version" CTensor[...], a covariant derivative CD also has a component version CCovD[...]. In the same way that we can change from a covariant derivative to another one using Christoffel tensors, we can also change from a CCovD object to another using a Christoffel CTensor object. This subsection explains how that works.

This is the Levi-Civita connection associated to the metric. It has the structure CCovD[pd, christoffel, metric], meaning that it is the covariant derivative that can be obtained from the partial derivative pd using the given christoffel tensor. The metric tensor is stored to denote where the object came from:

cd=CovDOfMetric[met]

Now we can use it to compute covariant derivatives of tensor fields. The notation is identical to that used in xTensor:

cd[-a][v[b]]

The covariant derivative of the metric field is zero:

cd[-a][met[-b,-c]]

This is the Christoffel tensor of the Levi-Civita connection with respect to the partial derivative of the chart:

Christoffel[cd,PDch]

%[a,-b,-c]

ToBasisExpand[%]

Commuting covariant derivatives:

comm=cd[-b]@cd[-a]@v[c]-cd[-a]@cd[-b]@v[c]//Simplify

We can check that the result coincides with the expected contraction with the Riemann tensor of the connection:

comm==Riemann[cd][-a,-b,-d,c]v[d]

%//Simplify

Being a Levi-Civita connection, it has zero torsion:

Torsion[cd][a,-b,-c]

The Schwarzschild metric is Ricci-flat:

Ricci[cd][-a,-b]

We will see later how to compute all curvature tensors in a single and faster computation, using the command MetricCompute.

3.1.5. Cleanup

Remove the structures defined through Def commands:

UnsetCMetric[met]

UndefConstantSymbol[mass]

UndefChart[ch]

UndefBasis[red]

UndefBasis[blue]

UndefManifold[M]

Remove the expressions we have defined interactively:

Remove[comm,cd,v,met,S,T]

Clear the cache:

ClearxCobaCache[All]

3.2. Tensors

3.2.1. CTensor expressions

The head CTensor describes a tensor by giving an array of components in some bases. A third argument adds density weight.

Define a manifold with a basis field for its tangent bundle:

DefManifold[M,4,{a,b,c,d,e,f}]

DefBasis[B,TangentM,{0,1,2,3}]

Define some scalar field on the manifold:

DefTensor[s[],M]

This is a vector field on M. It automatically adds the third argument, meaning that there is no added density weight:

v=CTensor[{0,I s[],1,3s[]^2},{B}]

This object is now treated by xAct as a head v declared with DefTensor:

xTensorQ[v]

SlotsOfTensor[v]

SymmetryGroupOfTensor[v]

DependenciesOfTensor[v]

WeightOfTensor[v]

Dagger[v]

We can have a higher-rank tensor. Let us also make it a density of weight +2 in the basis B:

A=CTensor[s[]Array[Signature[{##}]&,{4,4,4}],{-B,-B,-B},2B]

xTensorQ[A]

SlotsOfTensor[A]

SymmetryGroupOfTensor[A]

DependenciesOfTensor[A]

WeightOfTensor[A]

Dagger[A]===A

A CTensor with an array of zeros is replaced by the head Zero, which autoconverts to 0 when given indices:

CTensor[{{0,0},{0,0}},{B,B}]

Zero[a,b]

A CTensor containing a scalar is automatically converted into the scalar:

CTensor[s[],{}][]

%//InputForm

However, if it is a scalar density then we cannot do that:

CTensor[s[],{},B][]

%//InputForm

A self-consistency check:

CTensor[{{1,2},{3,4}},{B}]//Catch

Finally, note that there is no collection of the CTensor objects existing in a given session. In particular they do not appear in the list $Tensors.

3.2.2. Indexed CTensor expressions

Following the standard behaviour in xTensor, the CTensor expressions have a special typesetting when indices are added. This is just MatrixForm, which uses:
	T[a]		(Column)
	T[a, b]		(Column-Row)
	T[a, b, c]	(Column-Row)-(Column)
	...		...

Vectors (both covariant and contravariant) are represented as columns. In xAct we use the index height to distinguish upper/lower, and never to distinguish rows from columns in a matrix.

v[a]

The color of the lines denote the basis associated to the components:

v

BasisColor[B]

Higher-order tensors. Note the double tilde, representing weight +2 in the basis B:

A[-a,-b,-c]

All three indices use the same basis B, so all lines in the representation are red.

A

Note that odd indices correspond to vertical lines and even indices correspond to horizontal lines. This is nonstandard, but as we said it helps in distinguishing a matrix {n, 1} from a vector, etc:

{MatrixForm[{{1},{2}}],MatrixForm[{1,2}]}

This type of representation can get difficult to understand for ranks beyond 2. To help with that you can hover over a given element with the mouse, and you will get positional information.

To avoid formatted tensors getting too large, a given component will not be typeset if its ByteCount is larger than a given predefined limit (that you can configure):

$LargeComponentSize

$LargeComponentString

{(s[]+1)^4,(s[]+1)^5,(s[]+1)^6,(s[]+1)^7}//Expand

ByteCount/@%

CTensor[{(s[]+1)^4,(s[]+1)^5,(s[]+1)^6,(s[]+1)^7},{B}][a]//Expand

Still, hovering over the “hidden component” will show you what it actually is.

As usual in xTensor, you can choose your own formatting for the tensor:

v[a]

We need to use Evaluate because PrintAs has attribute HoldFirst:

PrintAs[Evaluate[v]]^="𝕍";

v[a]

%//InputForm

That definition was actually stored as an upvalue of CTensor. You can remove it as follows:

CTensor/:PrintAs[Evaluate[v]]=.

v[a]

Components of a CTensor may be other tensors, as long as there are no free indices:

CTensor[{1,Basis[{-a,-B},{b,B}],3s[],0},{B}][c]

Loading xTerior, the components can also be differential forms, but not tensor-valued differential forms.

Self-consistency checks:

v[a,b]//Catch

v[-a]//Catch

3.2.3. Basic operations

Standard algebraic operations proceed with identical notations to those in xTensor.

Scalar expressions are automatically multiplied by CTensor objects:

v[a]

-7s[]^2v[a]

Add CTensor expressions with the same index configuration:

w=CTensor[{Pi,E,I,1},{B}];

v[a]+3w[a]

Sometimes it may happen that the tensors have the same indices, but in a different order. Then we reorder one of them:

T=CTensor[{{1,2},{3,4}},{B,B}];

T[a,b]+T[b,a]

T[a,b]-T[b,a]

However, we do not move the indices by default, because that may interfere with the canonicalizer. The function ToCCanonical is needed then, to sort the indices as determined by IndexSort:

T[b,a]

%//ToCCanonical

Note that ToCanonical is a different operation, based on the existence of symmetry of the tensor. In this case there is no symmetry and hence ToCanonical does nothing:

%%//ToCanonical

Times multiplication of two CTensor objects triggers their immediate tensor product:

v[a]v[b]

v[a]T[b,c]

Finally, contractions are denoted by repeated staggered indices:

A[-a,-b,-c]v[b]

% v[c]

%//Simplify

3.2.4. Metric

Once we have a metric field in our problem, there are many more things we can do. In this section we focus on how to set a metric field, and which other tensors are algebraically related to it. We can specify a metric field in several possible ways. We start with the simpler case of a metric given in a coordinate chart. We shall use the example of spherical angles on the 2-sphere.

Define the S2 manifold, with script indices:

DefManifold[S2,2,{𝒶,𝒷,𝒸,𝒹,ℯ,𝒻}]

Define the chart:

DefChart[sph,S2,{1,2},{θ[],ϕ[]}]

Construct a metric field, that is a symmetric covariant 2-tensor:

met=CTensor[{{1,0},{0,Sin[θ[]]^2}},{-sph,-sph}]

met[-a,-b]

We have not specified that this is a metric, and there is actually no metric yet in TangentS2. Therefore this object is not yet defined:

met[a,b]//Catch

Compute the inverse metric tensor with the Inv operator:

imet=Inv[met]

Check that they are indeed inverses:

met[-a,-b]imet[b,c]

We can also compute the metric determinant, which is a density of weight +2 in the given chart (note the third argument of CTensor in the result):

Determinant[met,sph]

Or the epsilon tensor, which is a pure tensor, and not a density:

epsilon[met]

It is constructed as a SparseArray. Use Normal to get the corresponding normal array:

%//Normal

Simplify[%,Sin[θ[]]>0]

So far the metric field we have is just a normal tensor field, and xCoba does not know yet that it is going to play any special role. In order to declare it as special we need to use SetCMetric, the component equivalent of DefMetric.

Declare met as the first-metric of the tangent bundle TangentS2. This will precompute a number of objects associated to the metric.

SetCMetric[met,sph,SignatureOfMetric->{2,0,0}]

Now we can raise and lower indices, because we know what the metric is.

v=CTensor[{1,2},{sph}]

v[a]

v[-a]

met[-a,-b]v[b]

% imet[a,b]

And there is a contravariant version of the metric, identical to the inverse metric tensor we constructed before:

{met[a,b],imet[a,b]}

3.2.5. ToCTensor: changes of bases and height

One of the most useful features of xCoba in comparison with other tensor CASs is its ability to manipulate several bases simultaneously and tensor components in different, even mixed bases. We can also have CTensor objects expressed in different bases, and we can easily change those bases, effectively implementing the basic tensorial transformation law. Note that this changes simultaneously bases and components, such that the changes compensate mutually and the actual tensor is left unchanged. Hence we are referring to passive changes.

Introduce a second frame on the sphere. To avoid discussing coordinate dependencies, we choose it to be a rigid transformation. Note how the transformation is provided: the new frame is associated to the first tensor slot, and in covariant form:

DefBasis[sph2,TangentS2,{1,2},BasisChange->CTensor[{{2,1},{-3,2}},{-sph2,sph}],BasisColor->Blue]

This is a vector field on the sphere:

v=CTensor[{1,1},{sph}]

Change it to the new frame:

ToCTensor[v,{sph2}]

Change it back to the original frame:

ToCTensor[%,{sph}]

The identity tensor in the sph frame:

ToCTensor[delta,{-sph,sph}]

Its components do not change under a change of basis:

ToCTensor[%,{-sph2,sph2}]

The components of the metric do change:

met

ToCTensor[%,{-sph2,-sph2}]

but they still form a symmetric 2-covariant tensor field:

SymmetryGroupOfTensor[%]

We can also use CTensor to change the height of the slots of a tensor. Effectively that absorbs metric factors:

ToCTensor[met,{-sph2,sph2}]

v

ToCTensor[v,{-sph}]

Finally, we can also use ToCTensor to expand a tensor into its components:

DefTensor[S[𝒶,-𝒷],S2]

ToCTensor[S,{sph,-sph2}]

Compare with the result from ComponentArray:

ComponentArray[S[{𝒶,sph},{-𝒷,-sph2}]]

Instead of specifying the change of basis with the option BasisChange at the time of definition of the new basis, we can first define the two bases and then use SetBasisChange to specify the change.

Define another basis, this time without specifying the basis change:

DefBasis[sph3,TangentS2,{1,2},BasisColor->Orange]

Now set the change of basis:

SetBasisChange[CTensor[{{2,1},{-3,2}},{-sph3,sph}]]

Again, ToCTensor performs simple changes of basis with that information:

ToCTensor[v,{sph3}]

3.2.6. Extracting components

Closely related to the problem of changes of bases we have that of extraction of particular components. If necessary, a change of basis will be performed automatically.

Extract a component of the metric in the same bases:

met

met[{2,-sph},{2,-sph}]

Extract a component in a different basis:

met[{2,-sph2},{2,-sph2}]

ToCTensor[met,{-sph2,-sph2}][-𝒶,-𝒷]

A mixture of bases:

met[{1,-sph},{2,-sph2}]

ToCTensor[met,{-sph,-sph2}][-𝒶,-𝒷]

We can extract a sub-CTensor expression:

met[-𝒶,{1,-sph2}]

met[{2,-sph},-𝒶]

Using b-indices will produce an expansion into basis objects:

met[{𝒶,-sph},{𝒷,-sph}]

The same result can be obtained directly from the abstract-index expression using the following syntax:

met[-𝒶,-𝒷]

%//ToBasisExpand

%//FromBasisExpand

3.2.7. Cleanup

UndefTensor/@{Jacobiansphsph3,Jacobiansphsph2};

Undefine S2:

UndefBasis[sph3]

UndefBasis[sph2]

UndefChart[sph]

UndefTensor[S]

UndefManifold[S2]

Undefine M:

UndefBasis[B]

UndefTensor[s]

UndefManifold[M]

Remove[A,T,v,w,met,imet]

ClearxCobaCache[All]

3.3. Covariant derivatives

3.3.1. CCovD expressions

The key idea is to represent a covariant derivative as another connection plus a Christoffel CTensor expressing the relation between those two. Optionally, we can also specify a metric CTensor, in case the connection is associated to it. Note that in this subsection we do not need to raise and lower indices automatically, and therefore there is no need to use SetCMetric.

Define polar coordinates on the 2-plane:

DefManifold[E2,2,{a,b,c,d,e,f}]

DefChart[polar,E2,{1,2},{r[],θ[]}]

met=CTensor[{{1,0},{0,r[]^2}},{-polar,-polar}]

This is the Levi-Civita connection of the metric. We see it has three arguments: the derivative of the coordinate basis, a Christoffel tensor and the metric itself.

cd=CovDOfMetric[met]

This is the Christoffel tensor:

Christoffel[cd,PDpolar]

%[a,-b,-c]

ToBasisExpand[%]

This derivative operator can be now used normally to perform operations:

cd[-a][r[]^3]

cd[-a][CTensor[{1,0},{polar}][b]]

cd[-a][met[-b,-c]]

cd[-a]@cd[-b]@CTensor[{0,r[]},{polar}][c]

%//ToCCanonical

CCovD derivatives are typeset with the ∇ character, and have a tooltip:

DefTensor[v[a],E2]

cd[-a][v[b]]

Expansion is not automatic, as usual in xTensor, and must be asked for explicitly:

ChangeCovD[%,cd,PDpolar]

ChangeCovD[%,PDpolar,cd]

%//ToCanonical

[This is an interesting problem. The two CTensor expresions should cancel, but they don’t (TODO). We need to force them together:]

Collect[%,_v,Simplify]

We can mix abstract and CTensor Christoffels:

ChangeCovD[cd[-a][v[b]],cd]

3.3.2. Torsion and curvature

Given a 2-covariant symmetric CTensor object we can use it as a metric field, from which we can compute curvature. This can be done step by step or with the command MetricCompute. In this subsection we use the simpler approach, and then we will give example of MetricCompute usage in the next section.

Compute the torsion of the covariant derivative defined in the previous subsection. It is a torsion-free derivative:

Torsion[cd]

Compute its curvature. It is a flat connection:

Riemann[cd]

Suppose we introduce a new metric:

MET=CTensor[{{1,0},{0,r[]^4}},{-polar,-polar}]

CD=CovDOfMetric[MET]

It is still torsionless:

Torsion[CD][a,-b,-c]

But now it has curvature:

Riemann[CD][-a,-b,-c,d]

SymmetryGroupOfTensor[Riemann[CD]]

That tensor is not Riemann-symmetric. We cannot automatically lower its last index (because we have not used SetCMetric), but we can manually do it:

Riemann[CD][-a,-b,-c,e]MET[-e,-d]

There is also this alternative equivalent object, which is now fully Riemann-symmetric:

RiemannDown[CD][-a,-b,-c,-d]

SymmetryGroupOfTensor[RiemannDown[CD]]

Ricci and Ricci scalar:

Ricci[CD][-a,-b]

RicciScalar[CD][]

As long as we do not use SetCMetric we can compute with many different metric objects simultaneously. Using SetCMetric would select one as being the first-metric and therefore xCoba might use it to raise and lower indices of some tensors.

Take a list of different CTensor metrics:

Table[CTensor[{{1,0},{0,r[]^n}},{-polar,-polar}],{n,0,10,2}]

Compute their respective Riemann tensors:

Riemann[CovDOfMetric[#]][-a,-b,-c,d]&/@%

3.3.3. Cleanup

UndefChart[polar]

UndefTensor[v]

UndefManifold[E2]

Remove[cd,CD,met,MET,n]

ClearxCobaCache[All]

3.4. Vector analysis in flat space

All of xAct can work in flat space situations, but it may feel overwhelming at times, because xAct was developed with curved space applications in mind. The default settings are prepared to handle the general case.

This subsection explains how to construct trivial operators like Grad, Div, Curl and Laplacian in 3-dimensional flat space but with non-Cartesian coordinates. To distinguish them from the Wolfram Language builtins, we will prepend their names with x: xGrad, xDiv, xCurl and xLaplacian. To make it more interesting, we will work with coordinated bases, which are typically used in non-flat situations, and then compare to orthonormal bases, which are typically used in flat space computations (and what the WL builtins use). This will feel convoluted, but will allow us to explain a number of important concepts.

3.4.1. Set up bases and metric

Define the Euclidean 3-dimensional space with indices i...n:

DefManifold[E3,3,{i,j,k,l,m,n}]

We will work with spherical coordinates:

DefChart[sph,E3,{1,2,3},{r[],θ[],ϕ[]}]

The previous step just declared that there are 3 coordinate fields on E3 and specified their names, but it did not yet declare them as being spherical coordinates. The coordinates are spherical because the metric has this form in their coordinate basis. It is fundamental to note that the coordinate fields are given as r[], θ[], etc, not just as r, θ. The empty pair of brackets is what indicates that they are scalar fields:

met=CTensor[{{1,0,0},{0,r[]^2,0},{0,0,r[]^2Sin[θ[]]^2}},{-sph,-sph}]

Now declare that this tensor field will be used to raise and lower indices. Yes, this is important even if we are working on a flat space, because we are not using an orthonormal basis. The second argument specifies that the metric is given as field in terms of the coordinates of the chart sph. The signature of this metric has three +1s, zero -1s and zero 0s:

SetCMetric[met,sph,SignatureOfMetric->{3,0,0}]

Already the metric has different forms depending on the index structure:

met[-i,-j]

met[-i,j]

met[i,-j]

met[i,j]

Take a covariant vector field, given by its three components in the sph vector basis:

v=CTensor[{r[]^2,r[]Cos[θ[]],r[]},{sph}]

v[i]

v[-i]

This is its norm squared:

met[-i,-j]v[i]v[j]

It can also be computed as

v[i]v[-i]

To compare with standard results, let us introduce an orthonormal basis. The orthonormal basis is not a coordinated basis and therefore cannot be defined using DefChart via another chart (aka coordinate system). We need to do it using DefBasis and provide a transformation matrix from the (already declared) coordinated basis.

These are the three vector fields of the coordinated basis sph. The contravariant abstract index i indicates they are vector fields. The red (meaning basis sph) basis indices number the vectors from 1 to 3:

{Basis[{1,-sph},i],Basis[{2,-sph},i],Basis[{3,-sph},i]}

The (square of the) norms of those coordinated basis vectors are not always 1:

met[-i,-j]Basis[{1,-sph},i]Basis[{1,-sph},j]//ContractBasis

met[-i,-j]Basis[{2,-sph},i]Basis[{2,-sph},j]//ContractBasis

met[-i,-j]Basis[{3,-sph},i]Basis[{3,-sph},j]//ContractBasis

They are orthogonal, though:

met[-i,-j]BasisArray[-sph,-sph][i,j]//ContractBasis

Hence, to construct an orthonormal basis we just have to divide the vectors by their norms. This time we sill represent the new basis with blue color. Note that the basis is defined on the vbundle TangentE3 of the manifold E3, not on the manifold E3 itself:

DefBasis[ortho,TangentE3,{1,2,3},BasisColor->Blue,BasisChange->CTensor[DiagonalMatrix[{1,1/r[],1/(r[]Sin[θ[]])}],{-ortho,sph}]]

These are the three orthonormal basis vectors:

BasisArray[-ortho][i]

Express them in the coordinate basis sph:

BasisExpand[%,sph]//ToValues

They are now orthonormal:

met[-i,-j]BasisArray[-ortho,-ortho][i,j]//ContractBasis

3.4.2. Differentiation in flat space

Now let us discuss differentiation. We need to distinguish the partial derivative with respect to the coordinate fields (which is independent of the choice of metric) from covariant differentiation (which is determined by the choice of metric).

The first thing to observe is that all derivative operators coincide on scalars, and give the coordinated basis vectors:

PD[-i]/@{r[],θ[],ϕ[]}

PDsph[-i]/@{r[],θ[],ϕ[]}

Therefore we have trivially that (again using any derivative):

PD[{1,-sph}]/@{r[],θ[],ϕ[]}

PD[{2,-sph}]/@{r[],θ[],ϕ[]}

PD[{3,-sph}]/@{r[],θ[],ϕ[]}

Derivatives of vectors do not all coincide, and this is where things get interesting. The PDsph derivative is the only one which gives zero on the basis vectors of the sph chart. Other derivatives, like the general PD derivative, give Christoffel tensors, with will be discussed further down:

PD[-i][Basis[-j,{1,sph}]]

PDsph[-i][Basis[-j,{1,sph}]]

If the result depends on the choice of derivative, which one is the important one to use? In general, it is the covariant derivative of the metric, which is the derivative with geometrical meaning (i.e. with meaning determined by the metric).

The covariant derivative associated to the metric is the so-called Levi-Civita covariant derivative, computed with LC. The result is a CCovD object, which can be understood as playing the role for covariant derivatives that CTensor plays for tensors. It is a way of giving an *explicit* covariant derivative, as opposed to a general implicit covariant derivative defined with DefCovD:

cd=LC[met]

It gives zero on its own metric tensor:

cd[-i][met[-j,-k]]

Partial derivatives of the metric are not zero:

PDsph[-i][met[-j,-k]]

Again, the fact the three levels of this tensor are red means that the tensor components are given in the sph basis:

%//InputForm

3.4.3. xGrad

Let us start with the first differential operator, the gradient.

So let us define the gradient of a scalar field. It is a vector field, so we need to provide the index that the final expression will have:

xGrad[scalar_][i_]:=cd[i][scalar]

Take a general scalar field. In xAct we need to declare all symbols, even functions of other things. This is not so much to know the names in advance, but to know their tensorial properties. In this case we are declaring that all arguments of f must be scalars and that the result of f should also be treated as a scalar:

DefScalarFunction[f]

Compute the gradient of our general scalar field:

grad=xGrad[f[r[],θ[],ϕ[]]][i]

By default it is returned as a CTensor object, but we can easily translate it into a linear combination of basis vectors (of the same basis, i.e. color) of the CTensor itself:

ToBasisExpand[grad]

Compare with WL's result. It's different because we are using a different basis:

Grad[f[r,θ,ϕ],{r,θ,ϕ},"Spherical"]

Change the basis of our result. There are several possible ways to do it:

ToBasis[ortho][grad]

ToCTensor[Head[grad],{ortho}]

%[i]

3.4.4. xDiv

Now we introduce the divergence of a vector, which is a scalar.

We provide the input as a vector field, and with a second argument we specify the index in which this object is a vector field. We will use that information to know where to differentiate:

xDiv[v_,i_][]:=cd[-i][v]

Take our contravariant vector v and compute its divergence:

v

xDiv[v[i],i][]//Simplify

If we blindly try Div on the vector components we get a wrong result:

First[v]

Div[%,{r[],θ[],ϕ[]},"Spherical"]//Simplify

The problem again is that WL's Div expects vector components in the orthonormal basis:

ToCTensor[v,{ortho}]

First[%]

Div[%,{r[],θ[],ϕ[]},"Spherical"]//Simplify

Note that our notations contain the information of which basis is being used, and the system can handle all of them:

ToCTensor[v,{ortho}]

xDiv[%[i],i][]//ToValues//Simplify

We can even start with the covariant components of the tensor, and our xDiv will handle that too:

v[-i]

xDiv[%,-i][]//Simplify

3.4.5. xCurl

Now we show how to construct the Curl of a vector field. To do this, we first need to discuss another interesting object: the epsilon tensor associated with a metric:

The epsilon tensor is fully antisymmetric. Again it will have different forms in different bases, being particularly simple in the orthonormal basis:

epsilonmet=epsilon[met]//Normal//PowerExpand

ToCTensor[epsilonmet,{-ortho,-ortho,-ortho}]

Being associated to the metric, it also has zero covariant derivative with respect to the Levi-Civita connection:

cd[-i][epsilonmet[-j,-k,-l]]

This is the definition of Curl. Again, we need to provide the index in which the object is a vector. Do not use the same index for i and j in this definition. There are ways of avoiding this type of collision in xAct (for more robust code), but we will not discuss that in this intro section:

xCurl[v_,j_][i_]:=Module[{k},epsilonmet[i,k,-j]cd[-k][v]]

Apply it to our vector v:

xCurl[v[j],j][i]

To compare to WL's builtin we need to convert the original vector to the orthonormal basis, perform the computation and then change back to the coordinate basis:

First[ToCTensor[v,{ortho}]]

Curl[%,{r[],θ[],ϕ[]},"Spherical"]

CTensor[%,{ortho}]

ToCTensor[%,{sph}][i]

A final warning on xCurl vs Curl: our definition Hodge-dualizes only one index, leaving all other indices untouched, while WL's definition Hodge-dualizes all tensor indices, meaning that the original tensor needs to have less indices than the dimension (otherwise the result would be zero). In other words, for a tensor of rank n in dimension d (d is 3 in our example here), xCurl will produce a result of rank d + n - 3, while Curl will produce a result of rank d - n - 1 (see a discussion in [MSE]). For n = 1 both formulas give the same result of rank d - 2, but will differ for other input ranks.

3.4.6. xLaplacian

Finally we can construct the Laplacian, as a combination of xGrad and xDiv. It takes a scalar field and returns another scalar field:

The epsilon tensor is fully antisymmetric. Again it will have different forms in different bases, being particularly simple in the orthonormal basis:

xLaplacian[scalar_][]:=Module[{i},xDiv[xGrad[scalar][i],i][]]

xLaplacian[f[r[],θ[],ϕ[]]][]//Simplify

In this case the comparison with WL's builtin is direct, because there are no vectors involved in input or output:

Laplacian[f[r[],θ[],ϕ[]],{r[],θ[],ϕ[]},"Spherical"]//Simplify

3.4.7. Generalizations

We said that xGrad and xLaplacian act on scalars, and that xDiv and xCurl act on vectors. Actually, they can act on any tensor, and our definitions above already handle those extensions. Let us see how it works.

Let us take, for example, the gradient of our vector field:

grad=xGrad[v[i]][j]

What happened here is that the new index (j) was added at the end of the expression. In other words, the general rule is that xGrad will add a new index, at the end. The particular case of differentiating a scalar adds a new index at the end, which just happens to be the only index, and hence returns a vector.

The WL builtin can also do this, and as usual, the comparison involves just changes of basis:

res=ToCTensor[Head[grad],{ortho,ortho}]

First@ToCTensor[v,{ortho}]

Grad[%,{r[],θ[],ϕ[]},"Spherical"]//Simplify

%===First[res]

Now that we have a two-tensor grad, let us compute its divergence for example. But there are two indices in a two-tensor. Which one should we differentiate? This is where the index argument of xDiv comes in handy. The two-tensor grad has indices i, j. Let us take divergences in both separately:

grad

gradDivi=xDiv[grad,i][]//Simplify

gradDivj=xDiv[grad,j][]//Simplify

The general rule is that xDiv[expr, i] will remove index i, leaving all others present. The particular case of differentiating a vector removes the index of that vector and returns a scalar.

The WL builtin can also do this, but only with respect to the last (j in gradDivj in our case) index. Let us now compare by converting WL's result back to the coordinated basis:

First@ToCTensor[Head[grad],{ortho,ortho}]

Div[%,{r[],θ[],ϕ[]},"Spherical"]//Simplify

ToCTensor[CTensor[%,{ortho}],{sph}][i]

%===gradDivj

The same type of example could be done with xCurl (where we also have a freedom to choose the index to differentiate) and xLaplacian (where we don't have a freedom, because xGrad always places the new index at the end, and xDiv must differentiate the new index just added, and no other one).

Another interesting observation is that our definitions are valid for any type of xAct expression, not just CTensor expressions, though the results may not be as nice as expected. It is, however, interesting to explore what the possibilities are. We will see along the way how some new commands (like ContractBasis and BasisExpand) are needed to specify when to perform changes of basis.Those commands to manipulate Basis objects have been explained in detailed in previous sections.

First, we can use linear combinations of basis vectors. We will need ContractBasis to perform contractions of Basis vectors:

ToBasisExpand[v[i]]

xDiv[%,i][]

%//ContractBasis//Simplify

Let us now start with a different basis, as specified by BasisExpand. In this case we will find Basis change objects, like Basis[{3, -sph}, {1, ortho}]. Their values have been already given before, and can be inserted in the computation using ToValues:

ToBasisExpand[v[i]]

BasisExpand[%,ortho]

%//ToValues

xDiv[%,i][]//ContractBasis//ToValues

%//ToValues

There we see the Christoffel terms relating our two parallel derivatives. Currently we can evaluate them doing something like this [Jose: we should have an easier way to do this, perhaps via ToValues]:

%/.ChristoffelPDorthoPDsph->ToCTensor[ChristoffelPDorthoPDsph,{ortho,-ortho,-ortho}]//Simplify

Yet another possibility is to act on general tensor fields. For example take a scalar field F[]:

DefTensor[F[],E3]

This gives the derivative of the scalar field in terms of PDsph:

xGrad[F[]][i]

Compare with an expression in terms of a scalar function. Note how F[] and the coordinates r[], ϕ[] and ϕ[] are scalar fields on E3, while f is a scalar function of 3 scalar arguments:

%/.F[]->f[r[],θ[],ϕ[]]

3.4.8. Clean up

Clean up:

Remove[v,met,epsilonmet,cd,f,grad,xCurl,xDiv,xGrad,xLaplacian,gradDivi,gradDivj,res,scalar]

UndefTensor/@{Jacobianorthosph,Jacobiansph,F}

UndefBasis[ortho]

UndefChart[sph]

UndefManifold[E3]

3.5. Curvature computations

This is probably the most important computation in GR, and we need to do it in a simple and fast enough way. Each example in this section is prepared to be evaluated independently of the rest of the notebook, and hence there are respective Needs commands.

To have info messages from the caching system, activate this cell:

$xCobaCacheVerbose=True;

To have info messages from ComponentValue, activate this cell:

$CVVerbose=True;

To see messages from MetricCompute activate this cell:

SetOptions[MetricCompute,Verbose->True]

3.5.1. Kerr-Newman in the rotating frame

Needs["xAct`xCoba`"]

$DefInfoQ=$UndefInfoQ=False;
$CVVerbose=False;

Define a 4d manifold:

DefManifold[M,4,{a,b,c,d,e,f}]

Boyer-Lindquist coordinates:

DefChart[BL,M,{0,1,2,3},{t[],r[],θ[],ϕ[]}]

DefConstantSymbol[Mass,PrintAs->"M"]
DefConstantSymbol[Rotation,PrintAs->"a"]
DefConstantSymbol[Charge,PrintAs->"Q"]

Change of basis:

ρ2=r[]^2+Rotation^2Cos[θ[]]^2;

change={{((r[]^2+Rotation^2)/(ρ2)),0,0,(Rotation/(ρ2))},{0,1,0,0},{0,0,1,0},{Rotation Sin[θ[]]^2,0,0,1}}

Now define a frame in terms of the BL coordinates. Note that we need to provide the frame in a very specific way:

DefBasis[frame,TangentM,{0,1,2,3},BasisColor->RGBColor[0,0,1],BasisChange->CTensor[change,{-frame,BL}]]

This explains the change we have provided:

ToCTensor[Basis,{-frame,BL}]

ToCTensor[Basis,{-BL,frame}]//Simplify

ToCTensor[CTensor[{vt,vr,vθ,vϕ},{BL}],{frame}]

Remove[vt,vr,vθ,vϕ]

The metric tensor is diagonal in this frame:

factor=((r[]^2)/(ρ2))(1-((2Mass)/(r[]))+((Rotation^2+Charge^2)/(r[]^2)));

metric=CTensor[DiagonalMatrix[{-factor,1/factor,ρ2,ρ2 Sin[θ[]]^2}],{-frame,-frame},0]

The coordinated metric tensor is nondiagonal:

ToCTensor[metric,{-BL,-BL}]

Compute curvature tensors:

MetricCompute[metric,BL,All,Parallelize->True]//AbsoluteTiming

That computed and simplified everything, including the Kretschmann scalar and the first covariant derivative of the Riemann tensor. All those results are now cached. If we run it again the computation is much faster:

MetricCompute[metric,BL,All]//AbsoluteTiming

Extract information:

cd=CovDOfMetric[metric];

RicciScalar[cd][]

The Ricci tensor is again diagonal in this frame:

Ricci[cd]

%[-a,-b]

Kretschmann[cd][]

Riemann[cd]

Check the Bianchi identity:

cdriemdown=TensorDerivative[RiemannDown[cd],cd];

cdriemdown[-a,-b,-c,-d,-e]+cdriemdown[-b,-c,-a,-d,-e]+cdriemdown[-c,-a,-b,-d,-e]//Simplify//Timing

Clean up:

Remove[metric,cd,factor,ρ2,change,cdriemdown]

UndefConstantSymbol[{Mass,Rotation,Charge}];

UndefTensor[JacobianBLframe]

UndefBasis[frame]

UndefChart[BL]

UndefManifold[M]

Clear the cache:

ClearxCobaCache[All]

3.5.2. General 2d metric connection with torsion

Needs["xAct`xCoba`"]

$DefInfoQ=$UndefInfoQ=False;
$CVVerbose=False;

Define a 2d manifold:

DefManifold[M,2,{a,b,c,d,e,f}]

with a general chart:

DefChart[gen,M,{1,2},{x[],y[]}]

Let us now work with a fully general metric:

DefScalarFunction/@{gxx,gxy,gyy};

(metric=CTensor[{{gxx[x[],y[]],gxy[x[],y[]]},{gxy[x[],y[]],gyy[x[],y[]]}},{-gen,-gen}])[-a,-b]

SymmetryGroupOfTensor[metric]

In this case we will also have torsion, a general torsion tensor:

DefScalarFunction/@{T112,T212};

(torsion=CTensor[{{{0,T112[x[],y[]]},{-T112[x[],y[]],0}},{{0,T212[x[],y[]]},{-T212[x[],y[]],0}}},{gen,-gen,-gen}])[a,-b,-c]

SymmetryGroupOfTensor[torsion]

Construct a covariant derivative that is associated to the metric and has the given torsion tensor:

cd=CovDOfMetric[metric,torsion]

It has the torsion we provided:

Torsion[cd]===torsion

Therefore this covariant derivative is not the Levi-Civita connection of the metric. We can obtain the Levi-Civita connection specifying that torsion is Zero (which is actually the default):

lc=CovDOfMetric[metric,Zero]

Torsion[lc]

There is a shortcut to get the Levi-Civita connection:

LC[metric]===lc

Both covariant derivatives are associated to the metric, in the sense that:

cd[-a][metric[-b,-c]]

lc[-a][metric[-b,-c]]

The Christoffel tensor relating cd to the partial derivative of the chart is not symmetric:

christoffel=Christoffel[cd,PDgen]

Its antisymmetric part (actually twice it) is precisely given by the torsion:

christoffel[a,-b,-c]-christoffel[a,-c,-b]==torsion[a,-b,-c]//Simplify

The Christoffel tensor relating the covariant derivative with torsion and the Levi-Civita connection is proportional to the torsion tensor:

chris=Christoffel[cd,lc]//Simplify

Its antisymmetric part is the original torsion tensor:

chris[a,-b,-c]-chris[a,-c,-b]==torsion[a,-b,-c]//Simplify

Its symmetric part is a complicated object:

chris[a,-b,-c]+chris[a,-c,-b]//Simplify

Head[%]

Let us now look at the curvature tensors:

riemann=Riemann[cd]

To analyze the symmetries of the result we need the Riemann with four indices down:

riemanndown=RiemannDown[cd]

Symmetry is complete in this 2d case:

SymmetryGroupOfTensor[riemann]

SymmetryGroupOfTensor[riemanndown]

Now compute everything. Most of the time is spent in the simplification of the results:

MetricCompute[metric,torsion,gen,All,Parallelize->True]//AbsoluteTiming

There is Ricci-scalar curvature:

RicciScalar[cd][]

But there is no Einstein tensor, even having torsion:

Einstein[cd][-a,-b]

The Levi-Civita connection has different curvature:

RicciScalar[lc]

Full cache table:

PrintxCobaCache[]

Clean up:

Remove[metric,torsion,cd,christoffel,riemann,riemanndown,lc,chris]

UndefScalarFunction[$ScalarFunctions];

UndefChart[gen]

UndefManifold[M]

ClearxCobaCache[All]

3.5.3. General 3d metric connection with torsion

Now we repeat the same computation in the 3d case.

Needs["xAct`xCoba`"]

$DefInfoQ=$UndefInfoQ=False;
$CVVerbose=False;

Define a 3d manifold:

DefManifold[M,3,{a,b,c,d,e,f}]

DefChart[gen,M,{1,2,3},{x[],y[],z[]}]

Let us work with the general 3d case:

DefScalarFunction/@{gxx,gxy,gxz,gyy,gyz,gzz};

(metric=CTensor[{{gxx[x[],y[],z[]],gxy[x[],y[],z[]],gxz[x[],y[],z[]]},{gxy[x[],y[],z[]],gyy[x[],y[],z[]],gyz[x[],y[],z[]]},{gxz[x[],y[],z[]],gyz[x[],y[],z[]],gzz[x[],y[],z[]]}},{-gen,-gen}])[-a,-b]

SymmetryGroupOfTensor[metric]

DefScalarFunction/@{T112,T113,T123,T212,T213,T223,T312,T313,T323};

(torsion=CTensor[{
{
{0,T112[x[],y[],z[]],T113[x[],y[],z[]]},
{-T112[x[],y[],z[]],0,T123[x[],y[],z[]]},
{-T113[x[],y[],z[]],-T123[x[],y[],z[]],0}
},
{
{0,T212[x[],y[],z[]],T213[x[],y[],z[]]},
{-T212[x[],y[],z[]],0,T223[x[],y[],z[]]},
{-T213[x[],y[],z[]],-T223[x[],y[],z[]],0}
},
{
{0,T312[x[],y[],z[]],T313[x[],y[],z[]]},
{-T312[x[],y[],z[]],0,T323[x[],y[],z[]]},
{-T313[x[],y[],z[]],-T323[x[],y[],z[]],0}
}
},{gen,-gen,-gen}])[a,-b,-c]

SymmetryGroupOfTensor[torsion]

Declare that metric as the first-metric of the vbundle, and add torsion. This precomputes some tensors:

SetCMetric[metric,torsion,gen,SignatureOfMetric->{3,0,0}]//AbsoluteTiming

This is now the covariant derivative associated to the metric, by default with torsion:

(cd=CovDOfMetric[metric];)//AbsoluteTiming

It has the torsion we provided:

Torsion[cd]===torsion

(christoffel=Christoffel[cd,PDgen])[a,-b,-c]

christoffel[a,-b,-c]-christoffel[a,-c,-b]==torsion[a,-b,-c]//Simplify

The connection is associated to the metric:

TensorDerivative[metric,cd]

Compute everything. We simplify using Together because Simplify is too slow in this case:

MetricCompute[metric,torsion,gen,"Riemann"[-1,-1,-1,1],CVSimplify->Together,Parallelize->True]//AbsoluteTiming

It has curvature:

(riemann=Riemann[cd]);//AbsoluteTiming

(riemanndown=RiemannDown[cd]);//AbsoluteTiming

Symmetry is NOT complete in this 3d case:

SymmetryGroupOfTensor[riemann]

SymmetryGroupOfTensor[riemanndown]

Clean up:

UnsetCMetric[metric]

UndefScalarFunction/@$ScalarFunctions;

UndefChart[gen]

UndefManifold[M]

Remove[metric,torsion,cd,christoffel,riemann,riemanndown]

ClearxCobaCache[All]

3.5.4. A hard example: The Pomeransky-Senkov metric

Needs["xAct`xCoba`"]

$DefInfoQ=$UndefInfoQ=False;

DefManifold[M,5,{a,b,c,d,e,f}]

DefChart[PS,M,{0,1,2,3,4},{t[],x[],y[],ϕ[],ψ[]}]

Formulas taken from [arXiv:0911.0802]:

DefConstantSymbol[{λ,ν,k}]

ds2=((2 H[x,y] (k^2) )/((((1-ν))^2) (((x-y))^2)))(((dx^2)/(G[x]))-((dy^2)/(G[y])))-2((J[x,y])/(H[y,x]))dϕ dψ-((H[y,x])/(H[x,y]))(((dt+Ω))^2)-((F[x,y])/(H[y,x])) ((dψ)^2)+((F[y,x])/(H[y,x])) ((dϕ)^2);

Ω:=M[x,y] dψ+P[x,y] dϕ;

M[x_,y_]:=((2 k λ (Sqrt[(((ν+1))^2)-(λ^2)]) (y+1) (-λ+ν-2 ν x+ν x ((λ+ν-1) x+2) y-1))/((1-λ+ν) H[y,x]));
P[x_,y_]:=((2 k λ (Sqrt[ν]) (Sqrt[(((ν+1))^2)-(λ^2)]) ((x^2)-1) y)/(H[y,x]));

H[x_,y_]:=(λ^2)+2 ν (1-(x^2)) y λ+2 x (1-(ν^2) (y^2)) λ-(ν^2)+ν (-(λ^2)-(ν^2)+1) (x^2) (y^2)+1;
F[x_,y_]:=((2 (k^2))/((((x-y))^2) (((1-ν))^2))) ((1-(y^2)) (((((1-ν))^2)-(λ^2)) (ν+1)+y λ (-(λ^2)-3 (ν^2)+2 ν+1)) G[x]+(-(1-ν) ν ((λ^2)+(ν^2)-1) (x^4)+λ (2 (ν^3)-3 (ν^2)-(λ^2)+1) (x^3)+((((1-ν))^2)-(λ^2)) (ν+1) (x^2)+λ ((λ^2)+(((1-ν))^2)) x+2 (λ^2)) G[y]);
J[x_,y_]:=((2 (k^2) (1-(x^2)) (1-(y^2)) λ (Sqrt[ν]) ((λ^2)+2 (x+y) ν λ-(ν^2)-x y ν (-(λ^2)-(ν^2)+1)+1))/((x-y) (((1-ν))^2)));
G[x_]:=(1-(x^2)) (ν (x^2)+λ x+1);

(matrix=CoefficientArrays[ds2,{dt,dx,dy,dϕ,dψ},"Symmetric"->True][[3]]//Normal//Simplify;)//AbsoluteTiming

A check:

ds2-{dt,dx,dy,dϕ,dψ}.matrix.{dt,dx,dy,dϕ,dψ}//Simplify//AbsoluteTiming

met=CTensor[matrix/.{x->x[],y->y[]},{-PS,-PS}];

Simplify is way too slow for this metric. In this case it is more useful to simplify with Together:

$CVSimplify=Together;

Inverse has a hard time with this matrix. Specify explicitly the signature:

SetCMetric[met,PS,SignatureOfMetric->{4,1,0},Verbose->True]//AbsoluteTiming

Now we compute all the way down to the Ricci tensor.

MetricCompute[met,PS,"Ricci"[-1,-1],Verbose->True,Parallelize->True]//AbsoluteTiming

It is a Ricci-flat metric:

cd=CovDOfMetric[met];

Ricci[cd]

Remove[met,cd,dt,dx,dy,dϕ,dψ,ds2,F,G,H,J,P,Ω,matrix]

UndefConstantSymbol[{λ,ν,k}]

UndefChart[PS]

UndefManifold[M]

ClearxCobaCache[All]

3.5.5. A hard example: Kerr-Newman-deSitter in Boyer-Lindquist-like θ-coordinates

Needs["xAct`xCoba`"]

$DefInfoQ=$UndefInfoQ=False;

DefManifold[M,4,{a,b,c,d,e,f}]

DefChart[BL,M,{0,1,2,3},{t[],r[],θ[],ϕ[]}]

DefConstantSymbol[Mass,PrintAs->"M"]
DefConstantSymbol[Rotation,PrintAs->"a"]
DefConstantSymbol[Charge,PrintAs->"Q"]
DefConstantSymbol[CosmoC,PrintAs->"Λ"]

In Boyer–Lindquist-like coordinates:

Ξ=1+CosmoC Rotation^2/3;
Deltar=(1-CosmoC r[]^2/3)(r[]^2+Rotation^2)+Charge^2-2Ξ Mass r[];
Deltaθ=1+CosmoC Rotation^2Cos[θ[]]^2/3;
Sigma=r[]^2+Rotation^2Cos[θ[]]^2;

ds2=(Sigma/Deltar)dr^2+(Sigma/(Deltaθ))dθ^2+((Sin[θ[]]^2)/(Ξ^2Sigma))Deltaθ(Rotation dt-(r[]^2+Rotation^2)dϕ)^2-(Deltar/(Ξ^2Sigma))(dt-Rotation Sin[θ[]]^2dϕ)^2;

MatrixForm[KNdSMatrix=CoefficientArrays[ds2,{dt,dr,dθ,dϕ},"Symmetric"->True][[3]]//Normal//Simplify]

met=CTensor[KNdSMatrix,{-BL,-BL}];

$CVSimplify=Together;

SetCMetric[met,BL,SignatureOfMetric->{3,1,0}]//AbsoluteTiming

Compute Riemann tensor:

MetricCompute[met,BL,"Riemann"[-1,-1,-1,1],Verbose->True]//AbsoluteTiming

CD=CovDOfMetric[met];

ByteCount[Riemann[CD]]

$CVSimplify=Identity;

ricci=Ricci[CD];//AbsoluteTiming

Map[ByteCount,ricci[[1]],{2}]//MatrixForm

ricci=Together[ricci];//AbsoluteTiming

Map[ByteCount,ricci[[1]],{2}]//MatrixForm

We separate components to see which ones are harder. We see that we are missing the terms proportional to the charge:

diff=ricci-CosmoC met;

diff[{0,-BL},{0,-BL}]//Simplify//AbsoluteTiming

diff[{0,-BL},{1,-BL}]//Simplify//AbsoluteTiming

diff[{0,-BL},{2,-BL}]//Simplify//AbsoluteTiming

diff[{0,-BL},{3,-BL}]//Simplify//AbsoluteTiming

diff[{1,-BL},{1,-BL}]//Simplify//AbsoluteTiming

diff[{1,-BL},{2,-BL}]//Simplify//AbsoluteTiming

diff[{1,-BL},{3,-BL}]//Simplify//AbsoluteTiming

diff[{2,-BL},{2,-BL}]//Simplify//AbsoluteTiming

diff[{2,-BL},{3,-BL}]//Simplify//AbsoluteTiming

diff[{3,-BL},{3,-BL}]//Simplify//AbsoluteTiming

Remove[met,Ξ,ricci,diff,ds2,dt,dr,dθ,dϕ,Deltar,Deltaθ,Sigma,KNdSMatrix]

UndefConstantSymbol[{Mass,Rotation,Charge,CosmoC}]

UndefChart[BL]

UndefManifold[M]

ClearxCobaCache[All]

3.5.6. A very (actually too) hard example: The black saturn metric

Needs["xAct`xCoba`"]

$DefInfoQ=$UndefInfoQ=False;

Solution taken from [arXiv:0701035v2]. Thanks to Alfonso García-Parrado for the actual Mathematica formulas.

DefManifold[M,5,{a,b,c,d,e,f}]

DefChart[BS,M,{0,1,2,3,4},{t[],ρ[],z[],ψ[],ϕ[]}]

DefConstantSymbol[{ks,q,a1,a2,a3,a4,a5,c1,c2}]

DefScalarFunction[{μ1,μ2,μ3,μ4,μ5,M0,M1,M2,M3,M4,R1,R2,Hx,Hy,Gx,Gy,ωψ,P,F}]

μ and R functions do not depend on any other functions. These functions are the only ones with explicit dependence in z:

μFunctions={
μ1->Function[{ρ,z},Sqrt[ρ^2+(z-a1)^2]-(z-a1)],
μ2->Function[{ρ,z},Sqrt[ρ^2+(z-a2)^2]-(z-a2)],
μ3->Function[{ρ,z},Sqrt[ρ^2+(z-a3)^2]-(z-a3)],
μ4->Function[{ρ,z},Sqrt[ρ^2+(z-a4)^2]-(z-a4)],
μ5->Function[{ρ,z},Sqrt[ρ^2+(z-a5)^2]-(z-a5)]
};

RFunctions={
R1->Function[{ρ,z},Sqrt[ρ^2+(z-a1)^2]],
R2->Function[{ρ,z},Sqrt[ρ^2+(z-a2)^2]]
};

The G, P, F and M functions are given in terms of μ functions:

GFunctions={
Gx->Function[{ρ,z},((ρ^2μ4[ρ,z])/(μ3[ρ,z] μ5[ρ,z]))],
Gy->Function[{ρ,z},((μ3[ρ,z]μ5[ρ,z])/(μ4[ρ,z]))]
};

PFunction=P->Function[{ρ,z},(μ3[ρ,z]μ4[ρ,z]+ρ^2)^2(μ1[ρ,z]μ5[ρ,z]+ρ^2)(μ4[ρ,z]μ5[ρ,z]+ρ^2)];

FFunction=F->Function[{ρ,z},μ1[ρ,z]μ5[ρ,z](μ1[ρ,z]-μ3[ρ,z])^2(μ2[ρ,z]-μ4[ρ,z])^2(ρ^2+μ1[ρ,z]μ3[ρ,z])(ρ^2+μ2[ρ,z]μ3[ρ,z])(ρ^2+μ1[ρ,z]μ4[ρ,z])(ρ^2+μ2[ρ,z]μ4[ρ,z])(ρ^2+μ2[ρ,z]μ5[ρ,z])(ρ^2+μ3[ρ,z]μ5[ρ,z])(ρ^2+μ1[ρ,z]^2)(ρ^2+μ2[ρ,z]^2)(ρ^2+μ3[ρ,z]^2)(ρ^2+μ4[ρ,z]^2)(ρ^2+μ5[ρ,z]^2)];

MFunctions={M0->Function[{ρ,z},μ2[ρ,z]μ5[ρ,z]^2(μ1[ρ,z]-μ3[ρ,z])^2(μ2[ρ,z]-μ4[ρ,z])^2(ρ^2+μ1[ρ,z]μ2[ρ,z])^2(ρ^2+μ1[ρ,z]μ4[ρ,z])^2(ρ^2+μ2[ρ,z]μ3[ρ,z])^2],M1->Function[{ρ,z},μ2[ρ,z]μ3[ρ,z]μ4[ρ,z]μ5[ρ,z]μ1[ρ,z]^2ρ^2(μ1[ρ,z]-μ2[ρ,z])^2(μ2[ρ,z]-μ4[ρ,z])^2(μ1[ρ,z]-μ5[ρ,z])^2(ρ^2+μ2[ρ,z]μ3[ρ,z])^2],M2->Function[{ρ,z},μ2[ρ,z]μ3[ρ,z]μ4[ρ,z]μ5[ρ,z]ρ^2(μ1[ρ,z]-μ2[ρ,z])^2(μ1[ρ,z]-μ3[ρ,z])^2(ρ^2+μ1[ρ,z]μ4[ρ,z])^2(ρ^2+μ2[ρ,z]μ5[ρ,z])^2],M3->Function[{ρ,z},2μ1[ρ,z]μ2[ρ,z]μ3[ρ,z]μ4[ρ,z]μ5[ρ,z](μ1[ρ,z]-μ3[ρ,z])(μ1[ρ,z]-μ5[ρ,z])(μ2[ρ,z]-μ4[ρ,z])(ρ^2+μ1[ρ,z]^2)(ρ^2+μ2[ρ,z]^2)(ρ^2+μ1[ρ,z]μ4[ρ,z])(ρ^2+μ2[ρ,z]μ3[ρ,z])(ρ^2+μ2[ρ,z]μ5[ρ,z])],M4->Function[{ρ,z},μ1[ρ,z]^2μ2[ρ,z]μ3[ρ,z]^2μ4[ρ,z]^2(μ1[ρ,z]-μ5[ρ,z])^2(ρ^2+μ1[ρ,z]μ2[ρ,z])^2(ρ^2+μ2[ρ,z]μ5[ρ,z])^2]
};

The H functions are given in terms of M, μ, F functions:

HFunctions={
Hx->Function[{ρ,z},(M0[ρ,z]+c1^2M1[ρ,z]+c2^2M2[ρ,z]+c1 c2 M3[ρ,z]+c1^2c2^2M4[ρ,z])/F[ρ,z]],Hy->Function[{ρ,z},(M0[ρ,z]μ1[ρ,z]/μ2[ρ,z]-c1^2M1[ρ,z]ρ^2/(μ1[ρ,z]μ2[ρ,z])-c2^2M2[ρ,z](μ1[ρ,z]μ2[ρ,z])/ρ^2+c1 c2 M3[ρ,z]+c1^2c2^2M4[ρ,z]μ2[ρ,z]/μ1[ρ,z])μ3[ρ,z]/(μ4[ρ,z]F[ρ,z])]
};

ωψ is given in terms of R, M, F, G functions:

ωψFunction=ωψ->Function[{ρ,z},2(c1 R1[ρ,z]Sqrt[M0[ρ,z]M1[ρ,z]]-c2 R2[ρ,z]Sqrt[M0[ρ,z]M2[ρ,z]]+c1^2c2 R2[ρ,z]Sqrt[M1[ρ,z]M4[ρ,z]]-c1 c2^2 R1[ρ,z]Sqrt[M2[ρ,z]M4[ρ,z]])/(F[ρ,z]Sqrt@Gx[ρ,z])];

The metric is given in terms of H, ωψ, F, P, G functions (note that P and F appear together), meaning that in the end all functions are replaced by elementary functions:

BlackSaturn=-((Hy[ρ,z])/(Hx[ρ,z]))(Basis[-a,{0,BS}]+(((ωψ[ρ,z])/(Hy[ρ,z]))+q)Basis[-a,{3,BS}])(Basis[-b,{0,BS}]+(((ωψ[ρ,z])/(Hy[ρ,z]))+q)Basis[-b,{3,BS}])+Hx[ρ,z](ks^2P[ρ,z](Basis[-a,{1,BS}]Basis[-b,{1,BS}]+Basis[-a,{2,BS}]Basis[-b,{2,BS}])+((Gy[ρ,z])/(Hy[ρ,z]))Basis[-a,{3,BS}]Basis[-b,{3,BS}]+((Gx[ρ,z])/(Hx[ρ,z]))Basis[-a,{4,BS}]Basis[-b,{4,BS}])

matrix=Coefficient[BlackSaturn,BasisArray[BS,BS][-a,-b]]/.{ρ->ρ[],z->z[]}

If we expand the metric into its elementary functions then the result is too big to handle, even with Together:

ByteCount[ematrix=matrix/.ωψFunction/.HFunctions/.MFunctions/.FFunction/.PFunction/.GFunctions/.RFunctions/.μFunctions]

Level[ematrix,{-1},Heads->True]//Union

Instead, we will compute the Ricci tensor in the nonexpanded form and then replace the rules:

(met=CTensor[matrix,{-BS,-BS}])[-a,-b]

Actually, it is possible to multiply the H and ωψ functions by F and then F appears only dividing P in the metric, so that F could be reabsorbed in the definition of P, simplifying the form of some intermediate results, but we prefer to follow the original.

We still use Simplify everywhere:

SetCMetric[met,BS,SignatureOfMetric->{4,1,0}]//AbsoluteTiming

MetricCompute[met,BS,"Ricci"[-1,-1]]//AbsoluteTiming

cd=CovDOfMetric[met];

ricci=Ricci[cd];

Some components are already shown to be 0:

ricci[-a,-b]

Now we need to show that the rest are also zero:

This is easy:

Numerator[ricci[{4,-BS},{4,-BS}]]

%/.GFunctions//Expand;

%/.μFunctions//Expand;

%//Together//AbsoluteTiming

However the other six independent components are too hard for Mathematica on a laptop. We just show that results are correct for generic values of the coefficients and variables. Those random values must be exact values, in order to get an exact zero. Well, the truth is that not even with integers we can get a zero, so we do it numerically in the end.

SeedRandom[123];
random=Thread[{q,a1,a2,a3,a4,a5,c1,c2,ks,z[],ρ[]}->RandomInteger[100,11]]

rules=FoldedRule[ωψFunction,HFunctions,MFunctions,GFunctions,FFunction,PFunction,RFunctions,μFunctions];

r00=ricci[{0,-BS},{0,-BS}]/.rules;
r00n=r00/.random;
N[r00n]

r11=ricci[{1,-BS},{1,-BS}]/.rules;
r11n=r11/.random;
N[r11n]

r22=ricci[{2,-BS},{2,-BS}]/.rules;
r22n=r22/.random;
N[r22n]

r33=ricci[{3,-BS},{3,-BS}]/.rules;
r33n=r33/.random;
N[r33n]

r12=ricci[{1,-BS},{2,-BS}]/.rules;
r12n=r12/.random;
N[r12n]

r03=ricci[{0,-BS},{3,-BS}]/.rules;
r03n=r03/.random;
N[r03n]

Remove[BlackSaturn,matrix,ematrix,FFunction,GFunctions,HFunctions,PFunction,RFunctions,MFunctions,μFunctions,ωψFunction,met,cd,ricci,r00,r11,r22,r33,r12,r03,r00n,r11n,r22n,r33n,r12n,r03n,random,rules]

UndefScalarFunction[{μ1,μ2,μ3,μ4,μ5,M0,M1,M2,M3,M4,R1,R2,Hx,Hy,Gx,Gy,ωψ,P,F}]

UndefConstantSymbol[{ks,q,a1,a2,a3,a4,a5,c1,c2}]

UndefChart[BS]

UndefManifold[M]

ClearxCobaCache[All]

3.5.7. An example with two bases

Needs["xAct`xCoba`"]

$DefInfoQ=$UndefInfoQ=False;

DefManifold[M4,4,{μ,ν,α,β,γ,δ,λ}]

DefConstantSymbol[Mass,PrintAs->"M"]

This is the metric in the standard coordinates:

DefChart[schw,M4,{0,1,2,3},{t[],r[],θ[],ϕ[]}]

MatrixForm[SchwarzschildMatrix=DiagonalMatrix[{-1+2Mass/r[],1/(1-2Mass/r[]),r[]^2,r[]^2Sin[θ[]]^2}]]

$LargeComponentSize=2000;

metric=CTensor[SchwarzschildMatrix,{-schw,-schw}]

This is now the metric in an adapted frame:

DefBasis[frame,TangentM4,{0,1,2,3},BasisColor->RGBColor[0,0,1],BasisChange->CTensor[DiagonalMatrix[{1/Sqrt[1-2Mass/r[]],Sqrt[1-2Mass/r[]],1/r[],1/r[]/Sin[θ[]]}],{-frame,schw}]]

metricF=CTensor[DiagonalMatrix[{-1,1,1,1}],{-frame,-frame},0]

We start by precomputing everything in advance for both expressions of the metric:

MetricCompute[metric,schw,All]//Timing

MetricCompute[metricF,schw,All]//Timing

These are the metrics:

metric[-α,-β]

metricF[-α,-β]

and their inverses:

Inv[metric][α,β]

Inv[metricF][α,β]

Note that we do not use SetCMetric, and therefore we cannot raise and lower indices. In this way all metrics play an equivalent role.

The LeviCivita connections:

cd=CovDOfMetric[metric];

Torsion[cd]

cdF=CovDOfMetric[metricF];

Torsion[cdF]

Note the difference between a coordinated and a non-coordinated basis:

ToCTensor[TorsionPDschw,{schw,-schw,-schw}]

ToCTensor[TorsionPDframe,{frame,-frame,-frame}]

Metric compatibility. We can use any form of the metric or the derivative:

cd[-μ][metric[-α,-β]]

cdF[-μ][metricF[-α,-β]]

cdF[-μ][metric[-α,-β]]

cd[-μ][metricF[-α,-β]]

Riemann:

(riem=Simplify@Riemann[cd])[-α,-β,-γ,δ]

(riemF=Simplify@Riemann[cdF])[-α,-β,-γ,δ]

They are the same tensor, modulo a change of basis:

ToCTensor[riem,{-frame,-frame,-frame,frame}]==riemF

Ricci tensors:

Ricci[cd]

UndefTensor[Jacobianframeschw]

UndefBasis[frame]

UndefChart[schw]

Remove[SchwarzschildMatrix,cd,cdF,metric,metricF,riem,riemF]

UndefConstantSymbol[Mass]

UndefManifold[M4]

ClearxCobaCache[All]

3.6. Examples comparing with RGTC, by Bonanos

In order to understand why we do what we do you need to read Bonanos’ examples in the documentation of RGTC [link].

3.6.0. Load xCoba

Needs["xAct`xCoba`"]

$DefInfoQ=$UndefInfoQ=False;
$PrePrint=ScreenDollarIndices;

3.6.1. Flat spacetime in rotating prolate spheroidal coordinates

DefManifold[M,4,{a,b,c,d,e,f}]

DefChart[rps,M,{0,1,2,3},{t[],ϕ[],λ[],μ[]}]

DefConstantSymbol[Ω]

met=CTensor[{{1-Ω^2 (λ[]^2-1)(1-μ[]^2),-Ω (λ[]^2-1)(1-μ[]^2),0,0},{-Ω (λ[]^2-1)(1-μ[]^2),-(λ[]^2-1)(1-μ[]^2),0,0},{0,0,-(λ[]^2-μ[]^2)/(λ[]^2-1),0},{0,0,0,-(λ[]^2-μ[]^2)/(1-μ[]^2)}},{-rps,-rps}];

met[-a,-b]

$CVSimplify=Simplify;

SetCMetric[met,rps,SignatureOfMetric->{3,1,0}]//Timing

MetricCompute[met,rps,"Riemann"[-1,-1,-1,-1]]//Timing

cd=CovDOfMetric[met]

Riemann[cd]

TensorDerivative[met,cd]

TensorDerivative[Inv[met],cd]

Remove[met,cd]

UndefChart[rps]

UndefConstantSymbol[Ω]

UndefManifold[M]

ClearxCobaCache[All]

3.6.2. Schwarzschild

DefManifold[M,4,{a,b,c,d,e,f}]

DefChart[sch,M,{0,1,2,3},{t[],r[],θ[],ϕ[]}]

DefConstantSymbol[Mass]

met=CTensor[DiagonalMatrix[{1-2Mass/r[],-1/(1-2Mass/r[]),-r[]^2,-r[]^2Sin[θ[]]^2}],{-sch,-sch}]

$CVSimplify=Simplify;

SetCMetric[met,sch,SignatureOfMetric->{3,1,0}]//AbsoluteTiming

MetricCompute[met,sch,"Weyl"[-1,-1,-1,-1]]//AbsoluteTiming

cd=CovDOfMetric[met]

riemann=Riemann[cd]

riemann[{1,-sch},{0,-sch},{1,-sch},{0,sch}]

MetricCompute[met,sch,"CDRiemann"[-1,-1,-1,-1,-1]]//AbsoluteTiming

cdriemann=TensorDerivative[riemann,cd];

cdriemann[-a,-b,-c,-d,-e]+cdriemann[-c,-a,-b,-d,-e]+cdriemann[-b,-c,-a,-d,-e]//Simplify//AbsoluteTiming

Remove[met,cd,riemann,cdriemann]

UndefConstantSymbol[Mass]

UndefChart[sch]

UndefManifold[M]

ClearxCobaCache[All]

3.6.3. Schwarzschild with cosmological constant

DefManifold[M,4,{a,b,c,d,e,f}]

DefChart[sch,M,{0,1,2,3},{t[],r[],θ[],ϕ[]}]

DefConstantSymbol[Μ]

DefConstantSymbol[Λ]

met=CTensor[DiagonalMatrix[{1-2Μ/r[]-Λ r[]^2/3,-1/(1-2Μ/r[]-Λ r[]^2/3),-r[]^2,-r[]^2Sin[θ[]]^2}],{-sch,-sch}]

$CVSimplify=Simplify;

SetCMetric[met,sch,SignatureOfMetric->{3,1,0}]//AbsoluteTiming

MetricCompute[met,sch,"CDRiemann"[-1,-1,-1,-1,-1]]//AbsoluteTiming

cd=CovDOfMetric[met];

riemann=Riemann[cd];

riemann[{1,-sch},{0,-sch},{1,-sch},{0,sch}]

Ricci[cd][{2,-sch},{2,-sch}]

RicciScalar[cd][]

cdriemann=TensorDerivative[riemann,cd];

cdriemann[-a,-b,-c,-d,-e]+cdriemann[-c,-a,-b,-d,-e]+cdriemann[-b,-c,-a,-d,-e]//Simplify//AbsoluteTiming

weyl=Weyl[cd];

weyl[-a,-b,c,d]weyl[-c,-d,a,b]//AbsoluteTiming

weyl[-a,-b,c,d]weyl[-c,-d,e,f]weyl[-e,-f,a,b]//AbsoluteTiming

Remove[met,cd,riemann,cdriemann,weyl]

UndefConstantSymbol[Μ]

UndefConstantSymbol[Λ]

UndefChart[sch]

UndefManifold[M]

ClearxCobaCache[All]

3.6.4. Robertson-Walker

DefManifold[M,4,{a,b,c,d,e,f}]

DefChart[com,M,{0,1,2,3},{t[],r[],θ[],ϕ[]}]

DefConstantSymbol[κ]

DefScalarFunction[scale]

met=CTensor[{{1,0,0,0},{0,-scale[t[]]^2/(1-κ r[]^2),0,0},{0,0,-scale[t[]]^2r[]^2,0},{0,0,0,-r[]^2scale[t[]]^2Sin[θ[]]^2}},{-com,-com}];

$CVSimplify=Simplify;

SetCMetric[met,com,SignatureOfMetric->{3,1,0}]//AbsoluteTiming

MetricCompute[met,com,"CDRiemann"[-1,-1,-1,-1,-1]]//AbsoluteTiming

cd=CovDOfMetric[met];

The space is conformally flat:

Weyl[cd]

Ricci[cd][{0,-com},{0,-com}]

Einstein[cd][{0,-com},{0,-com}]

cdriemann=TensorDerivative[RiemannDown[cd],cd];

cdriemann[-a,-b,-c,-d,-e]+cdriemann[-b,-c,-a,-d,-e]+cdriemann[-c,-a,-b,-d,-e]//Simplify

Remove[met,cd,cdriemann]

UndefConstantSymbol[κ]

UndefScalarFunction[scale]

UndefChart[com]

UndefManifold[M]

ClearxCobaCache[All]

3.6.5. Conformally flat 5-dimensional metric

DefManifold[M,5,{a,b,c,d,e,f}]

DefChart[xs,M,{1,2,3,4,5},{x1[],x2[],x3[],x4[],x5[]}]

DefScalarFunction[Ω]

met=CTensor[Ω[x1[],x2[],x3[],x4[],x5[]]^2IdentityMatrix[5],{-xs,-xs}]

$CVSimplify=Simplify;

SetCMetric[met,xs,SignatureOfMetric->{4,1,0}]//AbsoluteTiming

MetricCompute[met,xs,"Weyl"[-1,-1,-1,-1]]//AbsoluteTiming

cd=CovDOfMetric[met];

Weyl[cd]

Nonzero components of RiemannDown:

Length[nonzero=DeleteCases[Flatten@First@RiemannDown[cd],0]]

Length[differentnonzero=DeleteDuplicates[nonzero]]

Apart from numerical factors:

Union[differentnonzero,SameTest->(NumericQ[Simplify[#1/#2]]&)]//Length

RicciScalar[cd][]

Constant curvature case:

ccrule=Ω->Function[{x1,x2,x3,x4,x5},1/(1+κ/4(x1^2+x2^2+x3^2+x4^2+x5^2))]

%%/.ccrule//Simplify

Ricci[cd]==4κ met/.ccrule//Simplify

Remove[ccrule,κ,met,cd,nonzero,differentnonzero]

UndefScalarFunction[Ω]

UndefChart[xs]

UndefManifold[M]

ClearxCobaCache[All]

3.6.6. Kerr in Boyer-Lindquist x-coordinates

DefManifold[M,4,{a,b,c,e,d,f}]

DefChart[BL,M,{0,1,2,3},{t[],r[],x[],ϕ[]}]

DefConstantSymbol[{Μ,α}]

Sigma=r[]^2+α^2x[]^2;
Delta=r[]^2+α^2-2Μ r[];

met=CTensor[{{1-2Μ r[]/Sigma,0,0,2α Μ r[](1-x[]^2)/Sigma},{0,-Sigma/Delta,0,0},{0,0,-Sigma/(1-x[]^2),0},{2α Μ r[](1-x[]^2)/Sigma,0,0,-(1-x[]^2)(r[]^2+α^2+2α^2Μ r[](1-x[]^2)/Sigma)}},{-BL,-BL}]

$CVSimplify=Simplify;

SetCMetric[met,BL,SignatureOfMetric->{3,1,0}]//AbsoluteTiming

MetricCompute[met,BL,"Weyl"[-1,-1,-1,-1]]

Ricci[CovDOfMetric[met]]

MetricCompute[met,BL,"CDRiemann"[-1,-1,-1,-1,-1]]//AbsoluteTiming

cd=CovDOfMetric[met];

cdR=TensorDerivative[RiemannDown[cd],cd];

cdR[-a,-b,-c,-d,-e]+cdR[-b,-c,-a,-d,-e]+cdR[-c,-a,-b,-d,-e]//Simplify

Find Killing vectors. Assume [r, x] dependence of the contravariant components of the Killings:

DefScalarFunction[{ξ0,ξ1,ξ2,ξ3}]

(ξ=CTensor[{ξ0[r[],x[]],ξ1[r[],x[]],ξ2[r[],x[]],ξ3[r[],x[]]},{BL}])[a]

We can compute the equations in two equivalent ways:

LieD[ξ[a]][met[-a,-b]]==cd[-a][ξ[-b]]+cd[-b][ξ[-a]]//Simplify

Killing=First@Head[cd[-a][ξ[-b]]+cd[-b][ξ[-a]]]//Simplify//Flatten//DeleteDuplicates

Solve[Killing[[{1,4}]]=={0,0},{ξ1[r[],x[]],ξ2[r[],x[]]}]

Killing=Killing/.(ξ1|ξ2)->Function[{r,x},0]//Simplify//DeleteDuplicates//DeleteCases[#,0]&

Solve[Killing=={0,0,0,0},{Derivative[1,0][ξ0][r,x],Derivative[0,1][ξ0][r,x],Derivative[1,0][ξ3][r,x],Derivative[0,1][ξ3][r,x]}]

We can check the result with a Lie derivative:

LieD[CTensor[{C0,0,0,C3},{BL}][a]][met[-a,-b]]

Clean up:

Remove[met,cdR,Sigma,Delta,ξ,C0,C3,cd,Killing]

UndefScalarFunction[{ξ0,ξ1,ξ2,ξ3}]

UndefConstantSymbol[{α,Μ}]

UndefChart[BL]

UndefManifold[M]

ClearxCobaCache[All]

3.6.7. Floratos-Kehagias 8-dim Ricci-flat metric

DefManifold[M,8,{a,b,c,d,e,f}]

DefChart[FK,M,Range[8],{x1[],x2[],x3[],x4[],x5[],x6[],x7[],x8[]}]

DefScalarFunction[{α,β,χ,υ,γ,η}]

met=CTensor[{{υ^2 γ^2 η^2,0,0,0,0,0,0,0},{0,υ^2 γ^2η^2,0,0,0,0,0,0},{0,0,1/υ^2,0,0,0,0,2α/υ^2},{0,0,0,1/γ^2,2β/γ^2,0,0,0},{0,0,0,2β/γ^2,γ^2+4β^2/γ^2,0,0,0},{0,0,0,0,0,η^2+4χ^2/η^2,2χ/η^2,0},{0,0,0,0,0,2χ/η^2,1/η^2,0},{0,0,2α/υ^2,0,0,0,0,4α^2/υ^2+υ^2}}/.f:(α|β|χ|υ|γ|η)->f[x1[],x2[]],{-FK,-FK}];

met[-a,-b]

$CVSimplify=Simplify;

SetCMetric[met,FK,SignatureOfMetric->{7,1,0}]//AbsoluteTiming

MetricCompute[met,FK,"Weyl"[-1,-1,-1,-1],Parallelize->True]//AbsoluteTiming

(ricci=Ricci[CovDOfMetric[met]])[-a,-b]

These are the analiticity rules used by Bonanos:

rules={
D[α[x1[],x2[]],x1[]]->-υ[x1[],x2[]]D[υ[x1[],x2[]],x2[]],
D[α[x1[],x2[]],x2[]]->   υ[x1[],x2[]]D[υ[x1[],x2[]],x1[]],
D[β[x1[],x2[]],x1[]]->-γ[x1[],x2[]]D[γ[x1[],x2[]],x2[]],
D[β[x1[],x2[]],x2[]]->   γ[x1[],x2[]]D[γ[x1[],x2[]],x1[]],
D[χ[x1[],x2[]],x1[]]->-η[x1[],x2[]]D[η[x1[],x2[]],x2[]],
D[χ[x1[],x2[]],x2[]]->   η[x1[],x2[]]D[η[x1[],x2[]],x1[]],
Z_. Derivative[0,2][G_][x1[],x2[]]+Z_. Derivative[2,0][G_][x1[],x2[]]:> -Z(Derivative[1,0][G][x1[],x2[]]^2+Derivative[0,1][G][x1[],x2[]]^2)/G[x1[],x2[]]
}

Using the first 6 rules we get that the Laplacians of α, β, γ vanish:

D[α[x1[],x2[]],x1[],x1[]]+D[α[x1[],x2[]],x2[],x2[]]==D[D[α[x1[],x2[]],x1[]]/.rules,x1[]]+D[D[α[x1[],x2[]],x2[]]/.rules,x2[]]

But using the last rule we also get:

D[α[x1[],x2[]],x1[],x1[]]+D[α[x1[],x2[]],x2[],x2[]]/.rules//Simplify

And then with the other rules:

%/.rules//Simplify

For some reason, for us the rules given by Bonanos are not enough:

ricci[-a,-b]//.rules//Simplify

We need to add the implied rules:

rules2={
D[υ[x1[],x2[]],x1[]]^2:>-D[υ[x1[],x2[]],x2[]]^2,
D[γ[x1[],x2[]],x1[]]^2:>-D[γ[x1[],x2[]],x2[]]^2,
D[η[x1[],x2[]],x1[]]^2:>-D[η[x1[],x2[]],x2[]]^2
};

ricci[-a,-b]//.rules//.rules2//Simplify

That is all a bit fishy, but in principle it is not trivial given that the functions are complex. However, if the functions are complex I’m not sure the operations are correct because I would expect a Hermitian metric, and not just symmetric. The algorithms are not prepared to handle Hermitian metrics anyway...

Remove[met,ricci,rules,rules2,G,Z]

UndefScalarFunction[{α,β,χ,γ,η,υ}]

UndefChart[FK]

UndefManifold[M]

ClearxCobaCache[All]

3.6.8. Harrison metric II-A-7

DefManifold[M,4,{a,b,c,d,e,f}]

DefChart[H,M,{0,1,2,3},{t[],x[],y[],z[]}]

DefScalarFunction[{v,F2}]

rt3=Sqrt[3];
F1=x[]/rt3-t[]^2/12 ;
F2'[z[]]=F2[z[]]v[z[]]/z[];
v'[z[]]=(v[z[]]^2-1)/(4z[])(2  v[z[]]/(z[]-1)+4/rt3);
n1t=2+rt3;
n1x=1+rt3;
n1y=-rt3;
n1z=3+rt3;
n2t=1+2/rt3;
n2x=1+2/rt3;
n2y=-1;
n2z=1+2/rt3;
n3t=1/rt3;
n3x=1+1/rt3;
n3y=-1/rt3;
n3z=2+1/rt3;

met=CTensor[DiagonalMatrix[{F1^n1t F2[z[]]^n2t(-1/z[])^n3t,-F1^n1x F2[z[]]^n2x(-1/z[])^n3x,-F1^n1y F2[z[]]^n2y(-1/z[])^n3y,-F1^n1z F2[z[]]^n2z(-1/z[])^n3z(v[z[]]^2-1)/(z[]-1)}],{-H,-H}]

$CVSimplify=Simplify;

SetCMetric[met,H,SignatureOfMetric->{3,1,0}]//AbsoluteTiming

MetricCompute[met,H,"Weyl"[-1,-1,-1,-1],Parallelize->True]//AbsoluteTiming

Ricci[CovDOfMetric[met]]

Determinant[met,H][]

Remove[met,F1,n1,n2,n3,n1t,n1x,n1y,n1z,n2t,n2x,n2y,n2z,n3t,n3x,n3y,n3z,rt3]

UndefScalarFunction[{v,F2}]

UndefChart[H]

UndefManifold[M]

ClearxCobaCache[All]

3.6.9. Bondi metric

This is an example of a case in which using the default simplification provided by Simplify is hopelessly slow. Instead, we use Expand, bringing down timings from a few minutes to less than 1 second.

DefManifold[M,4,{a,b,c,d,e,f}]

DefChart[bondi,M,{0,1,2,3},{u[],r[],θ[],ϕ[]}]

DefScalarFunction[{β,V,γ,U}]

met=CTensor[{{Exp[2 β[u[],r[],θ[]]]V[u[],r[],θ[]]/r[]-r[]^2Exp[2γ[u[],r[],θ[]]]U[u[],r[],θ[]]^2,Exp[2 β[u[],r[],θ[]]],r[]^2Exp[2γ[u[],r[],θ[]]]U[u[],r[],θ[]],0},{Exp[2 β[u[],r[],θ[]]],0,0,0},{r[]^2Exp[2γ[u[],r[],θ[]]]U[u[],r[],θ[]],0,-r[]^2Exp[2γ[u[],r[],θ[]]],0},{0,0,0,-r[]^2Exp[-2γ[u[],r[],θ[]]]Sin[θ[]]^2}},{-bondi,-bondi}];

$CVSimplify=Expand;

SetCMetric[met,bondi,SignatureOfMetric->{3,1,0}]//AbsoluteTiming

MetricCompute[met,bondi,"Einstein"[-1,-1]]//AbsoluteTiming

cd=CovDOfMetric[met];

Einstein[cd][{1,bondi},{2,-bondi}]

Einstein[cd][{1,-bondi},{2,-bondi}]

TensorDerivative[met,cd]

Check the Bianchi identities:

riemann=RiemannDown[cd];

cdriemann=TensorDerivative[riemann,cd];//AbsoluteTiming

cdriemann[-a,-b,-c,-d,-e]+cdriemann[-b,-c,-a,-d,-e]+cdriemann[-c,-a,-b,-d,-e]//Expand//AbsoluteTiming

Remove[met,cd,riemann,cdriemann]

UndefScalarFunction[$ScalarFunctions]

UndefChart[bondi]

UndefManifold[M]

ClearxCobaCache[All]

3.6.10. Kerr again in Boyer-Lindquist θ-coordinates

DefManifold[M,4,{a,b,c,d,e,f}]

DefChart[BL,M,{0,1,2,3},{u[],r[],θ[],ϕ[]}]

DefConstantSymbol[{α,Μ}]

met=CTensor[{{1-((2 Μ r[])/(((r[])^2)+(α^2) ((Cos[θ[]])^2))),1,0,((2α  Μ r[] Sin[θ[]]^2)/(((r[])^2)+(α^2) ((Cos[θ[]])^2)))},{1,0,0,-α  Sin[θ[]]^2},{0,0,-(r[]^2+α^2Cos[θ[]]^2),0},{((2α Μ r[] Sin[θ[]]^2)/(((r[])^2)+(α^2) ((Cos[θ[]])^2))),-α  Sin[θ[]]^2,0,-Sin[θ[]]^2(2 (α^2) Μ r[]+(α^2) ((r[])^2)+((r[])^4)+(α^2) ((α^2)-2 Μ r[]+((r[])^2)) ((Cos[θ[]])^2))/(((r[])^2)+(α^2) ((Cos[θ[]])^2))}},{-BL,-BL}]

$CVSimplify=Simplify;

SetCMetric[met,BL,SignatureOfMetric->{3,1,0}]//AbsoluteTiming

MetricCompute[met,BL,"Ricci"[-1,-1]]//AbsoluteTiming

Ricci[CovDOfMetric[met]]

Remove[met]

UndefConstantSymbol[{α,Μ}]

UndefChart[BL]

UndefManifold[M]

ClearxCobaCache[All]

3.6.11. Kerr in a rotating frame

DefManifold[M,4,{a,b,c,d,e,f}]

DefChart[BL,M,{0,1,2,3},{t[],r[],x[],ϕ[]}]

DefConstantSymbol[{α,Μ}]

Sigma=r[]^2+α^2x[]^2;
Delta=r[]^2+α^2-2Μ r[];

$CVSimplify=Simplify;

DefBasis[R,TangentM,{0,1,2,3},BasisColor->Blue,BasisChange->CTensor[Transpose@Inverse@{{1,0,0,2α Μ r[](1-x[]^2)/(Sigma-2Μ r[])},{0,1,0,0},{0,0,1,0},{0,0,0,1}},{-R,BL}]]

met=CTensor[DiagonalMatrix[{1-2Μ r[]/Sigma,-Sigma/Delta,-Sigma/(1-x[]^2),-Delta Sigma(1-x[]^2)/(Sigma-2Μ r[])}],{-R,-R}];

SetCMetric[met,BL,SignatureOfMetric->{3,1,0}]//AbsoluteTiming

MetricCompute[met,BL,"Weyl"[-1,-1,-1,-1]]//AbsoluteTiming

cd=CovDOfMetric[met];

Ricci[cd]

Remove[met,Delta,Sigma,cd]

UndefConstantSymbol[{α,Μ}]

UndefChart[BL]

UndefBasis[R]

UndefManifold[M]

ClearxCobaCache[All]

3.6.12. Kerr in a null frame

DefManifold[M,4,{a,b,c,d,e,f}]

DefChart[BL,M,{0,1,2,3},{t[],r[],x[],ϕ[]}]

DefConstantSymbol[{α,Μ}]

DefBasis[NP,TangentM,{1,2,3,4},BasisColor->Blue,BasisChange->CTensor[{{((((r[])^2)+(α^2) ((x[])^2))/( (Sqrt[-2 Μ r[]+((r[])^2)+(α^2) ((x[])^2)]))),-(Sqrt[(α^2)+r[] (-2 Μ+r[])]),0,0},{((((r[])^2)+(α^2) ((x[])^2))/(Sqrt[-2 Μ r[]+((r[])^2)+(α^2) ((x[])^2)])),(Sqrt[(α^2)+r[] (-2 Μ+r[])]),0,0},{((2I α Μ r[] (Sqrt[1-((x[])^2)]))/((Sqrt[(α^2)+r[] (-2 Μ+r[])]) (Sqrt[-2 Μ r[]+((r[])^2)+(α^2) ((x[])^2)]))),0,(Sqrt[1-((x[])^2)]),-((I (Sqrt[-2 Μ r[]+((r[])^2)+(α^2) ((x[])^2)]))/((Sqrt[(α^2)+r[] (-2 Μ+r[])]) (Sqrt[1-((x[])^2)])))},{-((2I α Μ r[] (Sqrt[1-((x[])^2)]))/((Sqrt[(α^2)+r[] (-2 Μ+r[])])(Sqrt[-2 Μ r[]+((r[])^2)+(α^2) ((x[])^2)]))),0,(Sqrt[1-((x[])^2)]),((I (Sqrt[-2 Μ r[]+((r[])^2)+(α^2) ((x[])^2)]))/((Sqrt[(α^2)+r[] (-2 Μ+r[])]) (Sqrt[1-((x[])^2)])))}}/(Sqrt[((r[])^2)+(α^2) ((x[])^2)])/(Sqrt[2]),{-NP,BL}]]

(met=CTensor[{{0,1,0,0},{1,0,0,0},{0,0,0,-1},{0,0,-1,0}},{-NP,-NP}])[-a,-b]

$CVSimplify=Simplify;

MetricCompute[met,BL,All,Parallelize->True]//AbsoluteTiming

cd=CovDOfMetric[met];

Einstein[cd][-a,-b]

Kretschmann[cd][]

cdRiemann=TensorDerivative[RiemannDown[cd],cd];

cdRiemann[-a,-b,-c,-d,-e]+cdRiemann[-b,-c,-a,-d,-e]+cdRiemann[-c,-a,-b,-d,-e]//Simplify

The NP scalars for Kerr (with some choice of signs...):

SetCMetric[met,BL]

l=CTensor[{1,0,0,0},{NP}];
n=CTensor[{0,1,0,0},{NP}];
m=CTensor[{0,0,1,0},{NP}];
mbar=CTensor[{0,0,0,1},{NP}];

met[-a,-b]==l[-a]n[-b]+l[-b]n[-a]-m[-a]mbar[-b]-m[-b]mbar[-a]

NPκ=-m[a]l[b]cd[-b][l[-a]]//Simplify

NPτ=-m[a]n[b]cd[-b][l[-a]]//Simplify

NPσ=-m[a]m[b]cd[-b][l[-a]]//Simplify

NPρ=-m[a]mbar[b]cd[-b][l[-a]]//Simplify

NPπ=-mbar[a]l[b]cd[-b][n[-a]]//Simplify

NPν=-mbar[a]n[b]cd[-b][n[-a]]//Simplify

NPμ=-mbar[a]m[b]cd[-b][n[-a]]//Simplify

NPλ=-mbar[a]mbar[b]cd[-b][n[-a]]//Simplify

NPϵ=-1/2(n[a]l[b]cd[-b][l[-a]]-mbar[a]l[b]cd[-b][m[-a]])//Simplify

NPγ=-1/2(n[a]n[b]cd[-b][l[-a]]-mbar[a]n[b]cd[-b][m[-a]])//Simplify

NPβ=-1/2(n[a]m[b]cd[-b][l[-a]]-mbar[a]m[b]cd[-b][m[-a]])//Simplify

NPα=-1/2(n[a]mbar[b]cd[-b][l[-a]]-mbar[a]mbar[b]cd[-b][m[-a]])//Simplify

NP propagation equations (16):

NPDl[expr_]:=Module[{a},l[a]cd[-a][expr]];
NPDn[expr_]:=Module[{a},n[a]cd[-a][expr]];
NPDm[expr_]:=Module[{a},m[a]cd[-a][expr]];
NPDb[expr_]:=Module[{a},mbar[a]cd[-a][expr]];

NPDl[l[a]]==-(NPϵ+Dagger[NPϵ])l[a]+ Dagger[NPκ]m[a]+ NPκ mbar[a]//Simplify

NPDn[l[a]]==-(NPγ+Dagger[NPγ])l[a]+ Dagger[NPτ]m[a]+ NPτ mbar[a]//Simplify

NPDm[l[a]]==-(NPβ+Dagger[NPα])l[a]+ Dagger[NPρ]m[a]+ NPσ mbar[a]//Simplify

NPDb[l[a]]==-(NPα+Dagger[NPβ])l[a]+ Dagger[NPσ]m[a]+ NPρ mbar[a]//Simplify

NPDl[n[a]]==(NPϵ+Dagger[NPϵ])n[a]+NPπ m[a]+Dagger[NPπ] mbar[a]//Simplify

NPDn[n[a]]==(NPγ+Dagger[NPγ])n[a]+ NPν m[a]+Dagger[NPν] mbar[a]//Simplify

NPDm[n[a]]==(NPβ+Dagger[NPα])n[a]+NPμ m[a]+Dagger[NPλ] mbar[a]//Simplify

NPDb[n[a]]==(NPα+Dagger[NPβ])n[a]+ NPλ m[a]+Dagger[NPμ] mbar[a]//Simplify

NPDl[m[a]]==-(NPϵ-Dagger[NPϵ])m[a]+Dagger[NPπ] l[a]+NPκ n[a]//Simplify

NPDn[m[a]]==-(NPγ-Dagger[NPγ])m[a]+Dagger[NPν] l[a]+NPτ n[a]//Simplify

NPDm[m[a]]==-(NPβ-Dagger[NPα])m[a]+Dagger[NPλ]l[a]+NPσ n[a]//Simplify

NPDb[m[a]]==-(NPα-Dagger[NPβ])m[a]+ Dagger[NPμ] l[a]+NPρ n[a]//Simplify

NPDl[mbar[a]]==(NPϵ-Dagger[NPϵ])mbar[a]+NPπ l[a]+Dagger[NPκ] n[a]//Simplify

NPDn[mbar[a]]==(NPγ-Dagger[NPγ])mbar[a]+NPν l[a]+Dagger[NPτ] n[a]//Simplify

NPDm[mbar[a]]==(NPβ-Dagger[NPα])mbar[a]+NPμ l[a]+Dagger[NPρ] n[a]//Simplify

NPDb[mbar[a]]==(NPα-Dagger[NPβ])mbar[a]+NPλ l[a]+Dagger[NPσ] n[a]//Simplify

weyl=Weyl[cd];

$LargeComponentSize=1000;

weyl[-a,-b,-c,-d]

Psi0=weyl[-a,-b,-c,-d]l[a]m[b]l[c]m[d]

Psi1=weyl[-a,-b,-c,-d]l[a]n[b]l[c]m[d]

Psi2=weyl[-a,-b,-c,-d]l[a]m[b]mbar[c]n[d]

Psi3=weyl[-a,-b,-c,-d]l[a]n[b]mbar[c]n[d]

Psi4=weyl[-a,-b,-c,-d]n[a]mbar[b]n[c]mbar[d]

UndefBasis[NP]

Remove[met,cd,cdRiemann,weyl,l,n,m,mbar,expr,"Global`NP*","Global`Psi*"]

UndefConstantSymbol[{α,Μ}]

UndefChart[BL]

UndefManifold[M]

ClearxCobaCache[All]

3.7. Extrinsic curvature computations do not yet work. TODO

Exit

<<xAct`xTensor`

$PrePrint=ScreenDollarIndices;
$DefInfoQ=False;

DefManifold[M,4,{μ,ν,α,β,γ,δ,λ,ρ,σ}]

DefMetric[-1,g[-μ,-ν],CD]

DefTensor[u[μ],M]

u/:u[μ_]u[-μ_]:=-1

DefMetric[1,h[-μ,-ν],cd,InducedFrom->{g,u}]

CD[-μ][u[-ν]]

%//GradNormalToExtrinsicK

% u[μ]//Expand

expr=GaussCodazzi[RicciCD[-μ,-ν],h]//ProjectDerivative

Now we construct the corresponding objects explicitly using xCoba:

<<xAct`xCoba`

DefChart[schw,M,{0,1,2,3},{t[],r[],θ[],ϕ[]}]

DefConstantSymbol[mass,PrintAs->"M"]

(g=CTensor[DiagonalMatrix[{-1+2mass/r[],1/(1-2mass/r[]),r[]^2,r[]^2Sin[θ[]]^2}],{-schw,-schw}])[-μ,-ν]

(u=CTensor[{1/Sqrt[1-2mass/r[]],0,0,0},{schw}])[μ]

The vector is normalized:

g[-μ,-ν]u[μ]u[ν]//Simplify

CD=LC[g]

CD[-μ][g[-α,-β]]

u[-ν]

CD[-μ][u[-ν]]//Simplify

Accelerationu=-Head[u[μ]CD[-μ][u[-ν]]]

The extrinsic curvature vanishes, because this is a time independent solution:

CD[-μ][u[-ν]]-u[-μ]Accelerationu[-ν]

ExtrinsicKh=Zero

We still have problems on how to replace the symbolic metric by the explicit metric.

expr

GaussCodazzi[RicciCD[-μ,-ν],h]//Simplify

4. TensorValues framework

4.0. Setup

Needs["xAct`xCoba`"]

DefManifold[M3, 3, {a,b,c,d,e,f}]

DefTensor[v[a],M3]
DefTensor[T[-a,-b],M3,Antisymmetric[{-a,-b}]]

DefBasis[polar,TangentM3,{0,1,2}]

DefBasis[cartesian,TangentM3,{0,1,2},BasisColor->Green]

4.1. Assigning values to components

We need some convenient way to store all the values of the components of a tensor (or, more generally, an arbitrary expression). xCoba` provides several tools to accomplish this, optimised to make full use of the symmetries. This section describes how to store and use these values, but not how to compute them. The latter is the object of Section 9 (not yet fully implemented).

Given a tensor there are several things we need to worry about when working with values:
	- 1. Are we giving values to the components of the tensor itself or to some derivative of it?
	- 2. The slot-symmetry of the expression.
	- 3. What is the character of the c-indices involved?
	- 4. Which bases are we using to form the component?
	- 5. When did we construct a particular set of components?
	- 6. Have we already computed values for all components of the expression?

This section describes a series of functions for the storage of tensor values.

4.2. FoldedRule and ValID

Identifying sets of component values.

Before introducing the general functions, let us consider a simple example. Let us suppose we have a rank two tensor (Subscript[U, ab]), with no symmetries and only one basis. Then, the straightforward way of storing its components would be with a list of replacement rules:
	{(Subscript[U, 11])-> a,  (Subscript[U, 12])-> b, (Subscript[U, 13])-> c, ...}
But now consider, still with just one basis in play and no metric, that our tensor (Subscript[T, ab]) is symmetric. Then it would be a waste to store twice the component (Subscript[T, 12])=(Subscript[T, 21]), for example. With higher rank tensors and higher symmetries the redundancy makes the process very inefficient. Instead, we could think of storing components as a pair of lists. The first one would give the relations due to symmetry and the second one the values of the independent components. We could have something like
	{ {(Subscript[T, 21])-> (Subscript[T, 12]), (Subscript[T, 31])-> (Subscript[T, 13]), (Subscript[T, 32])-> (Subscript[T, 23])}, { (Subscript[T, 11])-> a, (Subscript[T, 12])-> b, (Subscript[T, 13])-> c, (Subscript[T, 22])-> d, (Subscript[T, 23])-> e, (Subscript[T, 33])-> f} }
It may appear we have gained nothing, after all we still have 9 rules. However, all the information of the first list is determined by the symmetry group of the tensor, which xCoba` already knows since its definition. Also, the independent values may be very large expressions while the dependent rules are always very simple. This structure is represented in xCoba` with the concept of FoldedRule (defined in the auxiliary package xCore`):

?FoldedRule

Example:

x+t/.FoldedRule[{x->y,t->x},{y->z}]

In the next subsection we shall see how xCoba`  generates these lists. The next problem comes when we consider a more general case where we have a metric and several bases. In this situation we may want to store the values for a given tensor in different bases and with different index characters. However, not all those combinations are independent. Recalling our symmetric tensor (Subscript[T, ab]), we  see that the FoldedRules that would contain its values for ((Subscript[T, a])^b)and (Subscript[(T^b), a]) would have the same independent components (likewise for (Subscript[T, ab]) and (Subscript[T, ba])). So among all sets of values for a tensor, only some will be independent. xCoba`  identifies these independent sets with the concept of ValID:

?ValID

In our example, we would have for our tensor (Subscript[U, ab]) a single ValID, which would be
	ValID[U, { {polar, polar}} ]
(both indices in the base polar). We have several for (Subscript[T, ab])
	ValID[T, { {polar, -polar} , {-polar, polar}} ]
	 ValID[T, { {polar, cartesian}, {cartesian, polar}]

The first would represent the fact that ((Subscript[T, a])^b)and (Subscript[(T^b), a]) have the same independent rules and the second would represent the situation for (Subscript[T, ab]) and (Subscript[T, ba]). Notice how even in the case of (Subscript[U, ab]), where there is only one possible basis configuration in the ValID, we give a list of lists of bases. The command VTensorValIDs returns all the ValIDs  we have stored for a given tensors (and its derivatives, as we shall see below).

All of this will be much clearer once we see how xCoba`  generates the FoldedRules and their corresponding ValIDs for a tensor in the next subsection.

4.3. ComponentValue and TensorValues without independent values

Storing values for components.

We have seen how xCoba` stores and labels sets of tensor values for different basis configurations; this section describes how these sets are generated. The basic function is ComponentValue, which generates the rule for a single component. ComponentValue can be called with one or two arguments:

?ComponentValue

We begin by examining how it works with just one argument (i.e. without specifying an independent value).

Let us define a totally antisymmetric tensor:

DefTensor[W[a,b,c],M3,Antisymmetric[{a,b,c}]]

SetOptions[CanonicalPerm, MathLink-> True]

Suppose we want to generate a rule for one of its components

W[{0,polar},{1,polar},{2,polar}]

ComponentValue[%]

TensorValues[W]

As we can see, xCoba` has recognised that (W^012) is an independent component and has added a rule for it in the second list of the FoldedRule. This rule is trivial, as we have not specified the numerical value of the component. But consider now the following

ComponentValue[W[{2,polar},{1,polar},{0,polar}]]

TensorValues[W]

xCoba`  sees that (W^210) is not an independent component and generates for it a rule in the first list of the FoldedRule. We can easily generate the full set of component values:

W[{a,polar},{b,polar},{c,polar}]//ComponentArray//Flatten

ComponentValue/@ %

As we can see by reading the messages, the rules for (W^012) and (W^210) have not been recomputed. Now:

TensorValues[W]

There is only one independent rule. We have one ValID for (W^abc), which consists of only one basis configuration:

TensorValIDs[W]

ValIDs are timestamped:

DateOfValID/@ %

Let us now consider an example with several bases. Recall that (Subscript[T, ab]) is a symmetric tensor:

ComponentValue[T[{2,-polar},{1,-cartesian}]]

T[-{a,polar},-{b,cartesian}] //ComponentArray//Flatten

ComponentValue /@ %

TensorValIDs[T]

We still do not have all the rules, because our ValID mixes the cases (Subscript[T, ab]) and (Subscript[T, ba]):

T[-{a,cartesian},-{b,polar}] //ComponentArray//Flatten

ComponentValue /@ %

TensorValues[T]

TensorValIDs[T]

Now we still only have one ValID for (Subscript[T, ab]), but it consists of two basis configurations.  As we can see, the FoldedRule returned by TensorValues mixes the cases (Subscript[T, ab]) and (Subscript[T, ba]). Another example, now mixing index characters:

ComponentArray[T[{a,polar},-{b,cartesian}]] //Flatten

ComponentValue /@ %

ComponentArray[T[{-a,cartesian},{a,polar}]] //Flatten

ComponentValue /@ %

TensorValIDs[T]

We have two ValIDs, both consisting of two basis configurations. Notice how newer ValIDs appear first.We can retrieve the TensorValues for either one of them:

TensorValues[T,{{-cartesian,polar},{polar,-cartesian}}]

or for all at the same time:

TensorValues[T]

We can delete single rules:

ComponentValue[T[{1,-cartesian},{2,-polar}],Null]

We can also easily delete all TensorValues for a given tensor:

DeleteTensorValues[W]

or only those for a single ValID:

DeleteTensorValues[T,{{-cartesian,polar},{polar,-cartesian}}]

4.4. ComponentValue and TensorValues with independent values

Until now, the second list of the FoldedRules contained only trivial replacement rules such as (Subscript[T, 11])-> (Subscript[T, 11]). It is very simple to specify values for the LHS, we just have to call ComponentValue with two arguments:

ComponentValue[T[{1,-polar},{2,-polar}],5]

TensorValues[T,{{-polar,-polar}}]

Notice what happens if we specify a dependent component

ComponentValue[T[{1,-polar},{0,-polar}],6]

First the dependent rule is added and then the value (with a possible sign) is assigned to the corresponding independent component:

TensorValues[T,{{-polar,-polar}}]

ComponentValue does not allow the user to introduce an inconsistent value

ComponentValue[W[{1,polar},{1,polar},{1,polar}],5]

We switch the rule generation messages off

$CVVerbose=False;

ComponentValue is threaded on pairs of lists when it has two arguments. This allows us to generate all independent rules with just one command.

values=Table[i+j,{i,1,3},{j,1,3}]

ComponentValue[ComponentArray[T[{a,polar},{b,polar}]],values]

ColumnForm/@TensorValues[T,{{polar,polar}}]

4.5. AllComponentValues

Generating all value rules.

We have just described an easy way to generate all the rules for a ValID which consisted on only one index configuration. But as we saw before, a single ComponentArray will not generate all the independent rules for a ValID with several index configurations. One such case was the ValID for (Subscript[T, ab]) and (Subscript[T, ba]). The following command (which only works for tensors and not for derivatives as we shall see in Section 7.4.) applies ComponentArray as many times as necessary in order to generate all the rules.

values=RandomInteger[{0,5},{3,3}]

ColumnForm/@ AllComponentValues[T[-{a,polar},-{b,cartesian}],values]

We turn the messages back on:

$CVVerbose=True;

4.6. Components and derivatives

ComponentValue also works with derivatives of tensors (but not for more general expressions, you can use Coba as a placeholder and then substitute the expression). Remember that with our definitions the components of the derivative are not the derivatives of the components.

PD[{2,-cartesian}][v[{1,cartesian}]]

ComponentValue[%]

PD[{1,-polar}][PDpolar[{2,-cartesian}][v[{1,cartesian}]]]

ComponentValue[%]

Derivatives are represented by  additional central arguments in ValID:

TensorValIDs[v]//ColumnForm

DeleteTensorValues[v]

4.7. BasisValues

Components of the basis changes.

The components of the basis change matrices require separate treatment and are represented by the BasisValues head. Here we do not allow derivatives, because the derivatives of these objects are automatically replaced by Christoffel tensors.

?BasisValues

$CVVerbose=True;

ComponentValue[Basis[{1,-polar},{2,cartesian}]]

AllComponentValues[Basis[{-a,-polar},{b,cartesian}],Table[Random[],{3},{3}]]

BasisValues[-polar,cartesian]

The inverse basis change is not automatically computed, because it may take a very long time

BasisValues[cartesian,-polar]

4.8. Components of the metric, MetricInBasis

Components of the metric.

It is very common in General Relativity to start working from a known line element. Inded, the typical approach is to start from the components of a metric in a coordinated basis, which determine both the metric and the coordinate system unambiguously. xCoba` provides a specialised function to define the values for the components of a metric, which can be used on its own or as an option for DefBasis.

?MetricInBasis

DefMetric[-1,metric[-a,-b], cd,{"|","D"},PrintAs-> "g"]

$Metrics

$CVVerbose=False;

MetricInBasis[metric, -polar, Table[i+j,{i,3},{j,3}]]

TensorValues[metric]

An example with a diagonal metric

DeleteTensorValues[metric]

MetricInBasis[metric,-polar, {1,2,3}]

And know with an orthonormal one

?SignatureOfMetric

SignatureOfMetric[metric]^={2,1,0}

MetricInBasis[metric,-cartesian, "Orthonormal"]

TensorValues[metric]

4.9. Changing bases or index characters

Changing bases or index characters.

Suppose we already have the tensor values for a given ValID and we want to generate the corresponding set for an (inequivalent) index configuration. We may want to perform a typical change of basis like (Subscript[T, ab]) to (Subscript[T, ab]), for example. Or we may want to lower or raise some indices. Given starting and finishing index configurations, xCoba` can compute the best route between them with the function ChangeComponents

A simple example

ChangeComponents[v[{a,polar}],v[-{a,polar}]]

TensorValIDs[v]

DeleteTensorValues[v]

A more complicated example, with an antisymmetric tensor (Subscript[Y, ab]). To keep it shorter we can change the cnumbers of polar to make xCoba` think the manifold is two dimensional

DefTensor[Y[a,b],M3,Antisymmetric[{1,2}]]

CNumbersOf[polar] ^={1,2}

We set some configuration global variables

$CVVerbose=True;
$CVReplace=False;
$CCSimplify=Expand;
$TUseValues=ToCanonical;

ChangeComponents[Y[-{a,polar},-{b,polar}],Y[{a,polar},{b,polar}]]

The system prints "Value "(Subscript[g, 12]^(  )) (Subscript[Y,  2]^(1 ))+(Subscript[g, 22]^(  )) (Subscript[Y,  2]^(2 ))" expected to be 0 by symmetry."This is because of $UseValues =ToCanonical which tells the system only to use the independent rules at each step. If we had written $UseValues =All instead that message would not have appeared. However, whenever a component is 0 because of the symmetry, the symmetry value is used, so our result is correct.

TensorValues[Y]

We can always collapse the FoldedRules, but this may generate very long expressions

?CollapseFoldedRule

CollapseFoldedRule[%%,All]

CNumbersOf[polar] ^={0,1,2}

We can also change bases

ChangeComponents[v[{a,cartesian}],v[{a,polar}]]

4.10. Replacing TensorValues in expressions

Replacing TensorValues in expressions.

As we have seen, xCoba` does not provide an automatic tool for setting values (use Set or SetDelayed instead of rules), because this is potentially dangerous and removing the definitions would not be trivial. However, it is very simple to apply all the rules in TensorValues to a given expression

?ToValues

TensorValues[T,{{polar,polar}}]

$CVVerbose=False;

ChangeComponents[T[-{a,polar},-{b,polar}],T[{a,polar},{b,polar}]]

T[{a,polar},{b,polar}]T[-{a,polar},-{b,polar}]

TraceBasisDummy[%]

That expression has only the tensor T and so we get

ToValues[%]

Now the metric tensors are explicit and hence:

ToValues[%]

4.11. Clean up

UndefMetric[metric]

UndefTensor/@{v,T,W,Y};

UndefBasis/@{polar,cartesian};

UndefManifold[M3]

Remove[i,j,t,x,y,z,values]

4.12. Example: Schwarzschild

This section is based on notes from Alfonso García-Parrado, circulated as a separate notebook since 2006, and now included here. This section can be executed separately from the rest of the notebook.

4.12.1. Setup

Load xCoba`, if it is not loaded yet:

Needs["xAct`xCoba`"]

$PrePrint=ScreenDollarIndices;

Define 4d manifold with Lorentzian metric:

DefManifold[M4,4,IndexRange[a,p]]

DefMetric[{3,1,0},metricg[-a,-b],CD,PrintAs->"g"]

Define the coordinates and their associated frame:

DefChart[Sch,M4,{0,1,2,3},{τ[],r[],θ[],ϕ[]}]

The mass of the spacetime:

DefConstantSymbol[M]

The covariant derivative of the elements of the basis will be described by this Christoffel tensor:

CD[-a][Basis[b,{-c,-Sch}]]

4.12.2. Input metric

Specify the values of the metric components in the coordinate basis just defined. We will store those components as tensor values for the metricg tensor.

This is the matrix of scalar components of the Schwarzschild metric in the Sch coordinate basis:

matrix=(({{-(1-2M/r[]), 0, 0, 0}, {0, 1/(1-2M/r[]), 0, 0}, {0, 0, r[]^2, 0}, {0, 0, 0, r[]^2Sin[θ[]]^2}}));

Set those scalars as tensor values for the metricg tensor, reporting each individual assignment. We will later see how to deactivate them:

MetricInBasis[metricg,-Sch,matrix]

This is the object stored internally, that separates values due to symmetry from truly independent values:

TensorValues[metricg]

In terms of a basis the metric takes the form:

TraceBasisDummy[metricg[{-a,-Sch},{-b,-Sch}]Basis[-c,{a,Sch}]Basis[-d,{b,Sch}]]

downmetric[-c_,-d_]=%/.TensorValues[metricg]

We need now the expression of the inverse metric:

Inverse[matrix]//Simplify

AllComponentValues[metricg[{a,Sch},{b,Sch}],%]

In terms of a basis this is:

TraceBasisDummy[metricg[{a,Sch},{b,Sch}]Basis[c,{-a,-Sch}]Basis[d,{-b,-Sch}]]

upmetric[c_,d_]=%/.TensorValues[metricg]

Alternatively we can also compute:

downmetric[-a,-b]

InverseMetric[%,Sch][c,d]

%==upmetric[c,d]

Consistency check:

upmetric[a,b]downmetric[-b,-c]

%//ContractBasis

%//Simplify

From now on we do not output the ComponentValue messages:

$CVVerbose=False;

4.12.3. The Klösch-Strobl coordinate system

As an example of basis change, we introduce an alternative coordinate system. See "Explicit global coordinates for Schwarzschild and Reissner-Nordstrøm solutions", Thomas Klösch and Thomas Strobl Class. Quantum Grav. 13 (1996) 1191--1199.

Define the new chart:

DefChart[Klosch,M4,{0,1,2,3},{x[],y[],Θ[],Φ[]},ChartColor->Blue]

Basis[-a,{1,Klosch}]

This is the coordinate change:

change={τ[]->x[]y[]+2M(1+Log[y[]/x[]]),r[]->x[]y[]+2M,θ[]->Θ[],ϕ[]->Φ[]}

Jacobian matrix:

Outer[D,Last/@change,ScalarsOfChart[Klosch]]

AllComponentValues[Basis[{a,Sch},{-b,-Klosch}],%]

Let us see the metric form in the new coordinates:

downmetric[-a,-b]

SeparateBasis[Klosch][%,IndicesOf[Sch]]

TraceBasisDummy[%]

%/.BasisValues[Sch,-Klosch]/.change

BasisCollect[%,Simplify]

4.12.4. Compute Christoffel

Next we calculate the Christoffel symbols. We will first do it directly from the metric coefficients, and then with a dedicated function:

First we compute all Christoffel values given by its symmetry:

AllComponentValues[Christoffel[CD,PDSch][{a,Sch},{-b,-Sch},{-c,-Sch}]]

This must be zero:

CD[-c][downmetric[-a,-b]]

%//ToBasis[Sch]

Flatten[%//ComponentArray]/.TensorValues[ChristoffelCDPDSch]

We solve the system of equations for the independent components of the Christoffel tensor:

Simplify/@First@Solve[Map[#==0&,%],First/@IndependentRules[TensorValues[ChristoffelCDPDSch]]]

and store them:

%/.Rule->ComponentValue

This is the Christoffel tensor:

christoffel=BasisExpand[ChristoffelCDPDSch[a,-b,-c],Sch]//ToValues

We may calculate the Christoffel symbols of different kind. Note that by symmetry we compute simultaneously {up,down,up} and {up,up,down}:

$BMUseValues=All;
$TUseValues=All;
$CCSimplify=Simplify;

ChangeComponents[ChristoffelCDPDSch[{a,Sch},{-b,-Sch},{c,Sch}],ChristoffelCDPDSch[{a,Sch},{-b,-Sch},{-c,-Sch}]]

ChangeComponents[ChristoffelCDPDSch[{-a,-Sch},{-b,-Sch},{-c,-Sch}],ChristoffelCDPDSch[{a,Sch},{-b,-Sch},{-c,-Sch}]]

These are the values we have computed, taking into account the symmetries:

TensorValIDs[ChristoffelCDPDSch]//ColumnForm

and this is when we did it:

DateOfValID/@TensorValIDs[ChristoffelCDPDSch]//ColumnForm

We can compute the same result directly with this function:

christoffelB=ChristoffelFromMetric[metricg[-a,-b],Sch][a,-b,-c]

christoffel-christoffelB

4.12.5. Compute Riemann

We now calculate the Riemann tensor. This time we use first a dedicated function and then we use the formula in terms of derivatives of Christoffel.

Compute the Riemann tensor directly:

riemann=RiemannFromChristoffel[ChristoffelCDPDSch[a,-b,-c],Sch][-a,-b,-c,d]

Check that the corresponding Ricci tensor vanishes:

riemann delta[-d,b]//ContractBasis

Alternatively, we can start from the formula of the Riemann tensor in terms of derivatives of the Christoffel tensor:

ChangeCurvature[RiemannCD[-a,-b,-c,d],CD,PDSch]

Expand into all actual components, insert the actual values, and compute the derivatives:

BasisExpand[%,Sch]//ToValues

riemannB=BasisCollect[%,Simplify]

riemann-riemannB

Store the results in the TensorValues framework:

ComponentArray[riemann downmetric[-d,-e]//ToBasis[Sch]//ContractBasis//ToValues//Simplify]

AllComponentValues[RiemannCD[{-a,-Sch},{-b,-Sch},{-c,-Sch},{-d,-Sch}],%];

Print the independent components only:

IndependentRules[TensorValues[RiemannCD]]//Column

4.12.6. Clean up

UndefMetric[metricg]

UndefConstantSymbol[M]

UndefBasis/@{Klosch,Sch}

UndefManifold[M4]

Remove[change,G,downmetric,upmetric,christoffel,christoffelB,riemann,riemannB]

4.13. Example: Kerr-Newman

This section can be evaluated separately from the rest of the notebook.

4.13.1. Setup

Load xCoba`, if it is not loaded yet:

Needs["xAct`xCoba`"]

$DefInfoQ=False;
$CVVerbose=False;

$PrePrint=ScreenDollarIndices;

Define 4d manifold with Lorentzian metric:

DefManifold[M4,4,IndexRange[a,p]]

DefMetric[{3,1,0},metricg[-a,-b],CD,PrintAs->"g"]

Declare the Boyer-Lindquist chart and their associated frame:

DefChart[B,M4,{0,1,2,3},{t[],r[],θ[],ϕ[]}]

Define the Kerr-Newman constants:

DefConstantSymbol[Mass,PrintAs->"M"]
DefConstantSymbol[Rotation,PrintAs->"a"]
DefConstantSymbol[Charge,PrintAs->"Q"]

The covariant derivative of the elements of the basis will be described by this Christoffel tensor:

CD[-a][Basis[b,{-c,-B}]]

4.13.2. Input metric

Specify the values of the metric components in the coordinate basis just defined. We will store those components as tensor values for the metricg tensor.

This is the matrix of scalar components of the Kerr-Newman metric in the B coordinate basis:

Delta=r[]^2+Rotation^2+Charge^2-2Mass r[];
Sigma=r[]^2+Rotation^2Cos[θ[]]^2;
MatrixForm[
matrix={
{-(Delta-Rotation^2Sin[θ[]]^2)/Sigma,0,0,-Rotation Sin[θ[]]^2(r[]^2+Rotation^2-Delta)/Sigma},
{0,Sigma/Delta,0,0},
{0,0,Sigma,0},
{-Rotation Sin[θ[]]^2(r[]^2+Rotation^2-Delta)/Sigma,0,0,((r[]^2+Rotation^2)^2-Delta Rotation^2Sin[θ[]]^2)/Sigma Sin[θ[]]^2}
}
]

Set those scalars as tensor values for the metricg tensor:

MetricInBasis[metricg,-B,matrix]

This is the object stored internally, that separates values due to symmetry from truly independent values:

TensorValues[metricg]

In terms of a basis the metric takes the form:

TraceBasisDummy[metricg[{-a,-B},{-b,-B}]Basis[-c,{a,B}]Basis[-d,{b,B}]]

%/.TensorValues[metricg]

4.13.3. Compute metric-related tensors

Rather than following the flexible, but potentially slow, steps we used with the Schwarzschild metric, here we shall use MetricCompute, which is simpler and faster, as we did in the CTensor framework.

Compute the down-Riemann tensor. The use of Simplify simplification produces nicer results, but slower:

MetricCompute[metricg,B,"Riemann"[-1,-1,-1,-1],CVSimplify->Simplify,Verbose->True,Parallelize->True]

Check the independent components of the result:

IndependentRules[TensorValues[RiemannCD]]//Column

Compute the Kretschmann scalar. Note how previous computations were cached:

MetricCompute[metricg,B,"Kretschmann"[],CVSimplify->Simplify,Verbose->True]

This is the result:

KretschmannCD[]//ToValues

Collect powers of the charge parameter:

Collect[#,Charge,FullSimplify]&/@%

4.13.4. Bianchi identity

Request computation of derivatives of the Riemann tensor:

MetricCompute[metricg,B,All,CVSimplify->Simplify,Verbose->True,Parallelize->True]

In particular now we have computed the determinant of the metric as well:

DetmetricgB[]//ToValues

Check of the contracted Bianchi identity. Start from the uncontracted derivative of the Einstein tensor:

CD[-a][EinsteinCD[-b,-c]]//ToBasis[B]//ToBasis[B]

Now perform the contraction:

%metricg[{c,B},{b,B}]//Expand

Expand into components. Each expression has 140 terms:

array=%//TraceBasisDummy//ComponentArray//Expand

Length/@array

Replace the actual values of the tensors and simplify:

array//ToValues//Simplify

4.13.5. Clean up

UndefMetric[metricg]

UndefConstantSymbol[Mass]
UndefConstantSymbol[Rotation]
UndefConstantSymbol[Charge]

UndefChart[B]

UndefManifold[M4]

Remove[matrix,array,Delta,Sigma]

∞. Final comments

Note: For further information about xCoba`, and to be kept informed about new releases, you may contact the authors electronically at yllanes@xact.es or jose@xact.es. Suggestions and comments are welcome and very much appreciated!

This is the doc file xCobaDoc.nb of version 0.8.4 of xCoba`. Last update on 16 February 2020.

?xAct`xCoba`*

Names["xAct`xCoba`*"]//Length

1.2. Euclidean spaces (Where should this go?)

Currently the only predefined manifolds are the Euclidean spaces Reals[dim] for nonnegative integer dim. Its elements (“points”) are lists of real numbers. There are no complex manifolds in xTensor` yet.

Euclidean manifold.

This is the manifold of 3-tuples:

Reals[3]

Points are considered 0-dimensional manifolds

P={-1,3,Pi};

ManifoldQ[P]

DimOfManifold[P]

They are submanifolds of the Euclidean space of the corresponding dimension:

SubmanifoldQ[Reals[3],P]

SubmanifoldQ[Reals[5],P]

The Euclidean manifolds have tangent spaces, denoted as TangentReals[dim]. Its elements are vectors, and we will NOT use a notation based on lists. Euclidean vectors will be handled in the twin package xCoba`, using the CTensor[list, {basis}] construction.

Tangent bundle of an Euclidean manifold.

This is the tangent vbundle of the Euclidean 4-space:

Tangent[Reals[4]]

%//InputForm