\documentstyle{article} \begin{document} \title{{\it SetTensor}, a package for {\it MathTensor}} \author{S. Brandt} \maketitle \section{Introduction} {} {\it MathTensor} provides a series of powerful tools for evaluating tensor objects. These functions must be implemented in a specific sequence of steps if the desired output is to be obtained. First you must apply transformation rules, make sure derivative indices are lowered, evaluate covariant derivatives (the function {\tt CDtoOD} may need to be called more than once to accomplish this and there is no builtin automatic way to repeat it), make sums, assign index values, and last of all to evaluate ordinary derivatives. For optimization purposes it is useful to have some quantities (like affine connections) pre-calculated. All this can be a bit of a nuissance to write out. The {\it SetTensor} package is designed to make this easier. Some of the functionality of {\it SetTensor} can be obtained with the package {\it Components}, but there are a few advantages to using {\it SetTensor}. {\it Components} requires you to edit a template file to define the metric in the appropriate way, set flags, etc. No such file is needed with {\it SetTensor}. Also {\it Components} may calculate some quantities that you do not really need. You may only need the lower, and not the upper, indexed values of the Riemann tensor. With {\it SetTensor} you will only calculate the quantities that you need. {\it Components} does not allow you to change the size of your metric and the value of your metric components during a session, {\it SetTensor} does. {\it SetTensor} adds new symbols to the environment, some of these are as follows: {\tt EvalMT}, {\tt Iter}, {\tt SetSqrtDetg}, and {\tt InitializeMetric}. In order to use these commands you need to first define some basic objects, most of which are standard to {\it MathTensor}. These standard objects are {\tt Dimension} and the variables {\tt x[1]}, {\tt x[2]}, ... {\tt x[Dimension]}. You may also want to define the value of {\tt Sqrt[Abs[Detg]]}. Your next step is to call {\tt InitializeMetric}. {\tt InitializeMetric} can be called in one of four ways: \begin{verbatim} MetricDown = { ... }; MetricUp = { ... } (* will call Inverse[] to make MetricUp *) InitializeMetric[MetricDown]; (* will call Inverse[] to make MetricUp, then apply MySimp *) (* to simplify the resulting matrix and affine connections. *) InitializeMetric[MetricDown,MySimp]; (* will simply accept the user's definition of MetricUp *) InitializeMetric[MetricDown,MetricUp]; (* will simply accept the user's definition of MetricUp *) (* and apply MySimp to the affine connection terms *) InitializeMetric[MetricDown,MetricUp,MySimp]; \end{verbatim} Where the {\tt MetricUp} and {\tt MetricDown} are {\tt Dimension}$\times${\tt Dimension} matrices. The last two of the methods above are espcially important. It may be that you are using a full 3-metric and do not wish to have to deal with the true metric inverse. You might prefer to deal with the metric in this form: \begin{verbatim} MetricDown = { {g11[x,y,z],g12[x,y,z],g13[x,y,z]}, {g21[x,y,z],g22[x,y,z],g23[x,y,z]}, {g31[x,y,z],g32[x,y,z],g33[x,y,z]} }; MetricUp = { {gu11[x,y,z],gu12[x,y,z],gu13[x,y,z]}, {gu21[x,y,z],gu22[x,y,z],gu23[x,y,z]}, {gu31[x,y,z],gu32[x,y,z],gu33[x,y,z]} }; \end{verbatim} This form for {\tt MetricUp} will be much easier for Mathematica to handle than {\tt Inverse[MetricDown]}. For added convenience, a function {\tt SubFun} is added to this package which can be used to replace a function (and all its derivatives) with a new form. The usage is illustrated by this example: \begin{verbatim} In[]= SubFun[f'[x]+f[x],f[x],x^2] Out[]= 2 x+x^2 \end{verbatim} \section{The {\tt EvalMT} command} Once these basic steps outlined above are accomplished, it is a straightforward matter to calculate the components of almost any tensor, or scalar object within the {\it MathTensor} framework. For a scalar, you need merely to issue the {\tt EvalMT} command. A complete example follows: \begin{verbatim} <-1,lj->-1}] \end{verbatim} Note that because only the affine connections are defined, it is necessary for you to provide the explicit call to the {\it MathTensor} builtin command {\tt RicciToAffine}. In evaluating tensors and components it is useful to remember the following additional {\it MathTensor} commands: {\tt ScalarRtoAffine}, and {\tt RiemannToAffine}. %\section{The First Form of {\tt SetTensor}} %{\it Mathematica} provides a command {\tt SetComponents} to assign %components to a tensor the user defines, or one of the standard %builtin tensors. Unfortunately, {\tt SetComponents} cannot be %made to evaluate the ordinary derivatives of its arguments or %to simplify them before assignment with any kind of ease. This %is one of the motivations of {\tt SetTensor}. {\tt SetComponents} %also does not recognize when antisymmetric indices make a component %zero. {\tt SetTensor} does this automatically, thereby avoiding %the needless calculation and simplification that may be necessary %for {\it Mathematica} to realize that a component is zero. %If you only want to know one component of the Ricci tensor, the %above example is fine. To evaluate them all, the {\it SetTensor} %package %provides the function {\tt SetTensor} which calls both the builtin %{\tt SetComponents} and the new routine {\tt EvalMT}. %It also allows for a means of post-processing the output of {\tt EvalMT} %with a command such as {\tt Simplify}. %You can use %{\tt SetTensor} to calculate all the lower indexed %Ricci components as follows: %\begin{verbatim} %SetTensor[RicciR[la,lb],RicciToAffine[RicciR[la,lb]] ]; %\end{verbatim} %This may take a while if you are using a higher dimensional or %more complicated matrix. If you want to know what's happening, %you can turn on the {\tt EvalMT::PrintIndex} flag using the {\tt %On[]} command provided by Mathematica (i.e. you can simply type %{\tt On[EvalMT::PrintIndex]}). If you do this it will %print out the indices of the tensor component being evaluated, as %each component is evaluated. %It may be that you wish to only assign simplified values to the %Ricci tensor. The last argument to {\tt SetTensor} is understood %to be a post-processing function, such as {\tt Simplify}. %\begin{verbatim} %SetTensor[RicciR[la,lb],RicciToAffine[RicciR[la,lb]],Simplify]; %\end{verbatim} %However, it is my recommendation that you do not directly assign %to any of {\it MathTensor}'s builtin objects. Instead, I recommend that %you enter this command: %\begin{verbatim} %sym=Symmetries[RicciR[la,lb]]; %DefineTensor[RicciRVal,"Rv",sym]; %SetTensor[RicciRVal[la,lb],RicciToAffine[RicciR[la,lb]],Simplify]; %\end{verbatim} %I recommend this because you can clear {\tt RicciRVal} from your %session and redefine it, something you cannot do to {\tt RicciR}. %Moreover, you have lost nothing in the process. You can use %{\tt RicciRVal} as follows to evaluate {\tt SclarR}. %\begin{verbatim} %EvalMT[RicciRVal[la,lb] Metricg[ua,ub]] %\end{verbatim} %In addition you now have the %option to look at the same quantity {\em without} substituting %for {\tt RicciR}. %\begin{verbatim} %tmp = EvalMT[RicciR[la,lb] Metricg[ua,ub]] %\end{verbatim} %It is completely legitimate to evalute {\tt RicciR} in this %expression simply by doing this: %\begin{verbatim} %tmp /. RicciR->RicciRVal %\end{verbatim} %This is actually how {\it SetTensor} %stores and evaluates the components for the metric %and affine connections. %That is, they are stored in {\tt MetricgVal} and %{\tt AffineGVal} respectively and {\tt EvalMT} substitues them. %You can access them directly if you %want, for example, to know the components of the affine connection. %Because {\it SetTensor} does nothing to the objects {\tt Metricg} and %{\tt AffineG} you can call %{\tt InitializeMetric} with different values during the same session %and have the subsequent evaluations behave properly. %The {\tt SetTensor} command is actually used internally by {\tt %InitializeMetric} to set the values of {\tt AffineGVal}. %\section{The Second Form of {\tt SetTensor}} %Another feature of the {\tt SetTensor} command is that it can be %used to assign arrays to tensor objects. For example: %\begin{verbatim} %DefineTensor[Shift,"b",{{1},1}]; %SetTensor[Shift[ua],{b1,b2,b3}]; %\end{verbatim} %Assigns the array on the right hand side to the upper indexed tensor %components on the left. This mechanism is actually used internally %by {\tt InitializeMetric} to assign the components to {\tt MetricgVal}. %This summarizes the basic features of this package. \section{\tt SetSqrtDetg} In calculations involving the Levi-Civita symbol, {\tt Epsilon}, one obtains (from {\it MathTensor} expressions like {\tt Sqrt[Abs[Detg]]} or {\tt Sqrt[Abs[Detg]]/Detg}. The {\tt SetTensor} package provides a function called {\tt SetSqrtDetg[]} which takes, as an argument, a function which is assumed to be the absolute value of the square root of the determinant. {\tt SetSqrtDetg[sign,val]} takes two arguments. The term {\tt sign} must be a number with a value equal to either {\tt 1} or {\tt -1}. {\tt SetSqrtDetg[sign,val]} uses {\tt val} to represent {\tt Sqrt[Abs[Detg]]}, and {\tt sign*val${}^2$} to represent {\tt Detg} when it does not appear otherwise. \section{Iter[Tensor Object,Code]} {\tt Iter} will loop over the {\em unassigned} indices of a tensor object, taking into account the objects symmetries. The index variables applied to the tensor object are filled in for the {\it Code} section to use. For example: \begin{verbatim} DefineTensor[foo,{{2,1},1}]; Iter[foo[la,lb], Print[la," and ",lb]; ]; \end{verbatim} Produces the following output: \begin{verbatim} PermWeight::sym: Symmetries of foo assigned PermWeight::def: Object foo defined -3 and -3 -3 and -2 -3 and -1 -2 and -2 -2 and -1 -1 and -1 \end{verbatim} If we wish {\tt Iter} to avoid one or more of these indices we can use {\tt Set1Component} which, as its name implies, sets one component. Running the code \begin{verbatim} Set1Component[foo[-1,-2],0]; Iter[foo[la,lb], Print[la," and ",lb]; ]; \end{verbatim} produces \begin{verbatim} -3 and -3 -3 and -2 -3 and -1 -2 and -2 -1 and -1 \end{verbatim} Note that if a {\tt foo} had been anti-symmetric in its two indices, then {\tt foo[-1,-1]} would automatically get the value zero and {\tt Iter} would not loop over that element. For example: \begin{verbatim} DefineTensor[foo,{{2,1},-1}]; Iter[foo[la,lb], Print[la," and ",lb]; ]; \end{verbatim} Produces the following output: \begin{verbatim} PermWeight::sym: Symmetries of foo assigned PermWeight::def: Object foo defined -3 and -2 -3 and -1 -2 and -1 \end{verbatim} \subsection{Assigning to Tensor Objects} You can use Iter to fill in the components of a tensor object. For example: \begin{verbatim} DefineTensor[foo,{{1},1}] Iter[foo[la], Set1Component[ foo[la],{f1[x],f2[x],f3[x]}[[-la]] ] ] \end{verbatim} will assign the values {\tt f1[x]}, etc. to the components of the tensor object {\tt foo}. Note that {\tt la} takes on negative values inside the {\tt Iter} loop, so when the array is indexed we used a negative sign. You can also combine this with {\tt EvalMT} and evaluate the simplified components of the {\tt Ricci} tensor. \begin{verbatim} DefineTensor[ric,{{2,1},1}]; Iter[ric[la,lb], Set1Component[ ric[la,lb],Simplify[ EvalMT[RicciToAffine[RicciR[lc,ld]],{lc->la,ld->lb}] ] ] ] \end{verbatim} \section{SetTensor} The package's namesake, the function {\tt SetTensor}, provides a shorthand for calling {\tt Iter}. The following examples illustrate how it works. \begin{verbatim} SetTensor[foo[la,ub],{ {f1,f2,f3}, {f4,f5,f6}, {f7,f8,f9}}] \end{verbatim} is the equivalent of \begin{verbatim} Iter[foo[la,ub], Set1Component[foo[la,ub],{ {f1,f2,f3}, {f4,f5,f6}, {f7,f8,f9}}[[-la,ub]] ] ] \end{verbatim} Notice that lower indices automatically get minus signs, but upper indices don't. \begin{verbatim} SetTensor[ric[la,lb],RicciToAffine[RicciR[la,lb]]] \end{verbatim} is equivalent to \begin{verbatim} DefineTensor[ric,{{2,1},1}]; Iter[ric[la,lb], Set1Component[ ric[la,lb], EvalMT[RicciToAffine[RicciR[lc,ld]],{lc->la,ld->lb}] ] ] \end{verbatim} \section{Naming Components of Arrays} It may be the case that, for the code you are writing, you wish to name all the components of an array in a similar way. Rather than typing out lengthy arrays, {\it SetTensor} provides a bit of shorthand for you. \subsection{AddDeps[Object], DefaultDepString} This is a very simple function which adds a dependency list to a string, then turns it into an expression. For example: \begin{verbatim} x[1] = x; x[2] = y; x[3] = z; Dimension = 3; AddDeps["psi"] \end{verbatim} produces \begin{verbatim} Out[]= psi[x,y,z] \end{verbatim} The utility of this function will become more apparent when it is combined with objects to be described in the next sections. If you do not want the dependency list generated automatically from {\tt x[1]}, {\tt x[2]}, and {\tt x[3]}, you can over-ride this by setting {\tt DefaultDepString}. Continuing the above example we find that \begin{verbatim} DefaultDepString="x,y"; AddDeps["psi"] \end{verbatim} produces \begin{verbatim} Out[]= psi[x,y]; \end{verbatim}. \subsection{AddIndex[Object]} {\tt AddIndex} adds an index. To create a single vector named ``U'' you would do the following: \begin{verbatim} x[1] = x; x[2] = y; x[3] = z; Dimension = 3; AddIndex["U"]; \end{verbatim} And this would produce the output \begin{verbatim} Out[]= {"Ux","Uy","Uz"} \end{verbatim} If we apply {\tt AddDeps} to this we obtain \begin{verbatim} Out[]= {Ux[x, y, z], Uy[x, y, z], Uz[x, y, z]} \end{verbatim} There is an optional argument to AddIndex, and its value can be either {\tt Prepend} or {\tt Append}. The latter is its default value. However, using the former value we obtain: \begin{verbatim} In[]= AddDeps[AddIndex[Prepend,"U"]] Out[]= {xU[x, y, z], yU[x, y, z], zU[x, y, z]} \end{verbatim} You can also apply {\tt AddIndex} to itself. \begin{verbatim} In[]= AddDeps[AddIndex[AddIndex["U"]]] Out[]= {{Uxx[x, y, z], Uxy[x, y, z], Uxz[x, y, z]}, {Uyx[x, y, z], Uyy[x, y, z], Uyz[x, y, z]}, {Uzx[x, y, z], Uzy[x, y, z], Uzz[x, y, z]}} \end{verbatim} \subsection{AddSym2[Object], AddAsym2} The last example above showed how to create a 2 index tensor. What if we want that tensor to be symmetric? We need not really do anything, as the symmetries of the tensor cannot be violated by the assignment process using {\tt SetTensor}. However, we might wind up with \begin{verbatim} In[]= foo[-2,-1] Out[]= fooyx[x, y, z] \end{verbatim} when we had wanted \begin{verbatim} Out[]= fooyx[x, y, z] \end{verbatim} if we were not careful. For this purpose, and to provide a shorthand for adding 2 indices at once, we provide {\it AddSym2}. \begin{verbatim} In[]= AddDeps[AddSym2["U"]] Out[]= {{Uxx[x, y, z], Uxy[x, y, z], Uxz[x, y, z]}, {Uxy[x, y, z], Uyy[x, y, z], Uyz[x, y, z]}, {Uxz[x, y, z], Uyz[x, y, z], Uzz[x, y, z]}} \end{verbatim} {\it AddSym2} also takes the optional first argument which may be set to {\tt Prepend} with effects similar to that for {\tt AddIndex}. {\it AddAsym2} works the same way as {\it AddSym}, but obviously produces an anti-symmetric rather than a symmetric matrix. \begin{verbatim} In[]= AddDeps[AddAsym2["U"]] Out[]= {{0, Uxy[x, y, z], Uxz[x, y, z]}, {-Uxy[x, y, z], 0, Uyz[x, y, z]}, {-Uxz[x, y, z], -Uyz[x, y, z], 0}} \end{verbatim} As a last example, let us make a matrix that has three indices, but is symmetric in the last two indices. The declaration looks like this: \begin{verbatim} tmp=AddIndex[AddSym2["U"]] \end{verbatim} For this array, {\tt tmp[[1,2,3]] == tmp[[1,3,2]]}. \section{Fortran Output} The package {\it Format}\footnote{For documentation, see http://www.wri.com/MathSource/.aliases/0205-254/Format.ps} with its function {\tt FortranAssign} function replaces the builtin {\it Mathematica} function {\tt FortranForm}. {\it Format} is automatically loaded by {\it SetTensor}. Note, however, that there is a namespace conflict between the two packages for the symbol {\tt Lower}. Please ignore the warning message this generates during loading. Several functions have been provided to interface between the package {\it Format} available from {\it MathSource} and {\it SetTensor}: {\tt DerivRule} provides a mechanism for making the derivatives of functions format in a reasonable way, and {\tt fwri} (Fortran write) simply calls {\tt Write[]}, {\tt DerivRule}, and {\tt FortranAssign} with some specific default options. \subsection{DerivRule, MakeDeriv[String,String,String]} By default, {\tt DerivRule} will convert a derivative to a function. Here are some examples to show you how it works: \begin{verbatim} In[]= D[foo[n,x],n] /. DerivRule Out[]= dnfoo[n, x] In[]= D[foo[n,x],x] /. DerivRule Out[]= dxfoo[n, x] In[]= D[foo[n,x],x,n] /. DerivRule Out[]= dnxfoo[n, x] In[]= D[foo[n,x],x,x] /. DerivRule Out[]= dxxfoo[n, x] \end{verbatim} The format of the derivative function is specified by {\tt MakeDeriv}. MakeDeriv is called by {\tt DerivRule} with three {\tt String} arguments. The first specifies the variables we are using to take the derivatives, i.e. in the above examples we get ``n'', ``x'', ``nx'', and ``xx'' respectively. The second argument specifies the name of the function, in the above example that would be ``foo'' in every case. The last argument is a string which supplies the function's variable dependence, ``[n,x]'' in every example above. The default definition of {\tt MakeDeriv} is, therefore: \begin{verbatim} MakeDeriv[wr2_,fun_,args_] := ToExpression["d"<>wr2<>fun<>args] \end{verbatim} You can over-ride this function to obtain the kind of expression you think is appropriate if the default is unacceptable. \subsection{fwri[Outputstream,lhs,rhs]} To use this function you need an {\tt Outputstream}, i.e. the output of the Mathematica function {\tt OpenWrite} for the first argument (for testing purposes you can use the string ``stdout'' to simply write to the screen). Next you supply the left-hand side and the right-hand side of the output you wish to produce. For example: \begin{verbatim} fwri["stdout",a,b] \end{verbatim} produces (because {\tt fwri} calls {\tt FortranAssign}) \begin{verbatim} a = b \end{verbatim} appropriately spaced for punchcards and thus for modern Fortran compilers. Note that {\tt fwri} automatically calles {\tt DerivRule}, and provides a few mappings. The character sequence ``UND'' is mapped to the underscore character. The default list of arguments is mapped to the null string, ``''. To understand this, consider for example the spacetime specified by \begin{verbatim} x[1] = x; x[2] = y; x[3] = z; Dimension = 3; \end{verbatim} The default argument list for it is ``(x,y,z)''. Now let us look at two slightly longer examples to see how all this works: \begin{verbatim} In[]= MakeDeriv[x1_,x2_,x3_] := ToExpression[ x2<>"UND"<>x1<>x3]; tmp=D[foo[n,x],x,x] /. DerivRule; fwri["stdout",a,tmp] Out[]= a = foo_xx(n,x) In[]= tmp=D[foo[x,y,z],x,x] /. DerivRule; fwri["stdout",a,tmp] Out[]= a = foo_xx \end{verbatim} In the second example we see that the argument list is suppressed. This is because it is the default list ``(x,y,z)'' given above. If you call {\tt fwri} with the {\it lhs} value set to the symbol {\tt call}, then the assignment symbol ``='' will be suppressed on output. Since {\tt FortranAssign} converts integers to reals, we provide the symbol {\tt FortranInt[x1\_Integer]} to allow integers to format literally. \begin{verbatim} In[]= fwri["stdout",call,func[FortranInt[3]] ]; Out[]= call func(3) \end{verbatim} If you are writing F90 code with array notation, this is what you want to have happen. However, it may be that you are writing F77 code and want ``(x,y,z)'' to map to ``(i,j,k)'' instead. If this is so, you can do the following: \begin{verbatim} In[]= FortranOutputOfDepList = "(i,j,k)"; tmp=D[foo[x,y,z],x,x] /. DerivRule; fwri["stdout",a,tmp] Out[]= a = foo_xx(i,j,k) \end{verbatim} Note that you can supply all the arguments to {\tt FortranAssign} that you might normally want to, however if you use {\tt AssignReplace} (the mechanism used to produce the above string mappings) you lose the string mapping definitions supplied by SetTensor. \begin{verbatim} In[]= FortranOutputOfDepList = "(i,j,k)"; tmp=D[foo[x,y,z],x,x] /. DerivRule; fwri["stdout",a,tmp,AssignRelplace->{"x"->"X"}]; Out[]= a = fooUNDXX(X,y,z) \end{verbatim} \subsection{AddReplace} This unfortunate state of affairs can be remedied by using the argument {\tt AddAssign} as follows: \begin{verbatim} In[]= FortranOutputOfDepList = "(i,j,k)"; tmp=D[foo[x,y,z],x,x] /. DerivRule; fwri["stdout",a,tmp,AddRelplace->{"x"->"X"}]; Out[]= a = foo_XX(X,y,z) \end{verbatim} \section{NeedsIt[],ClearNeedsIt[],AddToNeedsIt[]} Sometimes you may wish to only write out some components of a tensor. For example, suppose you want to calculate the Ricci scalar. \begin{verbatim} Needs["SetTensor`"]; Dimension=3; x[1] = x; x[2] = y; x[3] = z; DefaultDepString = "x,y"; gv = { {AddDeps["f1"],0,0}, {0,AddDeps["f2"],0}, {0,0,AddDeps["f3"]}}; InitializeMetric[gv,Simplify]; ClearNeedsIt[]; fd = OpenWrite["rscal.h"]; rhs = ScalarRtoAffine[ScalarR]; rhs = rhs /. AffineG->af rhs = EvalMT[rhs]; AddToNeedsIt[rhs]; fwri[fd,rsc,rhs]; Close[fd]; \end{verbatim} The function {\tt NeedsIt} now knows which derivatives of the Affine connection you need. You can use this info to calculate only those components. \begin{verbatim} (* derivAf - Derivative of Affine Connection *) DefineTensor[derivAf,{{1,3,2,4},1}]; (* the next two lines make the same affine symbol used above *) tmp = AddIndex["A"]; tmp = AddSym2[tmp]; (* the next two lines prepend a dx, dy, or dz to the * affine connection name. *) tmp = AddIndex[Prepend,tmp]; tmp = AddStrs["d",tmp]; tmp = AddDeps[tmp]; (* at this point tmp[[1,1,1,2]] yields dyAxxx[x,y] *) rhs = OD[AffineG[ue,lf,lg],lh]; rhs = AffineToMetric[rhs]; rhs = Tsimplify[rhs]; fd = OpenWrite["dAf.h"]; Iter[derivAf[ua,lb,lc,ld], (* NeedsIt only returns True if the derivative * symbol was found in rhs in the call to * AddToNeedsIt above. *) If[NeedsIt[ tmp[[ua,-lb,-lc,-ld]] ], fwri[fd,tmp[[ua,-lb,-lc,-ld]], EvalMT[rhs,{ue->ua,lf->lb,lg->lc,lh->ld}] ] ] ] Close[fd]; \end{verbatim} You can now include the files ``dAf.h'' and ``rscal.h'' in in your Fortran code, first ``dAf.h'' to set the Affine connection derivatives, then ``rscal.h'' which uses them. Note that {\tt AddToNeedsIt} applies {\tt DerivRule} to its argument and then detects the functions with the default argument list. In the opinion of {\tt AddToNeedsIt}, you only ``need'' functions that have the default argument list. (In this example, the default arguments are ``x,y''. This was explicitly set using {\tt DefaultDepString} above. If it had not been set explicitly, the default arguments would've been ``x,y,z''.) For reference, the file ``dAf.h'' contains the following: \begin{verbatim} dxAxzz = 5.d-1*dxf1*dxf3/f1**2 - 5.d-1*dxxf3/f1 dxAxyy = 5.d-1*dxf1*dxf2/f1**2 - 5.d-1*dxxf2/f1 dyAxxy = -5.d-1*dyf1**2/f1**2 + 5.d-1*dyyf1/f1 dyAyzz = 5.d-1*dyf2*dyf3/f2**2 - 5.d-1*dyyf3/f2 dxAyxy = -5.d-1*dxf2**2/f2**2 + 5.d-1*dxxf2/f2 dyAyxx = 5.d-1*dyf1*dyf2/f2**2 - 5.d-1*dyyf1/f2 dyAzyz = -5.d-1*dyf3**2/f3**2 + 5.d-1*dyyf3/f3 dxAzxz = -5.d-1*dxf3**2/f3**2 + 5.d-1*dxxf3/f3 \end{verbatim} and the file ``rscal.h'' contains: \begin{verbatim} t1 = -(Axxy*Ayxx/f1) + Axxx*Ayxy/f1 - Ayxy**2/f1 + Ayxx*Ayyy/f1 & - Axxz*Azxx/f1 + Ayyz*Azxx/f1 - 2.d0*Ayxz*Azxy/f1 + Axxx*Azxz/f1 & - Azxz**2/f1 + Ayxx*Azyz/f1 + Azxx*Azzz/f1 - dxAyxy/f1 - dxAzxz & /f1 + dyAyxx/f1 - Axxy**2/f2 + Axxx*Axyy/f2 - Axyy*Ayxy/f2 + Axx & y*Ayyy/f2 - 2.d0*Axyz*Azxy/f2 + Axyy*Azxz/f2 t2 = Axxz*Azyy/f2 - Ayyz*Azyy/f2 + Ayyy*Azyz/f2 - Azyz**2/f2 + A & zyy*Azzz/f2 + dxAxyy/f2 - dyAxxy/f2 - dyAzyz/f2 - Axxz**2/f3 + A & xxx*Axzz/f3 + Axzz*Ayxy/f3 - 2.d0*Axyz*Ayxz/f3 - Ayyz**2/f3 + Ax & xy*Ayzz/f3 + Ayyy*Ayzz/f3 - Axzz*Azxz/f3 - Ayzz*Azyz/f3 + Axxz*A & zzz/f3 + Ayyz*Azzz/f3 + dxAxzz/f3 + dyAyzz/f3 rsc = t1 + t2 \end{verbatim} \section{Optimization} It is always desirable to reduce the number of quantities you need to calculate, and the number of indices you need to contract. Some of this has been taken account for you, by assuming that you always want the affine connections. The command \begin{verbatim} EvalMT[RicciRVal[li,lj],AffineToMetric[RicciToAffine[RicciR[li,lj]]] ]; \end{verbatim} will take much longer to evaluate than \begin{verbatim} EvalMT[RicciRVal[li,lj],RicciToAffine[RicciR[li,lj]] ]; \end{verbatim} simply because the affine connections have already been calculated and one level of summation has been removed. If the affine connections have been simplified, this helps speed things up all the more. When calculating the Riemann invariants, for another example, it is much more efficient to evaluate this \begin{verbatim} sym=Symmetries[RiemannR[la,lb,lc,ld]]; DefineTensor[riem,"r",sym]; SetTensor[riem[ua,ub,lc,ld],RiemannToAffine[RiemannR[ua,ub,lc,ld]]]; EvalMT[riem[ua,ub,lc,ld] riem[uc,ud,la,lb]] \end{verbatim} than it is to do this \begin{verbatim} sym=Symmetries[RiemannR[la,lb,lc,ld]]; DefineTensor[riem,"r",sym]; SetTensor[riem[ua,ub,uc,ud],RiemannToAffine[RiemannR[ua,ub,uc,ud]]]; SetTensor[riem[la,lb,lc,ld],RiemannToAffine[RiemannR[la,lb,lc,ld]]]; EvalMT[riem[ua,ub,uc,ud] riem[lc,ld,la,lb]] \end{verbatim} It also helps to apply {\tt Simplify} to the components of {\tt RiemannR}. \section{Timings} \begin{verbatim} Needs["SetTensor`"]; (* the next file defines g00,g11,g22,g33,b3 *) <<../BasicKerr.m; Dimension = 4; (* q is theta, p is phi *) x[2] = r; x[3] = q; x[4] = p; x[1] = t; metdn = { {g00,0,0,b3}, {0,g11,0,0}, {0,0,g22,0}, {b3,0,0,g33}}; dtg = g00 g33-b3\^{}2; metup = { {g33/dtg,0,0,-b3/dtg}, {0,1/g11,0,0}, {0,0,1/g22,0}, {-b3/dtg,0,0,g00/dtg}}; On[EvalMT::PrintIndex]; sym=Symmetries[RiemannR[la,lb,lc,ld]; DefineTensor[riem,"r",sym]; Timing[ InitializeMetric[metdn,metup,Simplify]; SetTensor[riem[ua,ub,lc,ld], RiemannToAffine[RiemannR[ua,ub,lc,ld]],Simplify]; Print["EvalMT..."]; tmp=EvalMT[riem[ua,ub,lc,ld] riem[uc,ud,la,lb]]; stmp=Simplify[tmp]; ] \end{verbatim} The above calculation took 3341.04 seconds on jean-luc, an RS6000. Making the substitution $u = a\,\cos\theta$ to remove the trigonmetric functions reduced the time to 549.81 seconds. By substituting {\tt FactorSquareFree} for {\tt Simplify} the time was reduced to 285.84 seconds. Setting the variable $m$ to unity (which merely involves a rescaling of $r$ and $a$), because it reduces the number of variables the simplification routines need to deal with, further reduced the run time of this calculation to 251.05 seconds. \section{Acknowledgements, Etc.} Much credit is due to Peter Musgrave (actually, almost everything I say in the optimization section I learned from him). I also want to acknowledge helpful conversations with the people of Wolfram Research. Unfortunately, this package is still an order of magnitude {\em slower} than the equivalent packages in {\it grtensor} as composed by Peter. I cannot imagine how he does it. \end{document}