Tensorial Tutorials

Common Tensor Operations With an Oblique Coordinate System

David Park

The notebook must be evaluated in order as later sections depend upon results established in earlier sections.

In this notebook we show how to:
1) Represent and manipulate various objects in an oblique coordinate system.
2) Use full tensor notation with a tensor basis and CircleTimes.
3) Evaluate expressions such as T[u,v] where T is a tensor and u and v are vectors.
4) Write transformations between coordinate systems.

Initialization

In[6]:=

Needs["TensorCalculus4`Tensorial`"]

We define tensor shortcuts and index flavors for the various tensors that will be used in the notebook.

In[7]:=

DeclareIndexFlavor[{red, Red}]

In[8]:=

DefineTensorShortcuts[{{g, u, v}, 1}, {{g, T, A, δ}, 2}]

SetTensorValues[δud[i, j], IdentityMatrix[NDim]]

SetTensorValues[δud[i, j]//ToFlavor[red], IdentityMatrix[NDim]]

1. Basis Vectors and Reciprocal Basis

We establish the natural and dual basis vectors for a Cartesian coordinate system. Notice that a first order tensor g can be given vector components.

In[11]:=

SetTensorValueRules[gu[i], IdentityMatrix[3]]

SetTensorValueRules[gd[i], IdentityMatrix[3]]

TensorValueRules[g]

Out[13]=

{g_1^1→ {1, 0, 0}, g_2^2→ {0, 1, 0}, g_3^3→ {0, 0, 1}, g_1^1→ {1, 0, 0}, g_2^2→ {0, 1, 0}, g_3^3→ {0, 0, 1}}

We will represent tensors in the oblique frame by red indices. The rows of the GBasis matrix will be the oblique basis vectors. The following establishes the oblique basis as tensor objects.

In[14]:=

GBasis = ({{1, 1, 0}, {0, 1, 2}, {1, 0, 1}}) ;

SetTensorValueRules[gd[red @ i], GBasis]

TensorValueRules[g]

Out[16]=

There is an easy way to calculate the red reciprocal basis vectors. Just take the rows of Transpose[Inverse[GBasis]]. However, let's work by more formal tensor methods.  This gives us some practice in performing tensor manipulations with Tensorial and Mathematica. We first usurp guu[red@i,red@j] to represent the components of gu[i]. (We will later use it to represent the metric tensor.)

In[17]:=

SetTensorValueRules[gu[red @ i], guu[i, j]//ToFlavor[red]//ToArrayValues[]]

Print["Definition of dual basis."] ;

gu[i] . gd[j] == δud[i, j]//ToFlavor[red]

Print["Expanding to an array of equations."] ;

%%//ToArrayValues[]

Print["Solving the equations."] ;

gsols = Solve[Flatten[%%]][[1]]

Print["Substituting into the components array and saving as up components of g"] ;

(step1 = (gu[red @ i]//ToArrayValues[])/.gsols)//MatrixForm

SetTensorValueRules[gu[red @ i], step1]

TensorValueRules[g]

Definition of dual basis.

Out[19]=

g_i^i . g_j^j == δ_ (ij)^(ij)

Expanding to an array of equations.

Out[21]=

Solving the equations.

Out[23]=

Substituting into the components array and saving as up components of g

Out[25]//MatrixForm=

( {{1/3, 2/3, -1/3}, {-1/3, 1/3, 1/3}, {2/3, -2/3, 1/3}} )

Out[27]=

We now have the basis and reciprocal basis vectors in both the black (Cartesian) and red (oblique) coordinate system.

We can check that we have obtained a proper solution for the reciprocal basis.

In[28]:=

gd[red @ i] . gu[red @ j]

%//ToArrayValues[]//MatrixForm

Out[28]=

g_i^i . g_j^j

Out[29]//MatrixForm=

( {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}} )

Notice how convenient and powerful the ToArrayValues command is.

2. Calculating the Metric

We can calculate the metric in the red coordinates by the standard definition. (The metric in the black coordinates is just the IdentityMatrix.)

In[30]:=

gd[i] . gd[j]//ToFlavor[red]

(metric = %//ToArrayValues[])//MatrixForm

Out[30]=

g_i^i . g_j^j

Out[31]//MatrixForm=

( {{2, 1, 1}, {1, 5, 2}, {1, 2, 2}} )

We set the metric values using SetTensorValueRules. We want to set both the down and up version of the metric. For no other reason than to illustrate the difference, we will set the Cartesian metric values using SetTensorValues.

In[32]:=

SetTensorValueRules[gdd[a, b]//ToFlavor[red], metric]

SetTensorValueRules[guu[a, b]//ToFlavor[red], Inverse[metric]]

SetTensorValues[gdd[a, b], IdentityMatrix[3]]

SetTensorValues[guu[a, b], IdentityMatrix[3]]

We could also have done this a little more compactly with...

In[36]:=

MapThread[SetTensorValueRules[#1, #2] &, {{gdd[a, b], guu[a, b]}//ToFlavor[red], {metric, Inverse @ metric}}] ;

SetTensorValues[#, IdentityMatrix[3]] &/@{gdd[a, b], guu[a, b]} ;

Notice again that we are using g with one index and g with two indices. They are different objects and do not interfer with each other.

In[38]:=

TensorValueRules[g]

Out[38]=

What happened to the Cartesian metric values? They are stored as UpValues of g.

In[39]:=

UpValues[g]

Out[39]=

The following illustrates how definitions are immediately applied, but the substitution rules are only applied when specified. Using substitution rules is nice for learning tensor calculus and Tensorial. We can more clearly control and see the explicit steps.

In[40]:=

{gdd[i, j], gdd[red @ i, red @ j]}

MatrixForm/@EinsteinArray[]/@%

%/.TensorValueRules[g]

Out[40]=

{g_ (ij)^(ij), g_ (ij)^(ij)}

Out[41]=

{( {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}} ), ( {{g_ (11)^(11), g_ (12)^(12), g_ (13)^(13)}, {g_ (21)^(21), g_ (22)^(22), g_ (23)^(23)}, {g_ (31)^(31), g_ (32)^(32), g_ (33)^(33)}} )}

Out[42]=

{( {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}} ), ( {{2, 1, 1}, {1, 5, 2}, {1, 2, 2}} )}

One the other hand, ToArrayValues automatically uses not only value definitions, but also uses value rules.

In[43]:=

{gdd[i, j], gdd[red @ i, red @ j]}

MatrixForm/@ToArrayValues[]/@%

Out[43]=

{g_ (ij)^(ij), g_ (ij)^(ij)}

Out[44]=

{( {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}} ), ( {{2, 1, 1}, {1, 5, 2}, {1, 2, 2}} )}

3. Dot Product of Two Vectors in the Two Coordinate Systems

Here are two vectors in the black coordinate system. In the natural Cartesian basis the up and down components of the vectors are the same.

In[45]:=

SetTensorValueRules[uu[i], {2, -1, 3}]

SetTensorValueRules[vu[i], {1, 1, 2}]

SetTensorValueRules[ud[i], {2, -1, 3}]

SetTensorValueRules[vd[i], {1, 1, 2}]

TensorValueRules[u, v]

Out[49]=

{u_1^1→2, u_2^2→ -1, u_3^3→3, u_1^1→2, u_2^2→ -1, u_3^3→3, v_1^1→1, v_2^2→1, v_3^3→2, v_1^1→1, v_2^2→1, v_3^3→2}

The tensor method of taking their dot product is...

In[50]:=

gdd[i, j] uu[i] vu[j]

%//ToArrayValues[]

Out[50]=

g_ (ij)^(ij) u_i^i v_j^j

Out[51]=

7

To compute the up components of u in the red frame we use the standard definition. We need to set IndexParsingRules because Tensorial does not know how to extract indices from a dot product with one factor an array. The index parsing rule extracts the tensor component of the dot product.

In[52]:=

IndexParsingRules = {a_List . b_→b} ;

Print["Standard definition of vector component."] ;

u . gu[red @ i]

Print["Substitute indicial expression of vector."] ;

%%/.u→uu[j] gd[j]

Print["Break out vector components from dot product."] ;

%%//LinearBreakout[Dot][gu[_], gd[_]]

%%//EinsteinSum[]

%/.TensorValueRules[g, u]

%//ToArrayValues[]

SetTensorValueRules[uu[red @ i], %]

Standard definition of vector component.

Out[54]=

u . g_i^i

Substitute indicial expression of vector.

Out[56]=

(g_j^j u_j^j) . g_i^i

Break out vector components from dot product.

Out[58]=

g_j^j . g_i^i u_j^j

Evaluate to array values and save results. We needed to set IndexParsingRules because Tensorial does not know how to extract indices from a dot product with one factor an array.

Out[60]=

g_1^1 . g_i^i u_1^1 + g_2^2 . g_i^i u_2^2 + g_3^3 . g_i^i u_3^3

Out[61]=

3 {0, 0, 1} . g_i^i - {0, 1, 0} . g_i^i + 2 {1, 0, 0} . g_i^i

Out[62]=

{-1, 0, 3}

We can calculate the down values by lowering the index.

In[64]:=

gdd[i, j] uu[j]//ToFlavor[red]

%//ToArrayValues[]

SetTensorValueRules[ud[red @ i], %]

TensorValueRules[u]

Out[64]=

g_ (ij)^(ij) u_j^j

Out[65]=

{1, 5, 5}

Out[67]=

{u_1^1→2, u_2^2→ -1, u_3^3→3, u_1^1→2, u_2^2→ -1, u_3^3→3, u_1^1→ -1, u_2^2→0, u_3^3→3, u_1^1→1, u_2^2→5, u_3^3→5}

All of these different u components represent the same geometric object. Now, let's calculate the components of v in the red frame. Just for variety we will calculate the down components of the v vector first and then raise the index to obtain the up values.

In[68]:=

v . gd[red @ i]

%/.v→vu[j] gd[j]

%//LinearBreakout[Dot][gu[_], gd[_]]

%//ToArrayValues[]

SetTensorValueRules[vd[red @ i], %]

Out[68]=

v . g_i^i

Out[69]=

(g_j^j v_j^j) . g_i^i

Out[70]=

g_j^j . g_i^i v_j^j

Out[71]=

{2, 5, 3}

In[73]:=

guu[i, j] vd[j]//ToFlavor[red]

%//ToArrayValues[]

SetTensorValueRules[vu[red @ i], %]

TensorValueRules[v]

Out[73]=

g_ (ij)^(ij) v_j^j

Out[74]=

{1/3, 2/3, 2/3}

Out[76]=

{v_1^1→1, v_2^2→1, v_3^3→2, v_1^1→1, v_2^2→1, v_3^3→2, v_1^1→2, v_2^2→5, v_3^3→3, v_1^1→1/3, v_2^2→2/3, v_3^3→2/3}

We can calculate the dot product by 4 different methods in each of the coordinate systems. We should and do obtain the same answer by all methods.

In[77]:=

step1 = {ud[i] vu[i], uu[i] vd[i], gdd[i, j] uu[i] vu[j], guu[i, j] ud[i] vd[j]} ;

Join[step1, step1//ToFlavor[red]]

ToArrayValues[]/@%

Out[78]=

{u_i^i v_i^i, u_i^i v_i^i, g_ (ij)^(ij) u_i^i v_j^j, g_ (ij)^(ij) u_i^i v_j^j, u_i^i v_i^i, u_i^i v_i^i, g_ (ij)^(ij) u_i^i v_j^j, g_ (ij)^(ij) u_i^i v_j^j}

Out[79]=

{7, 7, 7, 7, 7, 7, 7, 7}

4. A Second Order Tensor T Evaluated on Two Vectors
Working with Tensor Bases

Suppose we have a second order tensor T with the following components in the standard Cartesian frame.

In[80]:=

Tcomponents = ({{0, 2, 3}, {2, -1, 2}, {5, 0, -4}}) ;

SetTensorValueRules[Tuu[i, j], Tcomponents]

TensorValueRules[T]

Out[82]=

Let's evaluate this tensor operating on the u and v vectors defined in Section 3. We can evaluate T on the two vectors by two methods. So far, we have only the up components of T but all components of the metric g and the vectors u and v.

In[83]:=

{Tuu[i, j] ud[i] vd[j], gdd[i, m] gdd[j, n] Tuu[m, n] uu[i] vu[j]}

ToArrayValues[]/@%

Out[83]=

{T_ (ij)^(ij) u_i^i v_j^j, g_ (im)^(im) g_ (jn)^(jn) T_ (mn)^(mn) u_i^i v_j^j}

Out[84]=

{2, 2}

Let's be more formal and write out T explicitly in terms of the second order basis tensors, which are...

In[85]:=

GenerateBasisTensors[g, "dd"]

Out[85]=

{g_1^1⊗g_1^1, g_1^1⊗g_2^2, g_1^1⊗g_3^3, g_2^2⊗g_1^1, g_2^2⊗g_2^2, g_2^2⊗g_3^3, g_3^3⊗g_1^1, g_3^3⊗g_2^2, g_3^3⊗g_3^3}

The multiplication symbol is CircleTimes. It can be entered [ESC]c*[ESC] or by typing "\[ " followed by "CircleTimes]". I have it on a palette. It is also on the General Operators subpalette in the CompleteCharacters palette. This type of expression is sometimes called a dyad.

So the tensor can be represented as...

In[86]:=

T

%/.T→Tuu[i, j] gd[i] ⊗gd[j]

%//EinsteinSum[]

%/.TensorValueRules[T]

Out[86]=

T

Out[87]=

g_i^i⊗g_j^j T_ (ij)^(ij)

Out[88]=

Out[89]=

2 g_1^1⊗g_2^2 + 3 g_1^1⊗g_3^3 + 2 g_2^2⊗g_1^1 - g_2^2⊗g_2^2 + 2 g_2^2⊗g_3^3 + 5 g_3^3⊗g_1^1 - 4 g_3^3⊗g_3^3

We call this the basis form of a tensor.

The following cell evaluates T on u and v using the full basis of the tensor.

In[90]:=

Print["General expression of a tensor evaluated on vectors."] ;

T[u, v]

Print["Substituting the indexed expressions for the tensor and vectors."] ;

%%/.{T→Tuu[i, j] gd[i] ⊗gd[j], u→uu[m] gd[m], v→vu[n] gd[n]}

Print["Pushing the arguments onto the individual dyads."] ;

%%//PushOnto[{CircleTimes[__]}]

Print["Evaluating each vector on the corresponding slot of the dyad."] ;

%%/.CircleEvalRule

Print["Factoring out the vector components from the dot products."] ;

%%//LinearBreakout[Dot][gd[_]]

Print["Converting dot products of basis vectors to metric components."] ;

%%/.BasisDotProductRules[g, g]

Print["Expanding and evaluating."] ;

%%//ToArrayValues[]

General expression of a tensor evaluated on vectors.

Out[91]=

T[u, v]

Substituting the indexed expressions for the tensor and vectors.

Out[93]=

(g_i^i⊗g_j^j T_ (ij)^(ij))[g_m^m u_m^m, g_n^n v_n^n]

Pushing the arguments onto the individual dyads.

Out[95]=

T_ (ij)^(ij) (g_i^i⊗g_j^j)[g_m^m u_m^m, g_n^n v_n^n]

Evaluating each vector on the corresponding slot of the dyad.

Out[97]=

g_i^i . (g_m^m u_m^m) g_j^j . (g_n^n v_n^n) T_ (ij)^(ij)

Factoring out the vector components from the dot products.

Out[99]=

g_i^i . g_m^m g_j^j . g_n^n T_ (ij)^(ij) u_m^m v_n^n

Converting dot products of basis vectors to metric components.

Out[101]=

g_ (im)^(im) g_ (jn)^(jn) T_ (ij)^(ij) u_m^m v_n^n

Expanding and evaluating.

Out[103]=

2

Providing we have the down components of u and v we could also evaluate in one less step using EvaluateDotProducts.

In[104]:=

Print["General expression of a tensor evaluated on vectors."] ;

T[u, v]

Print["Substituting the indexed expressions for the tensor and vectors."] ;

%%/.{T→Tuu[i, j] gd[i] ⊗gd[j], u→uu[m] gd[m], v→vu[n] gd[n]}

Print["Pushing the arguments onto the individual dyads."] ;

%%//PushOnto[{CircleTimes[__]}]

Print["Evaluating each vector on the corresponding slot of the dyad."] ;

%%/.CircleEvalRule

Print["Evaluating the the dot products."] ;

%%//EvaluateDotProducts[g, g]

Print["Expanding and evaluating."] ;

%%//ToArrayValues[]

General expression of a tensor evaluated on vectors.

Out[105]=

T[u, v]

Substituting the indexed expressions for the tensor and vectors.

Out[107]=

(g_i^i⊗g_j^j T_ (ij)^(ij))[g_m^m u_m^m, g_n^n v_n^n]

Pushing the arguments onto the individual dyads.

Out[109]=

T_ (ij)^(ij) (g_i^i⊗g_j^j)[g_m^m u_m^m, g_n^n v_n^n]

Evaluating each vector on the corresponding slot of the dyad.

Out[111]=

g_i^i . (g_m^m u_m^m) g_j^j . (g_n^n v_n^n) T_ (ij)^(ij)

Evaluating the the dot products.

Out[113]=

T_ (ij)^(ij) u_i^i v_j^j

Expanding and evaluating.

Out[115]=

2

With Tensorial, we can concentrate on the basic definitions and operations. All the tedious calculations, plug and chug as in the last step above, can be relegated to the computer.

5. T Components in Oblique Coordinates

Using Basic Definitions

Now, let's calculate and store the components of T in oblique coordinates. We start with the standard definition for calculating the components.

In[116]:=

ClearTensorValues[Tuu[i, j]//ToFlavor[red]] ;

Print["Definition of components."] ;

Tuu[i, j] == T[gu[i], gu[j]]//ToFlavor[red]

Print["Substitute tensor in Cartesian basis."] ;

%%/.T[a_, b_] →Tuu[m, n] (gd[m] ⊗gd[n])[a, b]

Print["Evaluate on the slots."] ;

%%/.CircleEvalRule

Print["Since all the quantities on the rhs are known, expand to array values."] ;

MatrixForm/@(step1 = ToArrayValues[]/@%%)

SetTensorValueRules[Tuu[i, j]//ToFlavor[red], Last[step1]]

TensorValueRules[T]

Definition of components.

Out[118]=

T_ (ij)^(ij) == T[g_i^i, g_j^j]

Substitute tensor in Cartesian basis.

Out[120]=

T_ (ij)^(ij) == T_ (mn)^(mn) (g_m^m⊗g_n^n)[g_i^i, g_j^j]

Evaluate on the slots.

Out[122]=

T_ (ij)^(ij) == g_m^m . g_i^i g_n^n . g_j^j T_ (mn)^(mn)

Since all the quantities on the rhs are known, expand to array values.

Out[124]=

Out[126]=

Since we have previously calculated the components of u, v and g in the red frame, we can now evaluate T[u,v] completely in the red frame. We should obtain the same answer as in the black frame.

In[127]:=

gdd[i, m] gdd[j, n] Tuu[m, n] uu[i] vu[j]//ToFlavor[red]

%//ToArrayValues[]

Out[127]=

g_ (im)^(im) g_ (jn)^(jn) T_ (mn)^(mn) u_i^i v_j^j

Out[128]=

2

We could calculate the red frame components of T for other index configurations by the same method as above. Just put the desired combination or red basis or reciprocal basis vectors in the slots. But, since we have one set of components in the red frame, we can calculate the other index configurations simply by lowering indices with the metric tensor. For example...

In[129]:=

Tud[i, j] == gdd[j, m] Tuu[i, m]//ToFlavor[red]

MatrixForm/@ToArrayValues[]/@%

Out[129]=

T_ (ij)^(ij) == g_ (jm)^(jm) T_ (im)^(im)

Out[130]=

As Linear Transformations

There is another and perhaps more conventional method for calculating the components in the red frame. In Section 1, we simply gave the red basis vectors. But instead, we could have considered them to have been obtained as a linear transformation of the natural Cartesian basis vectors. We can represent this linear transformation by...

In[131]:=

Aud[i, red @ j]

Out[131]=

A_ (ij)^(ij)

We will always write the transformation matrices with the indices going northwest to southeast, that is, in the up-down configuration, Aud. The two indices correspond to different coordinate systems. The indices will always have different flavors. It turns out that the components of Aud will be given by the transpose of the GBasis matrix of Section 1, so let's set them and check them.

In[132]:=

SetTensorValueRules[Aud[i, red @ j], Transpose[GBasis]]

TensorValueRules[A]

Out[133]=

In[134]:=

gd[red @ i] == Aud[k, red @ i] gd[k]

%//ToArrayValues[]

Out[134]=

g_i^i == A_ (ki)^(ki) g_k^k

Out[135]=

{True, True, True}

To write the transformation going from one frame to another, we put in an A matrix for each index to be transformed and line up the colors. We have to be careful how the A tensor is initially defined. As a matrix equation we would obtain the obliqe basis by GBasis.(Euclidean basis vectors). But if we look at the first equation above, we really have the transpose when g is written on the right. The k summation indicies are not adjacent. This tells us that A must be the transpose of GBasis.

To obtain the transformation going the other way we need the inverse of A. This corresponds to simply switching the colors of the first and second slots of A. But we must actually calculate and set the values.

In[136]:=

SetTensorValueRules[Aud[red @ i, j], Inverse[Transpose[GBasis]]]

TensorValueRules[A]

Out[137]=

Here is the inverse transformation...

In[138]:=

gd[i] == Aud[red @ k, i] gd[red @ k]

%//ToArrayValues[]

Out[138]=

g_i^i == A_ (ki)^(ki) g_k^k

Out[139]=

{True, True, True}

We will generally need both forms of the transformation matrix. One form will be used to transform down indices and the other form will be used to transform up indices. Let's see how we use them to transform the T matrix from the black to the red frame. Just line up the colors of the indices, with summations on the "old" components. This checks with our previous calculation.

In[140]:=

Tuu[red @ i, red @ j] == Aud[red @ i, m] Aud[red @ j, n] Tuu[m, n]

%//ToArrayValues[]

Out[140]=

T_ (ij)^(ij) == A_ (im)^(im) A_ (jn)^(jn) T_ (mn)^(mn)

Out[141]=

{{True, True, True}, {True, True, True}, {True, True, True}}

We can then obtain the other index configurations by lowering indices.

Previously we calculated the up and down components of u in the red frame.

In[142]:=

TensorValueRules[u]

Out[142]=

{u_1^1→2, u_2^2→ -1, u_3^3→3, u_1^1→2, u_2^2→ -1, u_3^3→3, u_1^1→ -1, u_2^2→0, u_3^3→3, u_1^1→1, u_2^2→5, u_3^3→5}

Let's calculate the down components of u in the black frame starting from the red frame.

In[143]:=

ud[i] == Aud[red @ k, i] ud[red @ k]

%//ToArrayValues[]

Out[143]=

u_i^i == A_ (ki)^(ki) u_k^k

Out[144]=

{True, True, True}

Since the black frame has the natural Cartesian basis vectors, the down components should be the same as the up components. The following is not a proper tensor equation and if we try to use ToArrayValues on the entire equation we will obtain error messages because the free indices do not agree. But, if we map ToArrayValues to each side of the equation, the calculation will work.

In[145]:=

uu[i] == Aud[red @ k, i] ud[red @ k]

ToArrayValues[]/@%

Out[145]=

u_i^i == A_ (ki)^(ki) u_k^k

Out[146]=

True

Eliminate the index parsing rule

In[147]:=

IndexParsingRules = {} ;


Created by Mathematica  (November 22, 2007) Valid XHTML 1.1!