---
title: "Tensor Fields"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Tensor Fields}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```

{ricci} has limited support for [tensor fields](https://en.wikipedia.org/wiki/Tensor_field). Non-constant tensor fields in {ricci} are modeled by `array()`s whose elements are character strings, each string containing a mathematical expression (which are interpreted as functions of manifold coordinates).

This approach implies that only subset of all tensor fields can be modeled:

-   Only mathematical expressions that are understood by R are allowed.

-   If derivatives are involved only expressions where R's `deriv()` knows the derivative of are allowed.

-   Not all tensor fields can be expressed in closed-form expressions to begin with.

Nevertheless, the subset of tensor fields that {ricci} does currently support can still be nontrivial and we still consider this feature useful to this extent.

### Covariant derivative

The main usage of tensor fields in {ricci} is to calculate covariant derivatives, i.e. to make use of the function `covd()`. Similar to the product operator `*` which unifies products by making use of the index structure, `covd()` unifies various differential operators, like e.g. the "gradient", "divergence", "curl", the "Hessian" and the "Laplacian", in any dimension on any (pseudo-) Riemannian manifold and in any coordinate system.

`covd()` requires two main ingredients: a tensor field it can act upon, and a metric tensor field. This tensor field and the metric tensor are thought to exist on the same manifold and they are required to be specified in the same coordinate system.

#### Examples

{ricci} already provides a couple of standard metric tensors, the standard Euclidean metric tensor $g$ in Cartesian coordinates $(x_1,x_2,x_3)$ is simply the identity matrix, while in spherical coordinates $(r, \phi_1, \phi_2)$ the metric tensor appears more complicated:

```{r setup}
library(ricci)

# enable optional simplfying procedures
# (takes a toll on performance)
options(ricci.auto_simplify = TRUE)

g_eucl_cart(3)

g_eucl_sph(3)
```

Equipped with such metrics, we can for example pick a simple tensor field, namely a scalar function $f(r, \phi_1, \phi_2) = r^{-1}$, and calculate our first gradient:

```{r}
"1/r" |> covd(.(k), g = g_eucl_sph(3))
```

The argument `.(k)` is nothing special but simply defines a name for the new rank of the array.

The Hessian could be computed in similar fashion:

```{r}
"1/r" |> covd(.(k, l), g = g_eucl_sph(3))
```

The operation above can be set into the context of electrodynamics where $f$ is an electrostatic potential, and its gradient is the electric field. For the same electrostatic potential we can very simply calculate the electromagnetic tensor too after forming the electromagnetic potential $A_\mu$:

```{r}
# electromagnetic potential
A <- c("1/r", "0", "0", "0")

A %_% .(m) |>
  covd(.(n), g = g_mink_sph(4)) |>
  asym(m, n)
```

We can also chain multiple covariant derivatives in any way we'd like. A well known second-order differential operator, the Laplacian $\Delta = \nabla_k \nabla^k$ can easily be written down:

```{r}
# on scalar field
"1/r" |> covd(.(k, +k), g = g_mink_sph(4))

# on a vector field
A %_% .(i) |> covd(.(k, +k), g = g_mink_sph(4))
```

One property of the metric tensor is that the (Levi Civita) covariant derivative of the metric tensor vanishes. We can test this easily:

```{r}
g <- g_eucl_sph(3)

g %_% .(i, j) |>
  covd(.(k), g = g) |>
  as_a(i, j, k)
```
