The goal of {ricci} is to provide a compact1 R interface for performing tensor calculations. This is achieved by allowing (upper and lower) index labeling of R’s array
and making use of Ricci calculus conventions to implicitly trigger contractions and diagonal subsetting. Explicit tensor operations, such as addition, subtraction and multiplication of tensors via the standard operators (*
, +
, -
, /
, ==
), raising and lowering indices, taking symmetric or antisymmetric tensor parts, as well as the Kronecker product are available. Common tensors like the Kronecker delta, Levi Civita epsilon, certain metric tensors, the Christoffel symbols, the Riemann as well as Ricci tensors are provided. The covariant derivative of tensor fields with respect to any metric tensor can be evaluated. An effort was made to provide the user with useful error messages.
{ricci} uses the calculus package (Guidotti 2022) behind the scenes to perform calculations and provides an alternative interface to a subset of its functionality. Notably, {calculus} also supports symbolic calculations which also enables {ricci} to do the same. Symbolic expressions are optionally simplified when the Ryacas package is installed.
Installation
You can install the latest CRAN release of ricci with:
install.packages("ricci")
Alternatively, you can install the development version of ricci from GitHub with:
# install.packages("pak")
pak::pak("lschneiderbauer/ricci")
Example
The central object is R’s array
. Adding index slot labels allows us to perform common tensor operations implicitly. After the desired calculations have been carried out we can remove the labels to obtain an ordinary array
.
The following (admittedly very artificial) example shows how to express the contraction of two tensors, and subsequent symmetrization and diagonal subsetting. For demonstration purposes we use an arbitrary array of rank 3.
library(ricci)
# numeric data
a <- array(1:(2^3), dim = c(2, 2, 2))
# create labeled array (tensor)
(a %_% .(i, j, k) *
# mutliply with a labeled array (tensor) and raise index i and k
a %_% .(i, l, k) |> r(i, k, g = g_mink_cart(2))) |>
# * -i and +i as well as -k and +k dimension are implictely contracted
# the result is a tensor of rank 2
sym(j, l) |> # symmetrize over i and l
subst(l -> j) |> # rename index and trigger diagonal subsetting
as_a(j) # we unlabel the tensor with index order (j)
#> [1] 8 8
The same instructions work for a symbolic array:
# enable optional simplfying procedures
# (takes a toll on performance)
options(ricci.auto_simplify = TRUE)
# symbolic data
a <- array(paste0("a", 1:(2^3)), dim = c(2, 2, 2))
(a %_% .(i, j, k) *
# mutliply with a labeled array (tensor) and raise index i and k
a %_% .(i, l, k) |> r(i, k, g = g_mink_cart(2))) |>
# * -i and +i as well as -k and +k dimension are implictely contracted
# the result is a tensor of rank 2
sym(j, l) |> # symmetrize over i and l
subst(l -> j) |> # rename index and trigger diagonal subsetting
as_a(j) # we unlabel the tensor with index order (j)
#> [1] "a1^2+a6^2-(a5^2+a2^2)" "a3^2+a8^2-(a7^2+a4^2)"
Another main feature is the covariant derivative of symbolic arrays. The following examples calculate the Hessian matrix as well as the Laplacian of the scalar function \sin(r) in spherical coordinates in three dimensions.
D_{ik} = \nabla_i \nabla_k \sin(r)
covd("sin(r)", .(i, k), g = g_eucl_sph(3)) |>
simplify()
#> <Labeled Array> [3x3] .(-i, -k)
#> [,1] [,2] [,3]
#> [1,] "-sin(r)" "0" "0"
#> [2,] "0" "r*cos(r)" "0"
#> [3,] "0" "0" "r*sin(ph1)^2*cos(r)"
\Delta \sin(r) = \nabla_i \nabla^i \sin(r)
covd("sin(r)", .(i, +i), g = g_eucl_sph(3)) |>
simplify()
#> <Scalar>
#> [1] "(2*cos(r)-r*sin(r))/r"
The covariant derivative can not only be taken from scalars, but general indexed tensors, as the following example, involving the curl of a, shows.
\left(\nabla \times a\right)^i = \varepsilon^{i}_{\;jk} \nabla^j a^k
g <- g_eucl_sph(3)
a <- c(0, 1, 0)
(a %_% .(+k) |> covd(.(+j), g = g) *
e(i, j, k) |> r(i, g = g)) |>
simplify()
#> <Labeled Array> [3] .(+i)
#> [1] "0" "0" "2/(r^3*sin(ph1)^2)"
For more details, see vignette("ricci", package = "ricci")
. For more information about how to use tensor fields and the covariant derivative, see vignette("tensor_fields", package = "ricci")
.