OGRe: an object-oriented general relativity package​
​by Barak Shoshany​
Documentation for v1.7.0 (2021-09-27)
GitHub repository: https://github.com/bshoshany/OGRe​
Copyright (c) 2021 Barak Shoshany. Licensed under the MIT license.

Introduction

Summary

OGRe is a modern Mathematica package for tensor calculus, designed to be both powerful and user-friendly. It can be used in a variety of contexts where tensor calculations are needed, in both mathematics and physics, but it is especially suitable for general relativity.
Tensors are abstract objects, which can be represented as multi-dimensional arrays once a choice of index configuration and coordinate system is made. OGRe stays true to this definition, but takes away the complexities that come with combining tensors in different representations. This is done using an object-oriented programming approach, as detailed below.
The user initially defines each tensor in OGRe using its explicit components in any single representation. Operations on this tensor are then done abstractly, without needing to specify which representation to use. Possible operations include addition of tensors, multiplication of tensor by scalar, trace, contraction, and partial and covariant derivatives.
OGRe will automatically choose which representation to use for each tensor based on how the tensors are combined. For example, if two tensors are added, then OGRe will automatically use the same index configuration for both. Similarly, if two tensors are contracted, then OGRe will automatically ensure that the contracted indices are one upper (contravariant) and one lower (covariant). OGRe will also automatically transform all tensors being operated on to the same coordinate system.
Transformations between representations are done behind the scenes; all the user has to do is specify which metric to use for raising and lowering indices, and how to transform between the coordinate systems being used. This also means that there is no room for user error. The user cannot mistakenly perform “illegal” operations such as
2
μν
A
+
B
μλ
C
λν
. Instead, the user simply inputs the names of the tensors, the order (but not the configuration) of indices for each, and the operations to perform - and the correct combination
2
μν
A
μ
B
λ
λν
C
will be automatically deduced.
I initially created OGRe for use in my own research, so I made it as flexible and powerful as possible. I also wanted my students to be able to use it easily and efficiently, even if they only have minimal experience with Mathematica and/or general relativity, so I made it simple to learn and easy to use. As a result, this package is equally suitable for both experienced and novice researchers.

Features

◼
  • Define coordinate systems and the transformation rules between them. Tensor components are then transformed automatically between coordinates behind the scenes as needed.
  • ◼
  • Each tensor is associated with a specific metric. Tensor components are then transformed automatically between different index configurations, raising and lowering indices behind the scenes as needed.
  • ◼
  • Display any tensor in any index configuration and coordinate system, either in vector/matrix form or as a list of all unique non-zero elements. Metrics can also be displayed as a line element.
  • ◼
  • Automatically simplify tensor components, optionally with user-defined simplification assumptions. Simplifications can be parallelized for a significant performance boost. The user may utilize the package's simplification algorithm for any Mathematica expression, not just tensors.
  • ◼
  • Export tensors to a Mathematica notebook or to a file, so they can later be imported into another Mathematica session without having to redefine them from scratch.
  • ◼
  • Highly customizable. User settings are exported and imported along with the tensors. Some settings are persistent between sessions.
  • ◼
  • Easily calculate arbitrary tensor formulas using any combination of addition, multiplication by scalar, trace, contraction, partial derivative, and covariant derivative.
  • ◼
  • Built-in modules for calculating the Christoffel symbols (Levi-Civita connection), Riemann tensor, Ricci tensor and scalar, Einstein tensor, curve Lagrangian, and volume element from a metric.
  • ◼
  • Calculate the norm-squared of tensors of any rank.
  • ◼
  • Calculate the geodesic equations in terms of a user-defined affine curve parameter, in two different ways: from the Christoffel symbols or from the curve Lagrangian. For spacetime metrics, the geodesic equations can be calculated in terms of the time coordinate.
  • ◼
  • Designed with speed and performance in mind, using optimized algorithms designed specifically for this package.
  • ◼
  • Fully portable. Can be imported directly from the web into any Mathematica notebook, without downloading or installing anything. Integrates seamlessly with the Wolfram Cloud.
  • ◼
  • Clear and detailed documentation, with many examples, in both [Mathematica notebook](OGRe_Documentation.nb) and [PDF](OGRe_Documentation.pdf) format. Detailed usage messages are also provided.
  • ◼
  • Open source. The code is extensively documented; please feel free to fork and modify it as you see fit.
  • ◼
  • Under continuous and active development. Bug reports and feature requests are welcome, and should be made via GitHub issues.
  • The object-oriented design philosophy

    Object-oriented programming refers to a paradigm where a program’s code is organized around objects. An object belongs to a user-defined type, called a class. The class defines the data that the object stores, as well as methods or member functions that read or manipulate that data. One of the fundamental principles of object-oriented programming is encapsulation, which means that the user may only access an object’s data using the methods defined by the class, and is unable to access the object’s data directly.
    Importantly, encapsulation allows for the preservation of class invariants. An invariant is a condition of validity that can always be assumed to be satisfied by the data stored in each object. If the methods make sure to preserve the invariant whenever they store or manipulate the data, and the user is prevented from changing the data manually and thus potentially violating the invariant, then the implementation of the class can be greatly simplified, and performance can be improved, because we will not need to verify that the data is valid every time we perform an operation.
    The main idea behind OGRe is to simplify the use of tensors by encoding all the information about a tensor in a single, self-contained object. As we mentioned above, a tensor is an abstract object. We can find components which represent this abstract entity in a particular coordinate system and index configuration, but the tensor is not its components. In OGRe, a tensor object is initially defined (or constructed) by providing the components of the tensor in a particular representation - but once this is done, the user does not need to worry about coordinates or indices anymore, or even remember which coordinates and indices were initially used. The abstract tensor object will automatically transform the initial data to a different coordinate system or index configuration as needed, based on the context in which it was used.
    As a tensor object holds the components of the same tensor in many different representations, the most important class invariant is the assumption that the different components indeed represent the same tensor. This is achieved using encapsulation; the object’s data can only be modified by private methods that preserve the invariant, and thus the user cannot accidentally cause a violation of the invariant by assigning components to one representation that are not related to the components of all other representations by the appropriate coordinate and/or index transformation.
    Unfortunately, Mathematica does not have built-in support for object-oriented programming. However, version 10.0 of Mathematica, released in 2014, introduced the Association symbol. An Association is an associative array; it is similar to a List, except that instead of being just an array of values, an Association is a list of keys with a value associated to each key. This allows us to easily implement a rudimentary form of object-oriented programming, storing the properties of each object in the keys of a corresponding Association.
    Of course, as Mathematica is not truly object-oriented, there is no actual “tensor class” defined anywhere in the package. Instead, the tensor class exists only implicitly, as a design paradigm. Furthermore, the functions that process the data stored in the tensor objects are not methods of a class, they are simply modules that take tensor objects as input and/or produce tensor objects as outputs. (In earlier versions, I tried using a syntax that resembled method syntax in languages such as C++ or Python, but eventually decided against it, as it was too cumbersome.) Still, designing the package with object-oriented programming in mind allows us to reap many of this paradigm’s benefits, as explained above - and it simply makes sense for tensors, due to their abstract and multifaceted nature.

    Installing and loading the package

    This package is compatible with Mathematica 12.0 or newer. It consists of only one file, OGRe.m. There are several different ways to load the package:
    ◼
  • Run from local file with installation: This is the recommended option, as it allows you to permanently use the package offline from any Mathematica notebook. Download the file OGRe.m from https://github.com/bshoshany/OGRe and copy it to the directory given by FileNameJoin[{$UserBaseDirectory,”Applications”}]. The package may now be loaded from any notebook by writing Needs[“OGRe`”] (note the backtick ` following the word OGRe).
  • ◼
  • Run from local file without installation: This option allows you to use the package in a portable fashion, without installing it in the Applications directory. Download the file OGRe.m from https://github.com/bshoshany/OGRe, place it in the same directory as the notebook you would like to use, and use the command Get[“OGRe.m”,PathNotebookDirectory[]] to load the package.
  • ◼
  • Run from web with installation: This option allows you to automatically download and install the package on any computer. Simply run the command URLDownload[“https://raw.githubusercontent.com/bshoshany/OGRe/master/OGRe.m”,FileNameJoin[{$UserBaseDirectory,”Applications”,”OGRe.m”}]] from any Mathematica notebook to permanently install the package. Then use Needs[“OGRe`”] from any notebook to load it.
  • ◼
  • Run from web without installation: This option allows you to use the package from any Mathematica notebook on any computer, without manually downloading or installing it, as long as you have a working Internet connection. It also ensures that you always use the latest version of the package, but be aware that updates may sometimes not be fully backwards compatible. Simply write Get[“https://raw.githubusercontent.com/bshoshany/OGRe/master/OGRe.m”] in any Mathematica notebook to load the package.
  • To uninstall the package, just delete the file from the Applications directory, which can be done from within Mathematica using the command DeleteFile[FileNameJoin[{$UserBaseDirectory,”Applications”,”OGRe.m”}]].
    For the purposes of this documentation, I will use the “run from local file without installation” option, since you most likely downloaded both the documentation and the package together:
    In[]:=
    Get["OGRe.m",PathNotebookDirectory[]]
    OGRe:
    OGRe: An Object-Oriented General Relativity Package for Mathematica
    By Barak Shoshany (
    baraksh@gmail.com
    ) (
    baraksh.com
    )
    v1.7.0 (2021-09-17)
    GitHub repository:
    https://github.com/bshoshany/OGRe
    • To view the full documentation for the package, type
    TDocs[]
    .
    • To list all available modules, type
    ?OGRe`*
    .
    • To get help on a particular module, type ? followed by the module name.
    • To enable parallelization, type
    TSetParallelization[True]
    .
    • To check for updates, type
    TCheckForUpdates[]
    . To enable automatic checks for updates at startup, type
    TSetAutoUpdates[True]
    .
    The package displays a welcome message upon loading, which provides some information on how to get started. As stated in the welcome message, to list all of the modules available in this package, you may use the following command:
    In[]:=
    ?OGRe`*
    Out[]=
    OGRe`
    TAddCoordTransformation
    TChangeSymbol
    TNewCoordinates
    TCalc
    TCheckForUpdates
    TNewMetric
    TCalcChristoffel
    TCite
    TNewTensor
    TCalcEinsteinTensor
    TCovariantD
    TPartialD
    TCalcGeodesicFromChristoffel
    TDelete
    TSetAllowOverwrite
    TCalcGeodesicFromLagrangian
    TDocs
    TSetAssumptions
    TCalcGeodesicWithTimeParameter
    TExport
    TSetAutoUpdates
    TCalcLagrangian
    TExportAll
    TSetCurveParameter
    TCalcNormSquared
    TGetComponents
    TSetIndexLetters
    TCalcRicciScalar
    TImport
    TSetParallelization
    TCalcRicciTensor
    TImportAll
    TSetReservedSymbols
    TCalcRiemannTensor
    TInfo
    TShow
    TChangeDefaultCoords
    TLineElement
    TSimplify
    TChangeDefaultIndices
    TList
    TVolumeElementSquared
    TChangeID
    TMessage
    ​
    Clicking on the name of any module in this list will show its usage message. Notice that all OGRe modules start with the letter T, to help distinguish them from other modules, whether built-in or from other packages.
    When the package loads, it will automatically check the GitHub repository for updates. This can also be done manually using the module TCheckForUpdates. If a new version of the package has been released, you will be given the option to download or install it in one click. If you are running the package directly from the web, you will always be using the latest version.
    In[]:=
    ?TCheckForUpdates
    Out[]=
    Symbol
    TCheckForUpdates[] checks the GitHub repository for new versions of this package. If a new version is available, the user will be given the option to download or install it.
    You can also disable automatic checks for updates at startup in all future sessions by calling TSetAutoUpdates[False]:
    In[]:=
    ?TSetAutoUpdates
    Out[]=
    Symbol
    TSetAutoUpdates[False] turns off automatic checks for updates at startup. TSetAutoUpdates[True] turns them back on, which is the default setting. TSetAutoUpdates[] returns the current setting. Note that this setting is persistent between sessions.
    If at any point you wish to view this documentation, simply type TDocs[]. This file (OGRe_Documentation.nb) will then be automatically opened in Mathematica.
    In[]:=
    ?TDocs
    Out[]=
    Symbol
    TDocs[] opens the Mathematica notebook OGRe_Documentation.nb from the GitHub repository, which contains the full documentation for the package.

    Creating and displaying tensor objects

    Defining coordinates

    To define tensors, we first need to define the manifold on which they reside. Since we are focusing on general relativity, we will use 4-dimensional spacetime manifolds in the following examples, but this package works equally well with manifolds that are purely spatial and/or have a different number of dimensions.
    The first step is to define the coordinate system. In OGRe, coordinates are represented as a special tensor object: a vector
    μ
    x
    (a tensor of rank 1) representing a point. To define the coordinates, we use the module TNewCoordinates:
    In[]:=
    ?TNewCoordinates
    Out[]=
    Symbol
    TNewCoordinates[coordinatesID, symbols], creates a new tensor object representing a coordinate system.​coordinatesID is a string that will be used to identify the new object, and must be unique.​symbols are the coordinate symbols, e.g. {t, x, y, z}. They will automatically be cleared and protected against future changes using TSetReservedSymbols[].
    For example, to define the Cartesian coordinate system, we use the following syntax:
    In[]:=
    TNewCoordinates["Cartesian",{t,x,y,z}]
    Out[]=
    Cartesian
    The first argument is the new tensor object’s unique ID. This is the string that we will use to refer to this tensor from now on, and it will also be displayed whenever we print the tensor. The ID string is case-sensitive, can include any characters, and can be of any length, but it is recommended to keep it short and simple. Once a tensor object is created, you cannot create another tensor object with the same ID unless you rename or delete it first (see below). Note that the ID string is also the return value of the module; generally, all modules that operate on a tensor object will return its ID string as output. This allows us to compose different modules together, as we will see below.
    The second argument is the list of coordinates. Note that the order of coordinates matters, as it will determine the order of components in the tensors defined on this manifold. The symbols used also matter, as tensor components will usually be functions of these symbols. We can similarly define the spherical coordinate system:
    In[]:=
    TNewCoordinates["Spherical",{t,r,θ,ϕ}]
    Out[]=
    Spherical
    It is crucial that the coordinate symbols, in this case t, r, θ, ϕ, remain as undefined symbols throughout the calculation, otherwise errors may occur. For example, if our metric contains functions of r, and at some point in the notebook we set r = 1, then Mathematica will replace every instance of r with 1, which means those abstract functions will be replaced with their values evaluated at r = 1. Furthermore, if we, for example, want to take the derivative with respect to r (e.g. for the purpose of calculating various curvature tensors), this will not be possible, since one cannot take a derivative with respect to a number.
    To prevent such errors, TNewCoordinates automatically clears any previous definitions of the given symbols, and also protects them against future changes. This is done using the TSetReservedSymbols module:
    In[]:=
    ?TSetReservedSymbols
    Indeed, if we now try to give r the value 1, we will get an error:
    You can also use TSetReservedSymbols manually for any constants or parameters used in the tensor, as we will demonstrate in the next section. Note that it is always possible to replace reserved symbols with numbers or other expressions using /. or ReplaceAll, so giving a value to a reserved symbol directly is not something you will ever need to do.

    Defining metrics

    To finish defining a manifold, we need to define its metric tensor. For this, we use the module TNewMetric:
    Again, we must input a unique ID for the new tensor object corresponding to the metric. We also input the unique ID of a coordinate system created using TNewCoordinates. This coordinate system is the one in which the components of the metric are initially given, but they will be automatically transformed to other coordinate systems later as needed. Note that the components are assumed to be the representation of the metric with two lower indices, since that is how metrics are usually defined; one upper and one lower index is just the identity matrix, and two upper indices is the inverse metric. Optionally, we can also specify a symbol to be used for representing the metric.
    Let us use this module to create a tensor object for the Minkowski metric, specifying the components in Cartesian coordinates:
    As with TNewCoordinates, we received the ID string for the new tensor object as output.
    Similarly, let us define the Schwarzschild metric, this time specifying the components in spherical coordinates. However, before we can safely do so, we should take one additional step. When we defined the coordinates using TNewCoordinates, the symbols used were automatically reserved, that is, cleared and protected from future changes, using TSetReservedSymbols. However, TNewMetric does not automatically reserve any symbols it finds in the metric, since you might not want to reserve some of them. Since the Schwarzschild metric has a free parameter M, the mass of the black hole, we must reserve this symbol manually:
    Note that TSetReservedSymbols returned the full list of symbols we reserved so far: the coordinate symbols and the black hole’s mass.
    Now we can define the Schwarzschild metric:
    Note that since we do not specify a symbol, the symbol “g” will be used by default, as demonstrated below.

    Displaying tensors

    OGRe contains two modules for displaying the contents of tensors. The first one is TShow, which shows the ID, symbol, indices, coordinates, and components in those indices and coordinates, in vector or matrix form when applicable:
    Coordinates are also tensor objects, so we can use TShow to show the two coordinate tensors we defined above:
    We can also show the two metrics we created using these coordinates:
    The other module available in OGRe for displaying the contents of tensors is TList, which lists all of the unique (up to sign), non-zero components of the tensor. It is usually the best option for higher-rank tensors, which cannot be displayed in vector or matrix form, such as the Christoffel symbols or Riemann tensor (see below). Its syntax is:
    For example:
    Note that both TShow and TList display their outputs using the DisplayFormula Notebook style. It's up to the user to decide how to define this style; in this notebook, I used a font size of 20 and aligned to center. The style may be easily changed by clicking on the “Format” menu in Mathematica and selecting ”Edit Stylesheet”. Then, choose the DisplayFormula style, select that cell, and modify its format using the “Format” menu.
    If, as in the examples above, no additional arguments are given to TShow and TList, they display the tensors in their default indices and default coordinates, which are the ones first used to define the tensor (unless you change them later). So, for example, the default indices of the Minkowski metric are two lower indices, and its default coordinates are Cartesian. We will show later how to change these defaults, and how to display any tensor in any index configuration and coordinate system.

    Line and volume elements

    In the case of metrics, we can also display them as a line element using the module TLineElement:
    For example:
    Note that these are standard Mathematica expressions, so they can be manipulated like any other expressions, including operations such as simplifying or factoring. As an example of a more interesting (non-diagonal) line element, consider the Alcubierre warp drive metric:
    This is how the metric looks like in matrix form:
    f is a form function which is equal to 1 inside a “warp bubble” of finite radius and 0 outside it, and v is the velocity of the bubble, which can be faster than the speed of light (v > 1). Note that we reserved both v and f, since they are parameters used in the metric. Here we see another advantage of reserving symbols: since v and f are reserved symbols, and they are functions of the coordinates only, their arguments are not shown when using TShow and TList, for improved readability.
    It is easy to see that the metric is flat where f = 0, that is, outside the bubble. Its line element is:
    We can simplify it by doing some clever factorization:
    In this form, it is immediately clear that the metric is flat outside the warp bubble (where f is 0), and inside the warp bubble (when f is 1) it is a flat metric translated by an amount v[t] t in the z direction:
    Another thing we can do with a metric is calculate its volume elements squared, which is simply the determinant of the metric:
    For example:

    Choosing index letters

    By default, the TShow module uses Greek letters for the indices, in a specific order. The letters can be displayed or changed using the TSetIndexLetters module:
    The default letters are:
    This means that the letter μ will be used for the first index, ν for the second, and so on. However, sometimes we want to use different letters. For example, let us change the indices to lowercase English letters:
    “Show” will now use these letters - in this particular order - when displaying tensors:
    Finally, let us reset the letters to the default:

    Operations on single tensors

    Calculations with tensors

    Curves and geodesics

    More about tensor calculations

    Parallelization

    Additional information

    Version history

    For the full version history, please see the file CHANGELOG.md in the GitHub repository.

    Feature requests and bug reports

    This package is under continuous and active development. If you encounter any bugs, or if you would like to request any additional features, please feel free to open a new issue on GitHub and I will look into it as soon as I can.

    Copyright and citing

    Copyright (c) 2021 Barak Shoshany ( http://baraksh.com ) (baraksh@gmail.com). Licensed under the MIT license; please see the file LICENSE.txt in the GitHub repository for more information.
    Citing information can always be obtained by executing the command TCite[]:
    If you use this package in published research, please cite it as follows:
    ◼
  • Shoshany, B., (2021). OGRe: An Object-Oriented General Relativity Package for Mathematica. Journal of Open Source Software, 6(65), 3416, https://doi.org/10.21105/joss.03416
  • You can also use the following BibTeX entry:
    @article{Shoshany2021_OGRe,
    author = {Barak Shoshany},
    doi = {10.21105/joss.03416},
    journal = {Journal of Open Source Software},
    number = {65},
    pages = {3416},
    publisher = {The Open Journal},
    title = {OGRe: An Object-Oriented General Relativity Package for Mathematica},
    url = {https://doi.org/10.21105/joss.03416},
    volume = {6},
    year = {2021}
    }
    Thank you for citing my work! :)

    Acknowledgements

    A major portion of the code for this package was written while I was a postdoctoral researcher in Niayesh Afshordi’s group at Perimeter Institute for Theoretical Physics in Waterloo, Ontario, Canada. I would like to thank Niayesh Afshordi and Perimeter Institute for their support during that time. Research at Perimeter Institute is supported in part by the Government of Canada through the Department of Innovation, Science, and Economic Development Canada and by the Province of Ontario through the Ministry of Colleges and Universities.