Vector calculus in Cartesian and spherical coordinates

This worksheet illustrates some features of SageManifolds (version 1.0, as included in SageMath 7.5) regarding vector calculus in the Euclidean 3-space by means of Cartesian and spherical coordinates.

Click here to download the worksheet file (ipynb format). To run it, you must start SageMath with the Jupyter notebook, via the command sage -n jupyter

NB: a version of SageMath at least equal to 7.5 is required to run this worksheet:

In [1]:
version()
Out[1]:
'SageMath version 7.5, Release Date: 2017-01-11'

Euclidean 3-space and Cartesian coordinates

First we set up the notebook to display mathematical objects using LaTeX formatting:

In [2]:
%display latex

We then introduce the Euclidean space as a 3-dimensional differentiable manifold:

In [3]:
M = Manifold(3, 'M', start_index=1)
print(M)
3-dimensional differentiable manifold M

We then introduce the Cartesian coordinates $(x,y,z)$ as the chart cart on $M$:

In [4]:
cart.<x,y,z> = M.chart()
print(cart)
cart
Chart (M, (x, y, z))
Out[4]:

Spherical coordinates

We introduce spherical coordinates $(r,\theta,\phi)$ as the chart spher on $M$:

In [5]:
spher.<r,th,ph> = M.chart(r'r:(0,+oo) th:(0,pi):\theta ph:(0,2*pi):\phi')
print(spher)
spher
Chart (M, (r, th, ph))
Out[5]:

Spherical coordinates do not form a regular coordinate system of the Euclidean space. So declaring that they span $M$ means that, strictly speaking, the manifold $M$ is not the whole Euclidean space, but the Euclidean space minus some half plane (the azimuthal origin). However, in this worksheet, this difference will not matter.

The change of coordinates $(r,\theta,\phi) \rightarrow (x,y,z)$ is introduced as a transition map from chart spher to chart cart:

In [6]:
spher_to_cart = spher.transition_map(cart, [r*sin(th)*cos(ph), r*sin(th)*sin(ph), r*cos(th)])
spher_to_cart.display()
Out[6]:

The inverse is also set:

In [7]:
spher_to_cart.set_inverse(sqrt(x^2+y^2+z^2), atan2(sqrt(x^2+y^2),z), atan2(y, x),
                          verbose=True)
Check of the inverse coordinate transformation:
  r == r
  th == arctan2(r*sin(th), r*cos(th))
  ph == arctan2(r*sin(ph)*sin(th), r*cos(ph)*sin(th))
  x == x
  y == y
  z == z

The check that the provided formulas do correspond to the inverse change of coordinates is passed, modulo some lack of simplification in some trigonometrical formulas involving the function arctan2.

In [8]:
cart_to_spher = spher_to_cart.inverse()
cart_to_spher.display()
Out[8]:

The natural vector frame of spherical coordinates is

In [9]:
spher.frame()
Out[9]:

We shall expand vector and tensor fields on the orthonormal frame $(e_1, e_2, e_3)$ associated with spherical coordinates, which is related to the natural frame $(\partial/\partial r, \partial/\partial\theta, \partial/\partial\phi)$ displayed above by means of the following field of automorphisms:

In [10]:
to_orthonormal = M.automorphism_field()
to_orthonormal[spher.frame(),1,1,spher] = 1
to_orthonormal[spher.frame(),2,2,spher] = 1/r
to_orthonormal[spher.frame(),3,3,spher] = 1/(r*sin(th))
to_orthonormal.display(spher.frame(), spher)
Out[10]:

In other words, the change-of-basis matrix is

In [11]:
to_orthonormal[spher.frame(),:,spher] 
Out[11]:

We construct the orthonormal frame from the natural frame of spherical coordinates by this change of basis:

In [12]:
es = spher.frame().new_frame(to_orthonormal, 'e')
print(es)
es
Vector frame (M, (e_1,e_2,e_3))
Out[12]:
In [13]:
es[1].display(spher.frame(), spher)
Out[13]:
In [14]:
es[2].display(spher.frame(), spher)
Out[14]:
In [15]:
es[3].display(spher.frame(), spher)
Out[15]:

If we do not specify the frame and coordinates for the display, we get it in terms of the default ones (i.e. Cartesian frame and Cartesian coordinates):

In [16]:
es[1].display()
Out[16]:
In [17]:
es[2].display()
Out[17]:
In [18]:
es[3].display()
Out[18]:

By construction, the change of frame $(\partial/\partial r, \partial/\partial \theta, \partial/\partial \phi) \rightarrow (e_1,e_2,e_3)$ is known. We form the change of frame $(\partial/\partial x, \partial/\partial y, \partial/\partial z)\rightarrow (e_1,e_2,e_3)$ by composition of $(\partial/\partial x, \partial/\partial y, \partial/\partial z)\rightarrow (\partial/\partial r, \partial/\partial \theta, \partial/\partial \phi)$ with $(\partial/\partial r, \partial/\partial \theta, \partial/\partial \phi) \rightarrow (e_1,e_2,e_3)$:

In [19]:
M.set_change_of_frame(cart.frame(), es, 
                      M.change_of_frame(spher.frame(), es) * M.change_of_frame(cart.frame(), 
                                                                               spher.frame()),
                      compute_inverse=False)
M.change_of_frame(cart.frame(), es)[:, spher]
Out[19]:

Similarly, we form the inverse change of frame as:

In [20]:
M.set_change_of_frame(es, cart.frame(), 
                      M.change_of_frame(spher.frame(), cart.frame()) * 
                                  M.change_of_frame(es, spher.frame()),
                      compute_inverse=False)
M.change_of_frame(es, cart.frame())[:, spher]
Out[20]:

At this stage, the manifold (user) atlas is

In [21]:
M.atlas()
Out[21]:

The default chart is the first one introduced on the manifold (it can be changed by means of the function M.set_default_chart):

In [22]:
M.default_chart()
Out[22]:

The following vector frames have been introduced on the manifold:

In [23]:
M.frames()
Out[23]:

The default frame is the first one introduced on the manifold (it can be changed by means of the function M.set_default_frame):

In [24]:
M.default_frame()
Out[24]:

The following changes of frame have been defined:

In [25]:
M.changes_of_frame()
Out[25]:

Vector field defined in terms of its Cartesian components

We define the vector field $U$ in terms of its components with respect to the default frame, i.e. the Cartesian frame:

In [26]:
U = M.vector_field(name='U')
U[:] = [function('U_x')(x,y,z), function('U_y')(x,y,z), function('U_z')(x,y,z)]
U.display()
Out[26]:

We can ask for its components in terms of the spherical orthonormal frame:

In [27]:
U.display(es)
Out[27]:
In [28]:
U.display_comp(es)
Out[28]:

The above components are displayed in terms of the default chart (Cartesian coordinates). If we want them in terms of spherical coordinates, we have to specify it, by setting the second argument of the function display to spher:

In [29]:
U.display(es, spher)
Out[29]:
In [30]:
U.display_comp(es, spher)
Out[30]:

We may also ask for the components of $U$ w.r.t. the natural frame of spherical coordinates:

In [31]:
U.display(spher.frame())
Out[31]:
In [32]:
U.display(spher.frame(), spher)
Out[32]:

Vector field defined in terms of its spherical components

Let us consider a vector field $V$ defined by its components with respect to the orthonormal spherical frame es = $(e_1, e_2, e_3)$:

In [33]:
V = M.vector_field(name='V')
V[es,:,spher] = [function('V_1')(r,th,ph), function('V_2')(r,th,ph), function('V_3')(r,th,ph)]
V.display(es, spher)
Out[33]:

We may ask for the components of this vector field with respect to the Cartesian frame (first argument cart.frame()), each component being expressed in terms of spherical coordinates (second argument spher):

In [34]:
V.display(cart.frame(), spher)
Out[34]:
In [35]:
V.display_comp(cart.frame(), spher)
Out[35]:

Euclidean metric

The standard Euclidean metric is introduced as a Riemannian metric on $M$, whose components with respect to the Cartesian frame are $\mathrm{diag}(1,1,1)$:

In [36]:
g = M.riemannian_metric('g')
g[1,1], g[2,2], g[3,3] = 1, 1, 1
g.display()
Out[36]:

The components of $g$ with respect to the spherical coordinates are then:

In [37]:
g.display(spher.frame(), spher)
Out[37]:

Since es = $(e_1,e_2,e_3)$ is an orthonormal frame, the components of $g$ with respect to it are $\mathrm{diag}(1,1,1)$:

In [38]:
g.display(es, spher)
Out[38]:

The covariant derivative operator $\nabla$ is introduced as the (Levi-Civita) connection associated with $g$:

In [39]:
nabla = g.connection()
print(nabla)
nabla
Levi-Civita connection nabla_g associated with the Riemannian metric g on the 3-dimensional differentiable manifold M
Out[39]:

The connection coefficient with respect to the natural frame of spherical coordinates (Christoffel symbols) are:

In [40]:
nabla.display(spher.frame(), spher)
Out[40]:

while those with respect to the orthonormal spherical frame are:

In [41]:
nabla.display(es, spher)
Out[41]:

The covariant derivative of $U$ is

In [42]:
nabU = nabla(U)
print(nabU)
Tensor field nabla_g(U) of type (1,1) on the 3-dimensional differentiable manifold M
In [43]:
nabU.display()
Out[43]:

while the covariant derivative of $V$ is

In [44]:
nabV = nabla(V)
print(nabV)
Tensor field nabla_g(V) of type (1,1) on the 3-dimensional differentiable manifold M
In [45]:
nabV.display(es, spher)
Out[45]: