Affine Connections¶
The class AffineConnection
implements affine connections on
smooth manifolds.
AUTHORS:
- Eric Gourgoulhon, Michal Bejger (2013-2015) : initial version
- Marco Mancini (2015) : parallelization of some computations
REFERENCES:
- [Lee1997]
- [KN1963]
- [ONe1983]
-
class
sage.manifolds.differentiable.affine_connection.
AffineConnection
(domain, name, latex_name=None)¶ Bases:
sage.structure.sage_object.SageObject
Affine connection on a smooth manifold.
Let
be a differentiable manifold of class
(smooth manifold) over a non-discrete topological field
(in most applications
or
), let
be the algebra of smooth functions
(cf.
DiffScalarFieldAlgebra
) and letbe the
-module of vector fields on
(cf.
VectorFieldModule
). An affine connection onis an operator
that
- is
-bilinear, i.e. is bilinear when considering
as a vector space over
- is
-linear w.r.t. the first argument:
- obeys Leibniz rule w.r.t. the second argument:
The affine connection
gives birth to the covariant derivative operator acting on tensor fields, denoted by the same symbol:
where
stands for the
-module of tensor fields of type
on
(cf.
TensorFieldModule
), with the convention. For a vector field
, the covariant derivative
is a type-(1,1) tensor field such that
More generally for any tensor field
, we have
Note
The above convention means that, in terms of index notation, the “derivation index” in
is the last one:
INPUT:
domain
– the manifold on which the connection is defined (must be an instance of classDifferentiableManifold
)name
– name given to the affine connectionlatex_name
– (default:None
) LaTeX symbol to denote the affine connection; ifNone
, it is set toname
.
EXAMPLES:
Affine connection on a 3-dimensional manifold:
sage: M = Manifold(3, 'M', start_index=1) sage: c_xyz.<x,y,z> = M.chart() sage: nab = M.affine_connection('nabla', r'\nabla') ; nab Affine connection nabla on the 3-dimensional differentiable manifold M
A just-created connection has no connection coefficients:
sage: nab._coefficients {}
The connection coefficients relative to the manifold’s default frame [here
], are created by providing the relevant indices inside square brackets:
sage: nab[1,1,2], nab[3,2,3] = x^2, y*z # Gamma^1_{12} = x^2, Gamma^3_{23} = yz sage: nab._coefficients {Coordinate frame (M, (d/dx,d/dy,d/dz)): 3-indices components w.r.t. Coordinate frame (M, (d/dx,d/dy,d/dz))}
If not the default one, the vector frame w.r.t. which the connection coefficients are defined can be specified as the first argument inside the square brackets; hence the above definition is equivalent to:
sage: nab[c_xyz.frame(), 1,1,2], nab[c_xyz.frame(),3,2,3] = x^2, y*z sage: nab._coefficients {Coordinate frame (M, (d/dx,d/dy,d/dz)): 3-indices components w.r.t. Coordinate frame (M, (d/dx,d/dy,d/dz))}
Unset components are initialized to zero:
sage: nab[:] # list of coefficients relative to the manifold's default vector frame [[[0, x^2, 0], [0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, y*z], [0, 0, 0]]]
The treatment of connection coefficients in a given vector frame is similar to that of tensor components; see therefore the class
TensorField
for the documentation. In particular, the square brackets return the connection coefficients as instances ofCoordFunction
(of the subclassCoordFunctionSymb
in the current example), while the double square brackets return a scalar field:sage: nab[1,1,2] x^2 sage: nab[1,1,2].display() (x, y, z) |--> x^2 sage: type(nab[1,1,2]) <class 'sage.manifolds.coord_func_symb.CoordFunctionSymbRing_with_category.element_class'> sage: nab[[1,1,2]] Scalar field on the 3-dimensional differentiable manifold M sage: nab[[1,1,2]].display() M --> R (x, y, z) |--> x^2 sage: nab[[1,1,2]].coord_function() is nab[1,1,2] True
Action on a scalar field:
sage: f = M.scalar_field(x^2 - y^2, name='f') sage: Df = nab(f) ; Df 1-form df on the 3-dimensional differentiable manifold M sage: Df[:] [2*x, -2*y, 0]
The action of an affine connection connection on a scalar field must coincide with the differential:
sage: Df == f.differential() True
A generic affine connection has some torsion:
sage: DDf = nab(Df) ; DDf Tensor field nabla(df) of type (0,2) on the 3-dimensional differentiable manifold M sage: DDf.antisymmetrize()[:] # nabla does not commute on scalar fields: [ 0 -x^3 0] [ x^3 0 0] [ 0 0 0]
Let us check the standard formula
where the
‘s are the components of the connection’s torsion tensor:
sage: 2*DDf.antisymmetrize() == nab.torsion().contract(0,Df) True
The connection acting on a vector field:
sage: v = M.vector_field('v') sage: v[:] = (y*z, x*z, x*y) sage: Dv = nab(v) ; Dv Tensor field nabla(v) of type (1,1) on the 3-dimensional differentiable manifold M sage: Dv[:] [ 0 (x^2*y + 1)*z y] [ z 0 x] [ y x x*y*z^2]
Another example: connection on a non-parallelizable 2-dimensional manifold:
sage: M = Manifold(2, 'M') sage: U = M.open_subset('U') ; V = M.open_subset('V') sage: M.declare_union(U,V) # M is the union of U and V sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart() sage: transf = c_xy.transition_map(c_uv, (x+y, x-y), intersection_name='W', ....: restrictions1= x>0, restrictions2= u+v>0) sage: inv = transf.inverse() sage: W = U.intersection(V) sage: eU = c_xy.frame() ; eV = c_uv.frame() sage: c_xyW = c_xy.restrict(W) ; c_uvW = c_uv.restrict(W) sage: eUW = c_xyW.frame() ; eVW = c_uvW.frame() sage: nab = M.affine_connection('nabla', r'\nabla')
The connection is first defined on the open subset U by means of its coefficients w.r.t. the frame eU (the manifold’s default frame):
sage: nab[0,0,0], nab[1,0,1] = x, x*y
The coefficients w.r.t the frame eV are deduced by continuation of the coefficients w.r.t. the frame eVW on the open subset
:
sage: for i in M.irange(): ....: for j in M.irange(): ....: for k in M.irange(): ....: nab.add_coef(eV)[i,j,k] = nab.coef(eVW)[i,j,k,c_uvW].expr() ....:
At this stage, the connection is fully defined on all the manifold:
sage: nab.coef(eU)[:] [[[x, 0], [0, 0]], [[0, x*y], [0, 0]]] sage: nab.coef(eV)[:] [[[1/16*u^2 - 1/16*v^2 + 1/8*u + 1/8*v, -1/16*u^2 + 1/16*v^2 + 1/8*u + 1/8*v], [1/16*u^2 - 1/16*v^2 + 1/8*u + 1/8*v, -1/16*u^2 + 1/16*v^2 + 1/8*u + 1/8*v]], [[-1/16*u^2 + 1/16*v^2 + 1/8*u + 1/8*v, 1/16*u^2 - 1/16*v^2 + 1/8*u + 1/8*v], [-1/16*u^2 + 1/16*v^2 + 1/8*u + 1/8*v, 1/16*u^2 - 1/16*v^2 + 1/8*u + 1/8*v]]]
We may let it act on a vector field defined globally on
:
sage: a = M.vector_field('a') sage: a[eU,:] = [-y,x] sage: a[eV,0] = a[eVW,0,c_uvW].expr() sage: a[eV,1] = a[eVW,1,c_uvW].expr() sage: a.display(eU) a = -y d/dx + x d/dy sage: a.display(eV) a = v d/du - u d/dv sage: da = nab(a) ; da Tensor field nabla(a) of type (1,1) on the 2-dimensional differentiable manifold M sage: da.display(eU) nabla(a) = -x*y d/dx*dx - d/dx*dy + d/dy*dx - x*y^2 d/dy*dy sage: da.display(eV) nabla(a) = (-1/16*u^3 + 1/16*u^2*v + 1/16*(u + 2)*v^2 - 1/16*v^3 - 1/8*u^2) d/du*du + (1/16*u^3 - 1/16*u^2*v - 1/16*(u - 2)*v^2 + 1/16*v^3 - 1/8*u^2 + 1) d/du*dv + (1/16*u^3 - 1/16*u^2*v - 1/16*(u - 2)*v^2 + 1/16*v^3 - 1/8*u^2 - 1) d/dv*du + (-1/16*u^3 + 1/16*u^2*v + 1/16*(u + 2)*v^2 - 1/16*v^3 - 1/8*u^2) d/dv*dv
A few tests:
sage: nab(a.restrict(V)) == da.restrict(V) True sage: nab.restrict(V)(a) == da.restrict(V) True sage: nab.restrict(V)(a.restrict(U)) == da.restrict(W) True sage: nab.restrict(U)(a.restrict(V)) == da.restrict(W) True
-
add_coef
(frame=None)¶ Return the connection coefficients in a given frame for assignment, keeping the coefficients in other frames.
See method
coef()
for details about the definition of the connection coefficents.To delete the connection coefficients in other frames, use the method
set_coef()
instead.INPUT:
frame
– (default:None
) vector frame in which the connection coefficients are defined; ifNone
, the default frame of the connection’s domain is assumed.
Warning
If the connection has already coefficients in other frames, it is the user’s responsibility to make sure that the coefficients to be added are consistent with them.
OUTPUT:
- connection coefficients in the given frame, as an instance of the
class
Components
; if such connection coefficients did not exist previously, they are created. See methodcoef()
for the storage convention of the connection coefficients.
EXAMPLES:
Setting the coefficients of an affine connection w.r.t. some coordinate frame:
sage: M = Manifold(2, 'M', start_index=1) sage: X.<x,y> = M.chart() sage: nab = M.affine_connection('nabla', latex_name=r'\nabla') sage: eX = X.frame(); eX Coordinate frame (M, (d/dx,d/dy)) sage: nab.add_coef(eX) 3-indices components w.r.t. Coordinate frame (M, (d/dx,d/dy)) sage: nab.add_coef(eX)[1,2,1] = x*y sage: nab.display(eX) Gam^x_yx = x*y
Since
eX
is the manifold’s default vector frame, its mention may be omitted:sage: nab.add_coef()[1,2,1] = x*y sage: nab.add_coef() 3-indices components w.r.t. Coordinate frame (M, (d/dx,d/dy)) sage: nab.add_coef()[1,2,1] = x*y sage: nab.display() Gam^x_yx = x*y
Adding connection coefficients w.r.t. to another vector frame:
sage: e = M.vector_frame('e') sage: nab.add_coef(e) 3-indices components w.r.t. Vector frame (M, (e_1,e_2)) sage: nab.add_coef(e)[2,1,1] = x+y sage: nab.add_coef(e)[2,1,2] = x-y sage: nab.display(e) Gam^2_11 = x + y Gam^2_12 = x - y
The coefficients w.r.t. the frame
eX
have been kept:sage: nab.display(eX) Gam^x_yx = x*y
To delete them, use the method
set_coef()
instead.
-
coef
(frame=None)¶ Return the connection coefficients relative to the given frame.
being the manifold’s dimension, the connection coefficients relative to the vector frame
are the
scalar fields
defined by
If the connection coefficients are not known already, they are computed from the above formula.
INPUT:
frame
– (default:None
) vector frame relative to which the connection coefficients are required; if none is provided, the domain’s default frame is assumed
OUTPUT:
- connection coefficients relative to the frame
frame
, as an instance of the classComponents
with 3 indices ordered as
EXAMPLES:
Connection coefficient of an affine connection on a 3-dimensional manifold:
sage: M = Manifold(3, 'M', start_index=1) sage: c_xyz.<x,y,z> = M.chart() sage: nab = M.affine_connection('nabla', r'\nabla') sage: nab[1,1,2], nab[3,2,3] = x^2, y*z # Gamma^1_{12} = x^2, Gamma^3_{23} = yz sage: nab.coef() 3-indices components w.r.t. Coordinate frame (M, (d/dx,d/dy,d/dz)) sage: type(nab.coef()) <class 'sage.tensor.modules.comp.Components'> sage: M.default_frame() Coordinate frame (M, (d/dx,d/dy,d/dz)) sage: nab.coef() is nab.coef(c_xyz.frame()) True sage: nab.coef()[:] # full list of coefficients: [[[0, x^2, 0], [0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, y*z], [0, 0, 0]]]
-
connection_form
(i, j, frame=None)¶ Return the connection 1-form corresponding to the given index and vector frame.
The connection 1-forms with respect to the frame
are the
1-forms
defined by
for any vector
.
The components of
in the coframe
dual to
are nothing but the connection coefficients
relative to the frame
:
INPUT:
i
,j
– indices identifying the 1-formframe
– (default:None
) vector frame relative to which the connection 1-forms are defined; ifNone
, the default frame of the connection’s domain is assumed.
OUTPUT:
- the 1-form
, as an instance of
DiffForm
EXAMPLES:
Connection 1-forms on a 3-dimensional manifold:
sage: M = Manifold(3, 'M', start_index=1) sage: c_xyz.<x,y,z> = M.chart() sage: nab = M.affine_connection('nabla', r'\nabla') sage: nab[1,1,1], nab[1,1,2], nab[1,1,3] = x*y*z, x^2, -y*z sage: nab[1,2,3], nab[1,3,1], nab[1,3,2] = -x^3, y^2*z, y^2-x^2 sage: nab[2,1,1], nab[2,1,2], nab[2,2,1] = z^2, x*y*z^2, -x^2 sage: nab[2,3,1], nab[2,3,3], nab[3,1,2] = x^2+y^2+z^2, y^2-z^2, x*y+z^2 sage: nab[3,2,1], nab[3,2,2], nab[3,3,3] = x*y+z, z^3 -y^2, x*z^2 - z*y^2 sage: nab.connection_form(1,1) # connection 1-form (i,j)=(1,1) w.r.t. M's default frame 1-form nabla connection 1-form (1,1) on the 3-dimensional differentiable manifold M sage: nab.connection_form(1,1)[:] [x*y*z, x^2, -y*z]
The result is cached (until the connection is modified via
set_coef()
oradd_coef()
):sage: nab.connection_form(1,1) is nab.connection_form(1,1) True
Connection 1-forms w.r.t. a non-holonomic frame:
sage: ch_basis = M.automorphism_field() sage: ch_basis[1,1], ch_basis[2,2], ch_basis[3,3] = y, z, x sage: e = M.default_frame().new_frame(ch_basis, 'e') sage: e[1][:], e[2][:], e[3][:] ([y, 0, 0], [0, z, 0], [0, 0, x]) sage: nab.connection_form(1,1,e) 1-form nabla connection 1-form (1,1) on the 3-dimensional differentiable manifold M sage: nab.connection_form(1,1,e).comp(e)[:] [x*y^2*z, (x^2*y + 1)*z/y, -x*y*z]
Check of the formula
:
sage: #... on the manifold's default frame (d/dx, d/dy, d:dz) sage: dx = M.default_frame().coframe() ; dx Coordinate coframe (M, (dx,dy,dz)) sage: check = [] sage: for i in M.irange(): ....: for j in M.irange(): ....: check.append( nab.connection_form(i,j) == \ ....: sum( nab[[i,j,k]]*dx[k] for k in M.irange() ) ) ....: sage: check [True, True, True, True, True, True, True, True, True] sage: #... on the frame e sage: ef = e.coframe() ; ef Coframe (M, (e^1,e^2,e^3)) sage: check = [] sage: for i in M.irange(): ....: for j in M.irange(): ....: s = nab.connection_form(i,j,e).comp(c_xyz.frame(), from_basis=e) ....: check.append( nab.connection_form(i,j,e) == sum( nab.coef(e)[[i,j,k]]*ef[k] for k in M.irange() ) ) ....: sage: check [True, True, True, True, True, True, True, True, True]
Check of the formula
:
sage: v = M.vector_field() sage: v[:] = (x*y, z^2-3*x, z+2*y) sage: b = M.default_frame() sage: for j in M.irange(): # check on M's default frame ....: nab(b[j]).contract(v) == \ ....: sum( nab.connection_form(i,j)(v)*b[i] for i in M.irange()) True True True sage: for j in M.irange(): # check on frame e ....: nab(e[j]).contract(v) == \ ....: sum( nab.connection_form(i,j,e)(v)*e[i] for i in M.irange()) True True True
-
curvature_form
(i, j, frame=None)¶ Return the curvature 2-form corresponding to the given index and vector frame.
The curvature 2-forms with respect to the frame
are the
2-forms
defined by
where
is the connection’s Riemann curvature tensor (cf.
riemann()
),is the coframe dual to
and
is a generic pair of vectors.
INPUT:
i
,j
– indices identifying the 2-formframe
– (default:None
) vector frame relative to which the curvature 2-forms are defined; ifNone
, the default frame of the connection’s domain is assumed.
OUTPUT:
- the 2-form
, as an instance of
DiffForm
EXAMPLES:
Curvature 2-forms on a 3-dimensional manifold:
sage: M = Manifold(3, 'M', start_index=1) sage: c_xyz.<x,y,z> = M.chart() sage: nab = M.affine_connection('nabla', r'\nabla') sage: nab[1,1,1], nab[1,1,2], nab[1,1,3] = x*y*z, x^2, -y*z sage: nab[1,2,3], nab[1,3,1], nab[1,3,2] = -x^3, y^2*z, y^2-x^2 sage: nab[2,1,1], nab[2,1,2], nab[2,2,1] = z^2, x*y*z^2, -x^2 sage: nab[2,3,1], nab[2,3,3], nab[3,1,2] = x^2+y^2+z^2, y^2-z^2, x*y+z^2 sage: nab[3,2,1], nab[3,2,2], nab[3,3,3] = x*y+z, z^3 -y^2, x*z^2 - z*y^2 sage: nab.curvature_form(1,1) # long time 2-form curvature (1,1) of connection nabla w.r.t. Coordinate frame (M, (d/dx,d/dy,d/dz)) on the 3-dimensional differentiable manifold M sage: nab.curvature_form(1,1).display() # long time (if above is skipped) curvature (1,1) of connection nabla w.r.t. Coordinate frame (M, (d/dx,d/dy,d/dz)) = (y^2*z^3 + (x*y^3 - x)*z + 2*x) dx/\dy + (x^3*z^2 - x*y) dx/\dz + (x^4*y*z^2 - z) dy/\dz
Curvature 2-forms w.r.t. a non-holonomic frame:
sage: ch_basis = M.automorphism_field() sage: ch_basis[1,1], ch_basis[2,2], ch_basis[3,3] = y, z, x sage: e = M.default_frame().new_frame(ch_basis, 'e') sage: e[1].display(), e[2].display(), e[3].display() (e_1 = y d/dx, e_2 = z d/dy, e_3 = x d/dz) sage: ef = e.coframe() sage: ef[1].display(), ef[2].display(), ef[3].display() (e^1 = 1/y dx, e^2 = 1/z dy, e^3 = 1/x dz) sage: nab.curvature_form(1,1,e) # long time 2-form curvature (1,1) of connection nabla w.r.t. Vector frame (M, (e_1,e_2,e_3)) on the 3-dimensional differentiable manifold M sage: nab.curvature_form(1,1,e).display(e) # long time (if above is skipped) curvature (1,1) of connection nabla w.r.t. Vector frame (M, (e_1,e_2,e_3)) = (y^3*z^4 + 2*x*y*z + (x*y^4 - x*y)*z^2) e^1/\e^2 + (x^4*y*z^2 - x^2*y^2) e^1/\e^3 + (x^5*y*z^3 - x*z^2) e^2/\e^3
Cartan’s second structure equation is
where the
‘s are the connection 1-forms (cf.
connection_form()
). Let us check it on the frame e:sage: omega = nab.connection_form sage: check = [] sage: for i in M.irange(): # long time ....: for j in M.irange(): ....: check.append( nab.curvature_form(i,j,e) == \ ....: omega(i,j,e).exterior_derivative() + \ ....: sum( omega(i,k,e).wedge(omega(k,j,e)) for k in M.irange()) ) ....: sage: check # long time [True, True, True, True, True, True, True, True, True]
-
del_other_coef
(frame=None)¶ Delete all the coefficients but those corresponding to
frame
.INPUT:
frame
– (default:None
) vector frame, the connection coefficients w.r.t. which are to be kept; ifNone
, the default frame of the connection’s domain is assumed.
EXAMPLE:
We first create two sets of connection coefficients:
sage: M = Manifold(2, 'M', start_index=1) sage: X.<x,y> = M.chart() sage: nab = M.affine_connection('nabla', latex_name=r'\nabla') sage: eX = X.frame() sage: nab.set_coef(eX)[1,2,1] = x*y sage: e = M.vector_frame('e') sage: nab.add_coef(e)[2,1,1] = x+y sage: nab.display(eX) Gam^x_yx = x*y sage: nab.display(e) Gam^2_11 = x + y
Let us delete the connection coefficients w.r.t. all frames except for frame
eX
:sage: nab.del_other_coef(eX) sage: nab.display(eX) Gam^x_yx = x*y
The connection coefficients w.r.t. frame
e
have indeed been deleted:sage: nab.display(e) Traceback (most recent call last): ... ValueError: no common frame found for the computation
-
display
(frame=None, chart=None, symbol=None, latex_symbol=None, index_labels=None, index_latex_labels=None, coordinate_labels=True, only_nonzero=True, only_nonredundant=False)¶ Display all the connection coefficients w.r.t. to a given frame, one per line.
The output is either text-formatted (console mode) or LaTeX-formatted (notebook mode).
INPUT:
frame
– (default:None
) vector frame relative to which the connection coefficients are defined; ifNone
, the default frame of the connection’s domain is usedchart
– (default:None
) chart specifying the coordinate expression of the connection coefficients; ifNone
, the default chart of the connection’s domain is usedsymbol
– (default:None
) string specifying the symbol of the connection coefficients; ifNone
, ‘Gam’ is usedlatex_symbol
– (default:None
) string specifying the LaTeX symbol for the components; ifNone
, ‘\Gamma’ is usedindex_labels
– (default:None
) list of strings representing the labels of each index; ifNone
, integer labels are used, except ifframe
is a coordinate frame andcoordinate_symbols
is set toTrue
, in which case the coordinate symbols are usedindex_latex_labels
– (default:None
) list of strings representing the LaTeX labels of each index; ifNone
, integer labels are used, except ifframe
is a coordinate frame andcoordinate_symbols
is set toTrue
, in which case the coordinate LaTeX symbols are usedcoordinate_labels
– (default:True
) boolean; ifTrue
, coordinate symbols are used by default (instead of integers) as index labels wheneverframe
is a coordinate frameonly_nonzero
– (default:True
) boolean; ifTrue
, only nonzero connection coefficients are displayedonly_nonredundant
– (default:False
) boolean; ifTrue
, only nonredundant connection coefficients are displayed in case of symmetries
EXAMPLES:
Coefficients of a connection on a 3-dimensional manifold:
sage: M = Manifold(3, 'M', start_index=1) sage: c_xyz.<x,y,z> = M.chart() sage: nab = M.affine_connection('nabla', r'\nabla') sage: nab[1,1,2], nab[3,2,3] = x^2, y*z
By default, only the nonzero connection coefficients are displayed:
sage: nab.display() Gam^x_xy = x^2 Gam^z_yz = y*z sage: latex(nab.display()) \begin{array}{lcl} \Gamma_{ \phantom{\, x} \, x \, y }^{ \, x \phantom{\, x} \phantom{\, y} } & = & x^{2} \\ \Gamma_{ \phantom{\, z} \, y \, z }^{ \, z \phantom{\, y} \phantom{\, z} } & = & y z \end{array}
By default, the displayed connection coefficients are those w.r.t. to the default frame of the connection’s domain, so the above is equivalent to:
sage: nab.display(frame=M.default_frame()) Gam^x_xy = x^2 Gam^z_yz = y*z
Since the default frame is a coordinate frame, coordinate symbols are used to label the indices, but one may ask for integers instead:
sage: M.default_frame() is c_xyz.frame() True sage: nab.display(coordinate_labels=False) Gam^1_12 = x^2 Gam^3_23 = y*z
The index labels can also be customized:
sage: nab.display(index_labels=['(1)', '(2)', '(3)']) Gam^(1)_(1),(2) = x^2 Gam^(3)_(2),(3) = y*z
The symbol ‘Gam’ can be changed:
sage: nab.display(symbol='C', latex_symbol='C') C^x_xy = x^2 C^z_yz = y*z sage: latex(nab.display(symbol='C', latex_symbol='C')) \begin{array}{lcl} C_{ \phantom{\, x} \, x \, y }^{ \, x \phantom{\, x} \phantom{\, y} } & = & x^{2} \\ C_{ \phantom{\, z} \, y \, z }^{ \, z \phantom{\, y} \phantom{\, z} } & = & y z \end{array}
Display of Christoffel symbols, skipping the redundancy associated with the symmetry of the last two indices:
sage: M = Manifold(3, 'R^3', start_index=1) sage: c_spher.<r,th,ph> = M.chart(r'r:(0,+oo) th:(0,pi):\theta ph:(0,2*pi):\phi') sage: g = M.metric('g') sage: g[1,1], g[2,2], g[3,3] = 1, r^2 , (r*sin(th))^2 sage: g.display() g = dr*dr + r^2 dth*dth + r^2*sin(th)^2 dph*dph sage: g.connection().display(only_nonredundant=True) Gam^r_th,th = -r Gam^r_ph,ph = -r*sin(th)^2 Gam^th_r,th = 1/r Gam^th_ph,ph = -cos(th)*sin(th) Gam^ph_r,ph = 1/r Gam^ph_th,ph = cos(th)/sin(th)
By default, the parameter
only_nonredundant
is set toFalse
:sage: g.connection().display() Gam^r_th,th = -r Gam^r_ph,ph = -r*sin(th)^2 Gam^th_r,th = 1/r Gam^th_th,r = 1/r Gam^th_ph,ph = -cos(th)*sin(th) Gam^ph_r,ph = 1/r Gam^ph_th,ph = cos(th)/sin(th) Gam^ph_ph,r = 1/r Gam^ph_ph,th = cos(th)/sin(th)
-
domain
()¶ Return the manifold subset on which the affine connection is defined.
OUTPUT:
- instance of class
DifferentiableManifold
representing the manifold on whichself
is defined.
EXAMPLES:
sage: M = Manifold(3, 'M', start_index=1) sage: c_xyz.<x,y,z> = M.chart() sage: nab = M.affine_connection('nabla', r'\nabla') sage: nab.domain() 3-dimensional differentiable manifold M sage: U = M.open_subset('U', coord_def={c_xyz: x>0}) sage: nabU = U.affine_connection('D') sage: nabU.domain() Open subset U of the 3-dimensional differentiable manifold M
- instance of class
-
restrict
(subdomain)¶ Return the restriction of the connection to some subdomain.
If such restriction has not been defined yet, it is constructed here.
INPUT:
subdomain
– open subsetof the connection’s domain (must be an instance of
DifferentiableManifold
)
OUTPUT:
- instance of
AffineConnection
representing the restriction.
EXAMPLE:
Restriction of a connection on a 2-dimensional manifold:
sage: M = Manifold(2, 'M', start_index=1) sage: c_xy.<x,y> = M.chart() sage: nab = M.affine_connection('nabla', r'\nabla') sage: nab[1,1,2], nab[2,1,1] = x^2, x+y sage: nab[:] [[[0, x^2], [0, 0]], [[x + y, 0], [0, 0]]] sage: U = M.open_subset('U', coord_def={c_xy: x>0}) sage: nabU = nab.restrict(U) ; nabU Affine connection nabla on the Open subset U of the 2-dimensional differentiable manifold M sage: nabU.domain() Open subset U of the 2-dimensional differentiable manifold M sage: nabU[:] [[[0, x^2], [0, 0]], [[x + y, 0], [0, 0]]]
The result is cached:
sage: nab.restrict(U) is nabU True
until the connection is modified:
sage: nab[1,2,2] = -y sage: nab.restrict(U) is nabU False sage: nab.restrict(U)[:] [[[0, x^2], [0, -y]], [[x + y, 0], [0, 0]]]
-
ricci
()¶ Return the connection’s Ricci tensor.
The Ricci tensor is the tensor field
of type (0,2) defined from the Riemann curvature tensor
by
for any vector fields
and
,
being any vector frame and
the dual coframe.
OUTPUT:
- the Ricci tensor
, as an instance of
TensorField
EXAMPLES:
Ricci tensor of an affine connection on a 3-dimensional manifold:
sage: M = Manifold(3, 'M', start_index=1) sage: c_xyz.<x,y,z> = M.chart() sage: nab = M.affine_connection('nabla', r'\nabla') ; nab Affine connection nabla on the 3-dimensional differentiable manifold M sage: nab[1,1,2], nab[3,2,3] = x^2, y*z # Gamma^1_{12} = x^2, Gamma^3_{23} = yz sage: r = nab.ricci() ; r Tensor field of type (0,2) on the 3-dimensional differentiable manifold M sage: r[:] [ 0 2*x 0] [ 0 -z 0] [ 0 0 0]
The result is cached (until the connection is modified via
set_coef()
oradd_coef()
):sage: nab.ricci() is r True
- the Ricci tensor
-
riemann
()¶ Return the connection’s Riemann curvature tensor.
The Riemann curvature tensor is the tensor field
of type (1,3) defined by
for any 1-form
and any vector fields
,
and
.
OUTPUT:
- the Riemann curvature tensor
, as an instance of
TensorField
EXAMPLES:
Curvature of an affine connection on a 3-dimensional manifold:
sage: M = Manifold(3, 'M', start_index=1) sage: c_xyz.<x,y,z> = M.chart() sage: nab = M.affine_connection('nabla', r'\nabla') ; nab Affine connection nabla on the 3-dimensional differentiable manifold M sage: nab[1,1,2], nab[3,2,3] = x^2, y*z # Gamma^1_{12} = x^2, Gamma^3_{23} = yz sage: r = nab.riemann() ; r Tensor field of type (1,3) on the 3-dimensional differentiable manifold M sage: r.parent() Free module T^(1,3)(M) of type-(1,3) tensors fields on the 3-dimensional differentiable manifold M
By construction, the Riemann tensor is antisymmetric with respect to its last two arguments (denoted
and
in the definition above), which are at positions 2 and 3 (the first argument being at position 0):
sage: r.symmetries() no symmetry; antisymmetry: (2, 3)
The components:
sage: r[:] [[[[0, 2*x, 0], [-2*x, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 0]]], [[[0, 0, 0], [0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 0]]], [[[0, 0, 0], [0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, z], [0, -z, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 0]]]]
The result is cached (until the connection is modified via
set_coef()
oradd_coef()
):sage: nab.riemann() is r True
Another example: Riemann curvature tensor of some connection on a non-parallelizable 2-dimensional manifold:
sage: M = Manifold(2, 'M') sage: U = M.open_subset('U') ; V = M.open_subset('V') sage: M.declare_union(U,V) # M is the union of U and V sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart() sage: transf = c_xy.transition_map(c_uv, (x+y, x-y), intersection_name='W', ....: restrictions1= x>0, restrictions2= u+v>0) sage: inv = transf.inverse() sage: W = U.intersection(V) sage: eU = c_xy.frame() ; eV = c_uv.frame() sage: c_xyW = c_xy.restrict(W) ; c_uvW = c_uv.restrict(W) sage: eUW = c_xyW.frame() ; eVW = c_uvW.frame() sage: nab = M.affine_connection('nabla', r'\nabla') sage: nab[0,0,0], nab[0,1,0], nab[1,0,1] = x, x-y, x*y sage: for i in M.irange(): ....: for j in M.irange(): ....: for k in M.irange(): ....: nab.add_coef(eV)[i,j,k] = nab.coef(eVW)[i,j,k,c_uvW].expr() ....: sage: r = nab.riemann() ; r Tensor field of type (1,3) on the 2-dimensional differentiable manifold M sage: r.parent() Module T^(1,3)(M) of type-(1,3) tensors fields on the 2-dimensional differentiable manifold M sage: r.display(eU) (x^2*y - x*y^2) d/dx*dx*dx*dy + (-x^2*y + x*y^2) d/dx*dx*dy*dx + d/dx*dy*dx*dy - d/dx*dy*dy*dx - (x^2 - 1)*y d/dy*dx*dx*dy + (x^2 - 1)*y d/dy*dx*dy*dx + (-x^2*y + x*y^2) d/dy*dy*dx*dy + (x^2*y - x*y^2) d/dy*dy*dy*dx sage: r.display(eV) (1/32*u^3 - 1/32*u*v^2 - 1/32*v^3 + 1/32*(u^2 + 4)*v - 1/8*u - 1/4) d/du*du*du*dv + (-1/32*u^3 + 1/32*u*v^2 + 1/32*v^3 - 1/32*(u^2 + 4)*v + 1/8*u + 1/4) d/du*du*dv*du + (1/32*u^3 - 1/32*u*v^2 + 3/32*v^3 - 1/32*(3*u^2 - 4)*v - 1/8*u + 1/4) d/du*dv*du*dv + (-1/32*u^3 + 1/32*u*v^2 - 3/32*v^3 + 1/32*(3*u^2 - 4)*v + 1/8*u - 1/4) d/du*dv*dv*du + (-1/32*u^3 + 1/32*u*v^2 + 5/32*v^3 - 1/32*(5*u^2 + 4)*v + 1/8*u - 1/4) d/dv*du*du*dv + (1/32*u^3 - 1/32*u*v^2 - 5/32*v^3 + 1/32*(5*u^2 + 4)*v - 1/8*u + 1/4) d/dv*du*dv*du + (-1/32*u^3 + 1/32*u*v^2 + 1/32*v^3 - 1/32*(u^2 + 4)*v + 1/8*u + 1/4) d/dv*dv*du*dv + (1/32*u^3 - 1/32*u*v^2 - 1/32*v^3 + 1/32*(u^2 + 4)*v - 1/8*u - 1/4) d/dv*dv*dv*du
The same computation parallelized on 2 cores:
sage: Parallelism().set(nproc=2) sage: r_backup = r sage: nab = M.affine_connection('nabla', r'\nabla') sage: nab[0,0,0], nab[0,1,0], nab[1,0,1] = x, x-y, x*y sage: for i in M.irange(): ....: for j in M.irange(): ....: for k in M.irange(): ....: nab.add_coef(eV)[i,j,k] = nab.coef(eVW)[i,j,k,c_uvW].expr() ....: sage: r = nab.riemann() ; r Tensor field of type (1,3) on the 2-dimensional differentiable manifold M sage: r.parent() Module T^(1,3)(M) of type-(1,3) tensors fields on the 2-dimensional differentiable manifold M sage: r == r_backup True sage: Parallelism().set(nproc=1) # switch off parallelization
- the Riemann curvature tensor
-
set_coef
(frame=None)¶ Return the connection coefficients in a given frame for assignment.
See method
coef()
for details about the definition of the connection coefficents.The connection coefficients with respect to other frames are deleted, in order to avoid any inconsistency. To keep them, use the method
add_coef()
instead.INPUT:
frame
– (default:None
) vector frame in which the connection coefficients are defined; ifNone
, the default frame of the connection’s domain is assumed.
OUTPUT:
- connection coefficients in the given frame, as an instance of the
class
Components
; if such connection coefficients did not exist previously, they are created. See methodcoef()
for the storage convention of the connection coefficients.
EXAMPLES:
Setting the coefficients of an affine connection w.r.t. some coordinate frame:
sage: M = Manifold(2, 'M', start_index=1) sage: X.<x,y> = M.chart() sage: nab = M.affine_connection('nabla', latex_name=r'\nabla') sage: eX = X.frame(); eX Coordinate frame (M, (d/dx,d/dy)) sage: nab.set_coef(eX) 3-indices components w.r.t. Coordinate frame (M, (d/dx,d/dy)) sage: nab.set_coef(eX)[1,2,1] = x*y sage: nab.display(eX) Gam^x_yx = x*y
Since
eX
is the manifold’s default vector frame, its mention may be omitted:sage: nab.set_coef()[1,2,1] = x*y sage: nab.set_coef() 3-indices components w.r.t. Coordinate frame (M, (d/dx,d/dy)) sage: nab.set_coef()[1,2,1] = x*y sage: nab.display() Gam^x_yx = x*y
To set the coefficients in the default frame, one can even bypass the method
set_coef()
and call directly the operator[]
on the connection object:sage: nab[1,2,1] = x*y sage: nab.display() Gam^x_yx = x*y
Setting the connection coefficients w.r.t. to another vector frame:
sage: e = M.vector_frame('e') sage: nab.set_coef(e) 3-indices components w.r.t. Vector frame (M, (e_1,e_2)) sage: nab.set_coef(e)[2,1,1] = x+y sage: nab.set_coef(e)[2,1,2] = x-y sage: nab.display(e) Gam^2_11 = x + y Gam^2_12 = x - y
The coefficients w.r.t. the frame
eX
have been deleted:sage: nab.display(eX) Traceback (most recent call last): ... ValueError: no common frame found for the computation
To keep them, use the method
add_coef()
instead.
-
torsion
()¶ Return the connection’s torsion tensor.
The torsion tensor is the tensor field
of type (1,2) defined by
for any 1-form
and any vector fields
and
.
OUTPUT:
- the torsion tensor
, as an instance of
TensorField
EXAMPLES:
Torsion of an affine connection on a 3-dimensional manifold:
sage: M = Manifold(3, 'M', start_index=1) sage: c_xyz.<x,y,z> = M.chart() sage: nab = M.affine_connection('nabla', r'\nabla') sage: nab[1,1,2], nab[3,2,3] = x^2, y*z # Gamma^1_{12} = x^2, Gamma^3_{23} = yz sage: t = nab.torsion() ; t Tensor field of type (1,2) on the 3-dimensional differentiable manifold M sage: t.symmetries() no symmetry; antisymmetry: (1, 2) sage: t[:] [[[0, -x^2, 0], [x^2, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, -y*z], [0, y*z, 0]]]
The torsion expresses the lack of commutativity of two successive derivatives of a scalar field:
sage: f = M.scalar_field(x*z^2 + y^2 - z^2, name='f') sage: DDf = nab(nab(f)) ; DDf Tensor field nabla(df) of type (0,2) on the 3-dimensional differentiable manifold M sage: DDf.antisymmetrize()[:] # two successive derivatives do not commute: [ 0 -1/2*x^2*z^2 0] [ 1/2*x^2*z^2 0 -(x - 1)*y*z^2] [ 0 (x - 1)*y*z^2 0] sage: 2*DDf.antisymmetrize() == nab.torsion().contract(0,nab(f)) True
The above identity is the standard formula
where the
‘s are the components of the torsion tensor.
The result is cached:
sage: nab.torsion() is t True
as long as the connection remains unchanged:
sage: nab[2,1,3] = 1+x # changing the connection sage: nab.torsion() is t # a new computation of the torsion has been made False sage: (nab.torsion() - t).display() (-x - 1) d/dy*dx*dz + (x + 1) d/dy*dz*dx
Another example: torsion of some connection on a non-parallelizable 2-dimensional manifold:
sage: M = Manifold(2, 'M') sage: U = M.open_subset('U') ; V = M.open_subset('V') sage: M.declare_union(U,V) # M is the union of U and V sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart() sage: transf = c_xy.transition_map(c_uv, (x+y, x-y), intersection_name='W', ....: restrictions1= x>0, restrictions2= u+v>0) sage: inv = transf.inverse() sage: W = U.intersection(V) sage: eU = c_xy.frame() ; eV = c_uv.frame() sage: c_xyW = c_xy.restrict(W) ; c_uvW = c_uv.restrict(W) sage: eUW = c_xyW.frame() ; eVW = c_uvW.frame() sage: nab = M.affine_connection('nabla', r'\nabla') sage: nab[0,0,0], nab[0,1,0], nab[1,0,1] = x, x-y, x*y sage: for i in M.irange(): ....: for j in M.irange(): ....: for k in M.irange(): ....: nab.add_coef(eV)[i,j,k] = nab.coef(eVW)[i,j,k,c_uvW].expr() ....: sage: t = nab.torsion() ; t Tensor field of type (1,2) on the 2-dimensional differentiable manifold M sage: t.parent() Module T^(1,2)(M) of type-(1,2) tensors fields on the 2-dimensional differentiable manifold M sage: t[eU,:] [[[0, x - y], [-x + y, 0]], [[0, -x*y], [x*y, 0]]] sage: t[eV,:] [[[0, 1/8*u^2 - 1/8*v^2 - 1/2*v], [-1/8*u^2 + 1/8*v^2 + 1/2*v, 0]], [[0, -1/8*u^2 + 1/8*v^2 - 1/2*v], [1/8*u^2 - 1/8*v^2 + 1/2*v, 0]]]
Check of the torsion formula:
sage: f = M.scalar_field({c_xy: (x+y)^2, c_uv: u^2}, name='f') sage: DDf = nab(nab(f)) ; DDf Tensor field nabla(df) of type (0,2) on the 2-dimensional differentiable manifold M sage: DDf.antisymmetrize().display(eU) (-x^2*y - (x + 1)*y^2 + x^2) dx/\dy sage: DDf.antisymmetrize().display(eV) (1/8*u^3 - 1/8*u*v^2 - 1/2*u*v) du/\dv sage: 2*DDf.antisymmetrize() == nab(f).contract(nab.torsion()) True
- the torsion tensor
-
torsion_form
(i, frame=None)¶ Return the torsion 2-form corresponding to the given index and vector frame.
The torsion 2-forms with respect to the frame
are the
2-forms
defined by
where
is the connection’s torsion tensor (cf.
torsion()
),is the coframe dual to
and
is a generic pair of vectors.
INPUT:
i
– index identifying the 2-formframe
– (default:None
) vector frame relative to which the torsion 2-forms are defined; ifNone
, the default frame of the connection’s domain is assumed.
OUTPUT:
- the 2-form
, as an instance of
DiffForm
EXAMPLES:
Torsion 2-forms on a 3-dimensional manifold:
sage: M = Manifold(3, 'M', start_index=1) sage: c_xyz.<x,y,z> = M.chart() sage: nab = M.affine_connection('nabla', r'\nabla') sage: nab[1,1,1], nab[1,1,2], nab[1,1,3] = x*y*z, x^2, -y*z sage: nab[1,2,3], nab[1,3,1], nab[1,3,2] = -x^3, y^2*z, y^2-x^2 sage: nab[2,1,1], nab[2,1,2], nab[2,2,1] = z^2, x*y*z^2, -x^2 sage: nab[2,3,1], nab[2,3,3], nab[3,1,2] = x^2+y^2+z^2, y^2-z^2, x*y+z^2 sage: nab[3,2,1], nab[3,2,2], nab[3,3,3] = x*y+z, z^3 -y^2, x*z^2 - z*y^2 sage: nab.torsion_form(1) 2-form torsion (1) of connection nabla w.r.t. Coordinate frame (M, (d/dx,d/dy,d/dz)) on the 3-dimensional differentiable manifold M sage: nab.torsion_form(1)[:] [ 0 -x^2 (y^2 + y)*z] [ x^2 0 x^3 - x^2 + y^2] [ -(y^2 + y)*z -x^3 + x^2 - y^2 0]
Torsion 2-forms w.r.t. a non-holonomic frame:
sage: ch_basis = M.automorphism_field() sage: ch_basis[1,1], ch_basis[2,2], ch_basis[3,3] = y, z, x sage: e = M.default_frame().new_frame(ch_basis, 'e') sage: e[1][:], e[2][:], e[3][:] ([y, 0, 0], [0, z, 0], [0, 0, x]) sage: ef = e.coframe() sage: ef[1][:], ef[2][:], ef[3][:] ([1/y, 0, 0], [0, 1/z, 0], [0, 0, 1/x]) sage: nab.torsion_form(1, e) 2-form torsion (1) of connection nabla w.r.t. Vector frame (M, (e_1,e_2,e_3)) on the 3-dimensional differentiable manifold M sage: nab.torsion_form(1, e).comp(e)[:] [ 0 -x^2*z (x*y^2 + x*y)*z] [ x^2*z 0 (x^4 - x^3 + x*y^2)*z/y] [ -(x*y^2 + x*y)*z -(x^4 - x^3 + x*y^2)*z/y 0]
Cartan’s first structure equation is
where the
‘s are the connection 1-forms (cf.
connection_form()
). Let us check it on the frame e:sage: for i in M.irange(): # long time ....: nab.torsion_form(i, e) == ef[i].exterior_derivative() + \ ....: sum(nab.connection_form(i,j,e).wedge(ef[j]) for j in M.irange()) ....: True True True
- is