This worksheet illustrates some features of SageMath regarding vector calculus in the Euclidean 3-space by means of Cartesian and spherical coordinates. The corresponding tools have been developed within the SageManifolds project (version 1.1, as included in SageMath 8.1).
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:
version()
'SageMath version 8.1, Release Date: 2017-12-07'
First we set up the notebook to display mathematical objects using LaTeX formatting:
%display latex
We then introduce the Euclidean space as a 3-dimensional differentiable manifold:
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$:
cart.<x,y,z> = M.chart()
print(cart)
cart
Chart (M, (x, y, z))
We introduce spherical coordinates $(r,\theta,\phi)$ as the chart spher
on $M$:
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))
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
:
spher_to_cart = spher.transition_map(cart, [r*sin(th)*cos(ph), r*sin(th)*sin(ph), r*cos(th)])
spher_to_cart.display()
The inverse is also set:
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
.
cart_to_spher = spher_to_cart.inverse()
cart_to_spher.display()
The natural vector frame of spherical coordinates is
spher.frame()
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:
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)
In other words, the change-of-basis matrix is
to_orthonormal[spher.frame(),:,spher]
We construct the orthonormal frame from the natural frame of spherical coordinates by this change of basis:
es = spher.frame().new_frame(to_orthonormal, 'e')
print(es)
es
Vector frame (M, (e_1,e_2,e_3))
es[1].display(spher.frame(), spher)
es[2].display(spher.frame(), spher)
es[3].display(spher.frame(), spher)
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):
es[1].display()
es[2].display()
es[3].display()
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)$:
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]
Similarly, we form the inverse change of frame as:
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]
At this stage, the manifold (user) atlas is
M.atlas()
The default chart is the first one introduced on the manifold (it can be changed by means of the function M.set_default_chart
):
M.default_chart()
The following vector frames have been introduced on the manifold:
M.frames()
The default frame is the first one introduced on the manifold (it can be changed by means of the function M.set_default_frame
):
M.default_frame()
The following changes of frame have been defined:
M.changes_of_frame()
We define the vector field $U$ in terms of its components with respect to the default frame, i.e. the Cartesian frame:
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()
We can ask for its components in terms of the spherical orthonormal frame:
U.display(es)
U.display_comp(es)
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
:
U.display(es, spher)
U.display_comp(es, spher)
We may also ask for the components of $U$ w.r.t. the natural frame of spherical coordinates:
U.display(spher.frame())
U.display(spher.frame(), spher)
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)$:
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)
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
):
V.display(cart.frame(), spher)
V.display_comp(cart.frame(), spher)
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)$:
g = M.riemannian_metric('g')
g[1,1], g[2,2], g[3,3] = 1, 1, 1
g.display()
The components of $g$ with respect to the spherical coordinates are then:
g.display(spher.frame(), spher)
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)$:
g.display(es, spher)
The covariant derivative operator $\nabla$ is introduced as the (Levi-Civita) connection associated with $g$:
nabla = g.connection()
print(nabla)
nabla
Levi-Civita connection nabla_g associated with the Riemannian metric g on the 3-dimensional differentiable manifold M
The connection coefficient with respect to the natural frame of spherical coordinates (Christoffel symbols) are:
nabla.display(spher.frame(), spher)
while those with respect to the orthonormal spherical frame are:
nabla.display(es, spher)
The covariant derivative of $U$ is
nabU = nabla(U)
print(nabU)
Tensor field nabla_g(U) of type (1,1) on the 3-dimensional differentiable manifold M
nabU.display()
while the covariant derivative of $V$ is
nabV = nabla(V)
print(nabV)
Tensor field nabla_g(V) of type (1,1) on the 3-dimensional differentiable manifold M
nabV.display(es, spher)