Tensor Fields

The class TensorField implements tensor fields on differentiable manifolds. The derived class TensorFieldParal is devoted to tensor fields with values on parallelizable manifolds.

Various derived classes of TensorField are devoted to specific tensor fields:

  • VectorField for vector fields (rank-1 contravariant tensor fields)
  • AutomorphismField for fields of tangent-space automorphisms
  • DiffForm for differential forms (fully antisymmetric covariant tensor fields)
  • MultivectorField for multivector fields (fully antisymmetric contravariant tensor fields)

AUTHORS:

  • Eric Gourgoulhon, Michal Bejger (2013-2015) : initial version
  • Travis Scrimshaw (2016): review tweaks
  • Eric Gourgoulhon (2018): operators divergence, Laplacian and d’Alembertian; method TensorField.along()

REFERENCES:

class sage.manifolds.differentiable.tensorfield.TensorField(vector_field_module, tensor_type, name=None, latex_name=None, sym=None, antisym=None, parent=None)

Bases: sage.structure.element.ModuleElement

Tensor field along a differentiable manifold.

An instance of this class is a tensor field along a differentiable manifold \(U\) with values on a differentiable manifold \(M\), via a differentiable map \(\Phi: U \rightarrow M\). More precisely, given two non-negative integers \(k\) and \(l\) and a differentiable map

\[\Phi:\ U \longrightarrow M,\]

a tensor field of type \((k,l)\) along \(U\) with values on \(M\) is a differentiable map

\[t:\ U \longrightarrow T^{(k,l)}M\]

(where \(T^{(k,l)}M\) is the tensor bundle of type \((k,l)\) over \(M\)) such that

\[\forall p \in U,\ t(p) \in T^{(k,l)}(T_q M)\]

i.e. \(t(p)\) is a tensor of type \((k,l)\) on the tangent space \(T_q M\) at the point \(q = \Phi(p)\), that is to say a multilinear map

\[t(p):\ \underbrace{T_q^*M\times\cdots\times T_q^*M}_{k\ \; \mbox{times}} \times \underbrace{T_q M\times\cdots\times T_q M}_{l\ \; \mbox{times}} \longrightarrow K\]

where \(T_q^* M\) is the dual vector space to \(T_q M\) and \(K\) is the topological field over which the manifold \(M\) is defined. The integer \(k+l\) is called the tensor rank.

The standard case of a tensor field on a differentiable manifold corresponds to \(U=M\) and \(\Phi = \mathrm{Id}_M\). Other common cases are \(\Phi\) being an immersion and \(\Phi\) being a curve in \(M\) (\(U\) is then an open interval of \(\RR\)).

If \(M\) is parallelizable, the class TensorFieldParal should be used instead.

This is a Sage element class, the corresponding parent class being TensorFieldModule.

INPUT:

  • vector_field_module – module \(\mathfrak{X}(U,\Phi)\) of vector fields along \(U\) associated with the map \(\Phi: U \rightarrow M\) (cf. VectorFieldModule)
  • tensor_type – pair \((k,l)\) with \(k\) being the contravariant rank and \(l\) the covariant rank
  • name – (default: None) name given to the tensor field
  • latex_name – (default: None) LaTeX symbol to denote the tensor field; if none is provided, the LaTeX symbol is set to name
  • sym – (default: None) a symmetry or a list of symmetries among the tensor arguments: each symmetry is described by a tuple containing the positions of the involved arguments, with the convention position = 0 for the first argument; for instance:
    • sym = (0,1) for a symmetry between the 1st and 2nd arguments
    • sym = [(0,2), (1,3,4)] for a symmetry between the 1st and 3rd arguments and a symmetry between the 2nd, 4th and 5th arguments.
  • antisym – (default: None) antisymmetry or list of antisymmetries among the arguments, with the same convention as for sym
  • parent – (default: None) some specific parent (e.g. exterior power for differential forms); if None, vector_field_module.tensor_module(k,l) is used

EXAMPLES:

Tensor field of type (0,2) on the sphere \(S^2\):

sage: M = Manifold(2, 'S^2') # the 2-dimensional sphere S^2
sage: U = M.open_subset('U') # complement of the North pole
sage: c_xy.<x,y> = U.chart() # stereographic coordinates from the North pole
sage: V = M.open_subset('V') # complement of the South pole
sage: c_uv.<u,v> = V.chart() # stereographic coordinates from the South pole
sage: M.declare_union(U,V)   # S^2 is the union of U and V
sage: xy_to_uv = c_xy.transition_map(c_uv, (x/(x^2+y^2), y/(x^2+y^2)),
....:                 intersection_name='W', restrictions1= x^2+y^2!=0,
....:                 restrictions2= u^2+v^2!=0)
sage: uv_to_xy = xy_to_uv.inverse()
sage: W = U.intersection(V)
sage: t = M.tensor_field(0,2, name='t') ; t
Tensor field t of type (0,2) on the 2-dimensional differentiable
 manifold S^2
sage: t.parent()
Module T^(0,2)(S^2) of type-(0,2) tensors fields on the 2-dimensional
 differentiable manifold S^2
sage: t.parent().category()
Category of modules over Algebra of differentiable scalar fields on the
 2-dimensional differentiable manifold S^2

The parent of \(t\) is not a free module, for the sphere \(S^2\) is not parallelizable:

sage: isinstance(t.parent(), FiniteRankFreeModule)
False

To fully define \(t\), we have to specify its components in some vector frames defined on subsets of \(S^2\); let us start by the open subset \(U\):

sage: eU = c_xy.frame()
sage: t[eU,:] = [[1,0], [-2,3]]
sage: t.display(eU)
t = dx*dx - 2 dy*dx + 3 dy*dy

To set the components of \(t\) on \(V\) consistently, we copy the expressions of the components in the common subset \(W\):

sage: eV = c_uv.frame()
sage: eVW = eV.restrict(W)
sage: c_uvW = c_uv.restrict(W)
sage: t[eV,0,0] = t[eVW,0,0,c_uvW].expr()  # long time
sage: t[eV,0,1] = t[eVW,0,1,c_uvW].expr()  # long time
sage: t[eV,1,0] = t[eVW,1,0,c_uvW].expr()  # long time
sage: t[eV,1,1] = t[eVW,1,1,c_uvW].expr()  # long time

Actually, the above operation can by performed in a single line by means of the method add_comp_by_continuation():

sage: t.add_comp_by_continuation(eV, W, chart=c_uv)  # long time

At this stage, \(t\) is fully defined, having components in frames eU and eV and the union of the domains of eU and eV being the whole manifold:

sage: t.display(eV)  # long time
t = (u^4 - 4*u^3*v + 10*u^2*v^2 + 4*u*v^3 + v^4)/(u^8 + 4*u^6*v^2 + 6*u^4*v^4 + 4*u^2*v^6 + v^8) du*du
 - 4*(u^3*v + 2*u^2*v^2 - u*v^3)/(u^8 + 4*u^6*v^2 + 6*u^4*v^4 + 4*u^2*v^6 + v^8) du*dv
 + 2*(u^4 - 2*u^3*v - 2*u^2*v^2 + 2*u*v^3 + v^4)/(u^8 + 4*u^6*v^2 + 6*u^4*v^4 + 4*u^2*v^6 + v^8) dv*du
 + (3*u^4 + 4*u^3*v - 2*u^2*v^2 - 4*u*v^3 + 3*v^4)/(u^8 + 4*u^6*v^2 + 6*u^4*v^4 + 4*u^2*v^6 + v^8) dv*dv

Let us consider two vector fields, \(a\) and \(b\), on \(S^2\):

sage: a = M.vector_field(name='a')
sage: a[eU,:] = [-y,x]
sage: a.add_comp_by_continuation(eV, W, chart=c_uv)
sage: a.display(eV)
a = -v d/du + u d/dv
sage: b = M.vector_field(name='b')
sage: b[eU,:] = [y,-1]
sage: b.add_comp_by_continuation(eV, W, chart=c_uv)
sage: b.display(eV)
b = ((2*u + 1)*v^3 + (2*u^3 - u^2)*v)/(u^2 + v^2) d/du
 - (u^4 - v^4 + 2*u*v^2)/(u^2 + v^2) d/dv

As a tensor field of type \((0,2)\), \(t\) acts on the pair \((a,b)\), resulting in a scalar field:

sage: f = t(a,b); f
Scalar field t(a,b) on the 2-dimensional differentiable manifold S^2
sage: f.display()  # long time
t(a,b): S^2 --> R
on U: (x, y) |--> -2*x*y - y^2 - 3*x
on V: (u, v) |--> -(3*u^3 + (3*u + 1)*v^2 + 2*u*v)/(u^4 + 2*u^2*v^2 + v^4)

The vectors can be defined only on subsets of \(S^2\), the domain of the result is then the common subset:

sage: s = t(a.restrict(U), b) ; s  # long time
Scalar field t(a,b) on the Open subset U of the 2-dimensional
 differentiable manifold S^2
sage: s.display()  # long time
t(a,b): U --> R
   (x, y) |--> -2*x*y - y^2 - 3*x
on W: (u, v) |--> -(3*u^3 + (3*u + 1)*v^2 + 2*u*v)/(u^4 + 2*u^2*v^2 + v^4)
sage: s = t(a.restrict(U), b.restrict(W)) ; s  # long time
Scalar field t(a,b) on the Open subset W of the 2-dimensional
 differentiable manifold S^2
sage: s.display()  # long time
t(a,b): W --> R
   (x, y) |--> -2*x*y - y^2 - 3*x
   (u, v) |--> -(3*u^3 + (3*u + 1)*v^2 + 2*u*v)/(u^4 + 2*u^2*v^2 + v^4)

The tensor itself can be defined only on some open subset of \(S^2\), yielding a result whose domain is this subset:

sage: s = t.restrict(V)(a,b); s  # long time
Scalar field t(a,b) on the Open subset V of the 2-dimensional
 differentiable manifold S^2
sage: s.display()  # long time
t(a,b): V --> R
   (u, v) |--> -(3*u^3 + (3*u + 1)*v^2 + 2*u*v)/(u^4 + 2*u^2*v^2 + v^4)
on W: (x, y) |--> -2*x*y - y^2 - 3*x

Tests regarding the multiplication by a scalar field:

sage: f = M.scalar_field({c_xy: 1/(1+x^2+y^2),
....:                     c_uv: (u^2 + v^2)/(u^2 + v^2 + 1)}, name='f')
sage: t.parent().base_ring() is f.parent()
True
sage: s = f*t; s  # long time
Tensor field of type (0,2) on the 2-dimensional differentiable
 manifold S^2
sage: s[[0,0]] == f*t[[0,0]]  # long time
True
sage: s.restrict(U) == f.restrict(U) * t.restrict(U)  # long time
True
sage: s = f*t.restrict(U); s
Tensor field of type (0,2) on the Open subset U of the 2-dimensional
 differentiable manifold S^2
sage: s.restrict(U) == f.restrict(U) * t.restrict(U)
True

Same examples with SymPy as the symbolic engine

From now on, we ask that all symbolic calculus on manifold \(M\) are performed by SymPy:

sage: M.set_calculus_method('sympy')

We define the tensor \(t\) as above:

sage: t = M.tensor_field(0,2, name='t')
sage: t[eU,:] = [[1,0], [-2,3]]
sage: t.display(eU)
t = dx*dx - 2 dy*dx + 3 dy*dy
sage: t.add_comp_by_continuation(eV, W, chart=c_uv)  # long time
sage: t.display(eV)  # long time
t = (u**4 - 4*u**3*v + 10*u**2*v**2 + 4*u*v**3 + v**4)/(u**8 +
 4*u**6*v**2 + 6*u**4*v**4 + 4*u**2*v**6 + v**8) du*du +
 4*u*v*(-u**2 - 2*u*v + v**2)/(u**8 + 4*u**6*v**2 + 6*u**4*v**4
 + 4*u**2*v**6 + v**8) du*dv + 2*(u**4 - 2*u**3*v - 2*u**2*v**2
 + 2*u*v**3 + v**4)/(u**8 + 4*u**6*v**2 + 6*u**4*v**4 +
 4*u**2*v**6 + v**8) dv*du + (3*u**4 + 4*u**3*v - 2*u**2*v**2 -
 4*u*v**3 + 3*v**4)/(u**8 + 4*u**6*v**2 + 6*u**4*v**4 +
 4*u**2*v**6 + v**8) dv*dv

The default coordinate representations of tensor components are now SymPy objects:

sage: t[eV,1,1,c_uv].expr() # long time
(3*u**4 + 4*u**3*v - 2*u**2*v**2 - 4*u*v**3 + 3*v**4)/(u**8 +
 4*u**6*v**2 + 6*u**4*v**4 + 4*u**2*v**6 + v**8)
sage: type(t[eV,1,1,c_uv].expr()) # long time
<class 'sympy.core.mul.Mul'>

Let us consider two vector fields, \(a\) and \(b\), on \(S^2\):

sage: a = M.vector_field(name='a')
sage: a[eU,:] = [-y,x]
sage: a.add_comp_by_continuation(eV, W, chart=c_uv)
sage: a.display(eV)
a = -v d/du + u d/dv
sage: b = M.vector_field(name='b')
sage: b[eU,:] = [y,-1]
sage: b.add_comp_by_continuation(eV, W, chart=c_uv)
sage: b.display(eV)
b = v*(2*u**3 - u**2 + 2*u*v**2 + v**2)/(u**2 + v**2) d/du
    + (-u**4 - 2*u*v**2 + v**4)/(u**2 + v**2) d/dv

As a tensor field of type \((0,2)\), \(t\) acts on the pair \((a,b)\), resulting in a scalar field:

sage: f = t(a,b)
sage: f.display()  # long time
t(a,b): S^2 --> R
on U: (x, y) |--> -2*x*y - 3*x - y**2
on V: (u, v) |--> -(3*u**3 + 3*u*v**2 + 2*u*v + v**2)/(u**4 + 2*u**2*v**2 + v**4)

The vectors can be defined only on subsets of \(S^2\), the domain of the result is then the common subset:

sage: s = t(a.restrict(U), b)
sage: s.display()  # long time
t(a,b): U --> R
   (x, y) |--> -2*x*y - 3*x - y**2
on W: (u, v) |--> -(3*u**3 + 3*u*v**2 + 2*u*v + v**2)/(u**4 + 2*u**2*v**2 + v**4)
sage: s = t(a.restrict(U), b.restrict(W))  # long time
sage: s.display()  # long time
t(a,b): W --> R
   (x, y) |--> -2*x*y - 3*x - y**2
   (u, v) |--> -(3*u**3 + 3*u*v**2 + 2*u*v + v**2)/(u**4 + 2*u**2*v**2 + v**4)

The tensor itself can be defined only on some open subset of \(S^2\), yielding a result whose domain is this subset:

sage: s = t.restrict(V)(a,b)  # long time
sage: s.display()  # long time
t(a,b): V --> R
   (u, v) |--> -(3*u**3 + 3*u*v**2 + 2*u*v + v**2)/(u**4 + 2*u**2*v**2 + v**4)
on W: (x, y) |--> -2*x*y - 3*x - y**2

Tests regarding the multiplication by a scalar field:

sage: f = M.scalar_field({c_xy: 1/(1+x^2+y^2),
....:                     c_uv: (u^2 + v^2)/(u^2 + v^2 + 1)}, name='f')
sage: s = f*t # long time
sage: s[[0,0]] == f*t[[0,0]]  # long time
True
sage: s.restrict(U) == f.restrict(U) * t.restrict(U)  # long time
True
sage: s = f*t.restrict(U)
sage: s.restrict(U) == f.restrict(U) * t.restrict(U)
True
add_comp(basis=None)

Return the components of self in a given vector frame for assignment.

The components with respect to other frames having the same domain as the provided vector frame are kept. To delete them, use the method set_comp() instead.

INPUT:

  • basis – (default: None) vector frame in which the components are defined; if None, the components are assumed to refer to the tensor field domain’s default frame

OUTPUT:

  • components in the given frame, as a Components; if such components did not exist previously, they are created

EXAMPLES:

sage: M = Manifold(2, 'M') # the 2-dimensional sphere S^2
sage: U = M.open_subset('U') # complement of the North pole
sage: c_xy.<x,y> = U.chart() # stereographic coordinates from the North pole
sage: V = M.open_subset('V') # complement of the South pole
sage: c_uv.<u,v> = V.chart() # stereographic coordinates from the South pole
sage: M.declare_union(U,V)   # S^2 is the union of U and V
sage: e_uv = c_uv.frame()
sage: t = M.tensor_field(1, 2, name='t')
sage: t.add_comp(e_uv)
3-indices components w.r.t. Coordinate frame (V, (d/du,d/dv))
sage: t.add_comp(e_uv)[1,0,1] = u+v
sage: t.display(e_uv)
t = (u + v) d/dv*du*dv

Setting the components in a new frame:

sage: e = V.vector_frame('e')
sage: t.add_comp(e)
3-indices components w.r.t. Vector frame (V, (e_0,e_1))
sage: t.add_comp(e)[0,1,1] = u*v
sage: t.display(e)
t = u*v e_0*e^1*e^1

The components with respect to e_uv are kept:

sage: t.display(e_uv)
t = (u + v) d/dv*du*dv
add_comp_by_continuation(frame, subdomain, chart=None)

Set components with respect to a vector frame by continuation of the coordinate expression of the components in a subframe.

The continuation is performed by demanding that the components have the same coordinate expression as those on the restriction of the frame to a given subdomain.

INPUT:

  • frame – vector frame \(e\) in which the components are to be set
  • subdomain – open subset of \(e\)‘s domain in which the components are known or can be evaluated from other components
  • chart – (default: None) coordinate chart on \(e\)‘s domain in which the extension of the expression of the components is to be performed; if None, the default’s chart of \(e\)‘s domain is assumed

EXAMPLES:

Components of a vector field on the sphere \(S^2\):

sage: M = Manifold(2, 'S^2', start_index=1)
sage: # The two open subsets covered by stereographic coordinates (North and South):
sage: U = M.open_subset('U') ; V = M.open_subset('V')
sage: M.declare_union(U,V)   # S^2 is the union of U and V
sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart() # stereographic coordinates
sage: transf = c_xy.transition_map(c_uv, (x/(x^2+y^2), y/(x^2+y^2)),
....:             intersection_name='W', restrictions1= x^2+y^2!=0,
....:             restrictions2= u^2+v^2!=0)
sage: inv = transf.inverse()
sage: W = U.intersection(V) # The complement of the two poles
sage: eU = c_xy.frame() ; eV = c_uv.frame()
sage: a = M.vector_field('a')
sage: a[eU,:] = [x, 2+y]

At this stage, the vector field has been defined only on the open subset U (through its components in the frame eU):

sage: a.display(eU)
a = x d/dx + (y + 2) d/dy

The components with respect to the restriction of eV to the common subdomain W, in terms of the (u,v) coordinates, are obtained by a change-of-frame formula on W:

sage: a.display(eV.restrict(W), c_uv.restrict(W))
a = (-4*u*v - u) d/du + (2*u^2 - 2*v^2 - v) d/dv

The continuation consists in extending the definition of the vector field to the whole open subset V by demanding that the components in the frame eV have the same coordinate expression as the above one:

sage: a.add_comp_by_continuation(eV, W, chart=c_uv)

We have then:

sage: a.display(eV)
a = (-4*u*v - u) d/du + (2*u^2 - 2*v^2 - v) d/dv

and \(a\) is defined on the entire manifold \(S^2\).

add_expr_from_subdomain(frame, subdomain)

Add an expression to an existing component from a subdomain.

INPUT:

  • frame – vector frame \(e\) in which the components are to be set
  • subdomain – open subset of \(e\)‘s domain in which the components have additional expressions.

EXAMPLES:

We are going to consider a vector field in \(\RR^3\) along the 2-sphere:

sage: M = Manifold(3, 'M', structure="Riemannian")
sage: S = Manifold(2, 'S', structure="Riemannian")
sage: E.<X,Y,Z> = M.chart()

Let us define S in terms of stereographic charts:

sage: U = S.open_subset('U')
sage: V = S.open_subset('V')
sage: S.declare_union(U,V)
sage: stereoN.<x,y> = U.chart()
sage: stereoS.<xp,yp> = V.chart("xp:x' yp:y'")
sage: stereoN_to_S = stereoN.transition_map(stereoS,
....:                                 (x/(x^2+y^2), y/(x^2+y^2)),
....:                                 intersection_name='W',
....:                                 restrictions1= x^2+y^2!=0,
....:                                 restrictions2= xp^2+yp^2!=0)
sage: stereoS_to_N = stereoN_to_S.inverse()
sage: W = U.intersection(V)
sage: stereoN_W = stereoN.restrict(W)
sage: stereoS_W = stereoS.restrict(W)

The embedding of \(S^2\) in \(\RR^3\):

sage: phi = S.diff_map(M, {(stereoN, E): [2*x/(1+x^2+y^2),
....:                                     2*y/(1+x^2+y^2),
....:                                     (x^2+y^2-1)/(1+x^2+y^2)],
....:                        (stereoS, E): [2*xp/(1+xp^2+yp^2),
....:                                       2*yp/(1+xp^2+yp^2),
....:                               (1-xp^2-yp^2)/(1+xp^2+yp^2)]},
....:                   name='Phi', latex_name=r'\Phi')

To define a vector field v along S taking its values in M, we first set the components on U:

sage: v = M.vector_field('v').along(phi)
sage: vU = v.restrict(U)
sage: vU[:] = [x,y,x**2+y**2]

But because M is parallelizable, these components can be extended to S itself:

sage: v.add_comp_by_continuation(E.frame().along(phi), U)

One can see that v is not yet fully defined: the components (scalar fields) do not have values on the whole manifold:

sage: sorted(v._components.values())[0]._comp[(0,)].display()
S --> R
on U: (x, y) |--> x

To fix that, we first extend the components from W to V using add_comp_by_continuation():

sage: v.add_comp_by_continuation(E.frame().along(phi).restrict(V),
....:                            W, stereoS)

Then, the expression on the subdomain V is added to the already known components on S by:

sage: v.add_expr_from_subdomain(E.frame().along(phi), V)

The definition of v is now complete:

sage: sorted(v._components.values())[0]._comp[(2,)].display()
S --> R
on U: (x, y) |--> x^2 + y^2
on V: (xp, yp) |--> 1/(xp^2 + yp^2)
along(mapping)

Return the tensor field deduced from self via a differentiable map, the codomain of which is included in the domain of self.

More precisely, if self is a tensor field \(t\) on \(M\) and if \(\Phi: U \rightarrow M\) is a differentiable map from some differentiable manifold \(U\) to \(M\), the returned object is a tensor field \(\tilde t\) along \(U\) with values on \(M\) such that

\[\forall p \in U,\ \tilde t(p) = t(\Phi(p)).\]

INPUT:

  • mapping – differentiable map \(\Phi: U \rightarrow M\)

OUTPUT:

  • tensor field \(\tilde t\) along \(U\) defined above.

EXAMPLES:

Let us consider the 2-dimensional sphere \(S^2\):

sage: M = Manifold(2, 'S^2') # the 2-dimensional sphere S^2
sage: U = M.open_subset('U') # complement of the North pole
sage: c_xy.<x,y> = U.chart() # stereographic coordinates from the North pole
sage: V = M.open_subset('V') # complement of the South pole
sage: c_uv.<u,v> = V.chart() # stereographic coordinates from the South pole
sage: M.declare_union(U,V)   # S^2 is the union of U and V
sage: xy_to_uv = c_xy.transition_map(c_uv, (x/(x^2+y^2), y/(x^2+y^2)),
....:                 intersection_name='W', restrictions1= x^2+y^2!=0,
....:                 restrictions2= u^2+v^2!=0)
sage: uv_to_xy = xy_to_uv.inverse()
sage: W = U.intersection(V)

and the following map from the open interval \((0,5\pi/2)\) to \(S^2\), the image of it being the great circle \(x=0\), \(u=0\), which goes through the North and South poles:

sage: I.<t> = OpenInterval(0, 5*pi/2)
sage: J = I.open_interval(0, 3*pi/2)
sage: K = I.open_interval(pi, 5*pi/2)
sage: c_J = J.canonical_chart(); c_K = K.canonical_chart()
sage: Phi = I.diff_map(M, {(c_J, c_xy):
....:                      (0, sgn(pi-t)*sqrt((1+cos(t))/(1-cos(t)))),
....:                      (c_K, c_uv):
....:                      (0,  sgn(t-2*pi)*sqrt((1-cos(t))/(1+cos(t))))},
....:                  name='Phi')

Let us consider a vector field on \(S^2\):

sage: eU = c_xy.frame(); eV = c_uv.frame()
sage: w = M.vector_field(name='w')
sage: w[eU,0] = 1
sage: w.add_comp_by_continuation(eV, W, chart=c_uv)
sage: w.display(eU)
w = d/dx
sage: w.display(eV)
w = (-u^2 + v^2) d/du - 2*u*v d/dv

We have then:

sage: wa = w.along(Phi); wa
Vector field w along the Real interval (0, 5/2*pi) with values on
 the 2-dimensional differentiable manifold S^2
sage: wa.display(eU.along(Phi))
w = d/dx
sage: wa.display(eV.along(Phi))
w = -(cos(t) - 1)*sgn(-2*pi + t)^2/(cos(t) + 1) d/du

Some tests:

sage: p = K.an_element()
sage: wa.at(p) == w.at(Phi(p))
True
sage: wa.at(J(4*pi/3)) == wa.at(K(4*pi/3))
True
sage: wa.at(I(4*pi/3)) == wa.at(K(4*pi/3))
True
sage: wa.at(K(7*pi/4)) == eU[0].at(Phi(I(7*pi/4))) # since eU[0]=d/dx
True
antisymmetrize(*pos)

Antisymmetrization over some arguments.

INPUT:

  • pos – (default: None) list of argument positions involved in the antisymmetrization (with the convention position=0 for the first argument); if None, the antisymmetrization is performed over all the arguments

OUTPUT:

  • the antisymmetrized tensor field (instance of TensorField)

EXAMPLES:

Antisymmetrization of a type-\((0,2)\) tensor field on a 2-dimensional non-parallelizable 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: a = M.tensor_field(0,2, name='a')
sage: a[eU,:] = [[1,x], [2,y]]
sage: a.add_comp_by_continuation(eV, W, chart=c_uv)
sage: a[eV,:]
[ 1/4*u + 3/4 -1/4*u + 3/4]
[ 1/4*v - 1/4 -1/4*v - 1/4]
sage: s = a.antisymmetrize() ; s
2-form on the 2-dimensional differentiable manifold M
sage: s[eU,:]
[         0  1/2*x - 1]
[-1/2*x + 1          0]
sage: s[eV,:]
[                   0 -1/8*u - 1/8*v + 1/2]
[ 1/8*u + 1/8*v - 1/2                    0]
sage: s == a.antisymmetrize(0,1)  # explicit positions
True
sage: s == a.antisymmetrize(1,0)  # the order of positions does not matter
True
at(point)

Value of self at a point of its domain.

If the current tensor field is

\[t:\ U \longrightarrow T^{(k,l)} M\]

associated with the differentiable map

\[\Phi:\ U \longrightarrow M,\]

where \(U\) and \(M\) are two manifolds (possibly \(U = M\) and \(\Phi = \mathrm{Id}_M\)), then for any point \(p \in U\), \(t(p)\) is a tensor on the tangent space to \(M\) at the point \(\Phi(p)\).

INPUT:

  • pointManifoldPoint; point \(p\) in the domain of the tensor field \(U\)

OUTPUT:

  • FreeModuleTensor representing the tensor \(t(p)\) on the tangent vector space \(T_{\Phi(p)} M\)

EXAMPLES:

Tensor on a tangent space of 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: a = M.tensor_field(1,1, name='a')
sage: a[eU,:] = [[1+y,x], [0,x+y]]
sage: a.add_comp_by_continuation(eV, W, chart=c_uv)
sage: a.display(eU)
a = (y + 1) d/dx*dx + x d/dx*dy + (x + y) d/dy*dy
sage: a.display(eV)
a = (u + 1/2) d/du*du + (-1/2*u - 1/2*v + 1/2) d/du*dv
 + 1/2 d/dv*du + (1/2*u - 1/2*v + 1/2) d/dv*dv
sage: p = M.point((2,3), chart=c_xy, name='p')
sage: ap = a.at(p) ; ap
Type-(1,1) tensor a on the Tangent space at Point p on the
 2-dimensional differentiable manifold M
sage: ap.parent()
Free module of type-(1,1) tensors on the Tangent space at Point p
 on the 2-dimensional differentiable manifold M
sage: ap.display(eU.at(p))
a = 4 d/dx*dx + 2 d/dx*dy + 5 d/dy*dy
sage: ap.display(eV.at(p))
a = 11/2 d/du*du - 3/2 d/du*dv + 1/2 d/dv*du + 7/2 d/dv*dv
sage: p.coord(c_uv) # to check the above expression
(5, -1)
base_module()

Return the vector field module on which self acts as a tensor.

OUTPUT:

EXAMPLES:

The module of vector fields on the 2-sphere as a “base module”:

sage: M = Manifold(2, 'S^2')
sage: t = M.tensor_field(0,2)
sage: t.base_module()
Module X(S^2) of vector fields on the 2-dimensional differentiable
 manifold S^2
sage: t.base_module() is M.vector_field_module()
True
sage: XM = M.vector_field_module()
sage: XM.an_element().base_module() is XM
True
comp(basis=None, from_basis=None)

Return the components in a given vector frame.

If the components are not known already, they are computed by the tensor change-of-basis formula from components in another vector frame.

INPUT:

  • basis – (default: None) vector frame in which the components are required; if none is provided, the components are assumed to refer to the tensor field domain’s default frame
  • from_basis – (default: None) vector frame from which the required components are computed, via the tensor change-of-basis formula, if they are not known already in the basis basis

OUTPUT:

  • components in the vector frame basis, as a Components

EXAMPLES:

Components of a type-\((1,1)\) tensor field defined on two open subsets:

sage: M = Manifold(2, 'M')
sage: U = M.open_subset('U')
sage: c_xy.<x, y> = U.chart()
sage: e = U.default_frame() ; e
Coordinate frame (U, (d/dx,d/dy))
sage: V = M.open_subset('V')
sage: c_uv.<u, v> = V.chart()
sage: f = V.default_frame() ; f
Coordinate frame (V, (d/du,d/dv))
sage: M.declare_union(U,V)   # M is the union of U and V
sage: t = M.tensor_field(1,1, name='t')
sage: t[e,0,0] = - x + y^3
sage: t[e,0,1] = 2+x
sage: t[f,1,1] = - u*v
sage: t.comp(e)
2-indices components w.r.t. Coordinate frame (U, (d/dx,d/dy))
sage: t.comp(e)[:]
[y^3 - x   x + 2]
[      0       0]
sage: t.comp(f)
2-indices components w.r.t. Coordinate frame (V, (d/du,d/dv))
sage: t.comp(f)[:]
[   0    0]
[   0 -u*v]

Since e is M’s default frame, the argument e can be omitted:

sage: e is M.default_frame()
True
sage: t.comp() is t.comp(e)
True

Example of computation of the components via a change of frame:

sage: a = V.automorphism_field()
sage: a[:] = [[1+v, -u^2], [0, 1-u]]
sage: h = f.new_frame(a, 'h')
sage: t.comp(h)
2-indices components w.r.t. Vector frame (V, (h_0,h_1))
sage: t.comp(h)[:]
[             0 -u^3*v/(v + 1)]
[             0           -u*v]
contract(*args)

Contraction of self with another tensor field on one or more indices.

INPUT:

  • pos1 – positions of the indices in the current tensor field involved in the contraction; pos1 must be a sequence of integers, with 0 standing for the first index position, 1 for the second one, etc.; if pos1 is not provided, a single contraction on the last index position of the tensor field is assumed
  • other – the tensor field to contract with
  • pos2 – positions of the indices in other involved in the contraction, with the same conventions as for pos1; if pos2 is not provided, a single contraction on the first index position of other is assumed

OUTPUT:

  • tensor field resulting from the contraction at the positions pos1 and pos2 of the tensor field with other

EXAMPLES:

Contractions of a type-\((1,1)\) tensor field with a type-\((2,0)\) one on a 2-dimensional non-parallelizable 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: a = M.tensor_field(1,1, name='a')
sage: a[eU,:] = [[1,x], [0,2]]
sage: a.add_comp_by_continuation(eV, W, chart=c_uv)
sage: b = M.tensor_field(2,0, name='b')
sage: b[eU,:] = [[y,-1], [x+y,2]]
sage: b.add_comp_by_continuation(eV, W, chart=c_uv)
sage: s = a.contract(b) ; s   # contraction on last index of a and first one of b
Tensor field of type (2,0) on the 2-dimensional differentiable
 manifold M

Check 1: components with respect to the manifold’s default frame (eU):

sage: [[bool(s[i,j] == sum(a[i,k]*b[k,j] for k in M.irange()))
....:   for j in M.irange()] for i in M.irange()]
[[True, True], [True, True]]

Check 2: components with respect to the frame eV:

sage: [[bool(s[eV,i,j] == sum(a[eV,i,k]*b[eV,k,j]
....:                         for k in M.irange()))
....:   for j in M.irange()] for i in M.irange()]
[[True, True], [True, True]]

Instead of the explicit call to the method contract(), one may use the index notation with Einstein convention (summation over repeated indices); it suffices to pass the indices as a string inside square brackets:

sage: a['^i_k']*b['^kj'] == s
True

Indices not involved in the contraction may be replaced by dots:

sage: a['^._k']*b['^k.'] == s
True

LaTeX notation may be used:

sage: a['^{i}_{k}']*b['^{kj}'] == s
True

Contraction on the last index of a and last index of b:

sage: s = a.contract(b, 1) ; s
Tensor field of type (2,0) on the 2-dimensional differentiable
 manifold M
sage: a['^i_k']*b['^jk'] == s
True

Contraction on the first index of b and the last index of a:

sage: s = b.contract(0,a,1) ; s
Tensor field of type (2,0) on the 2-dimensional differentiable
 manifold M
sage: b['^ki']*a['^j_k'] == s
True

The domain of the result is the intersection of the domains of the two tensor fields:

sage: aU = a.restrict(U) ; bV = b.restrict(V)
sage: s = aU.contract(b) ; s
Tensor field of type (2,0) on the Open subset U of the
 2-dimensional differentiable manifold M
sage: s = a.contract(bV) ; s
Tensor field of type (2,0) on the Open subset V of the
 2-dimensional differentiable manifold M
sage: s = aU.contract(bV) ; s
Tensor field of type (2,0) on the Open subset W of the
 2-dimensional differentiable manifold M
sage: s0 = a.contract(b)
sage: s == s0.restrict(W)
True

The contraction can be performed on more than one index: c being a type-\((2,2)\) tensor, contracting the indices in positions 2 and 3 of c with respectively those in positions 0 and 1 of b is:

sage: c = a*a ; c
Tensor field of type (2,2) on the 2-dimensional differentiable
 manifold M
sage: s = c.contract(2,3, b, 0,1) ; s  # long time
Tensor field of type (2,0) on the 2-dimensional differentiable
 manifold M
sage: s == c['^.._kl']*b['^kl']  # the same double contraction in index notation; long time
True

The symmetries are either conserved or destroyed by the contraction:

sage: c = c.symmetrize(0,1).antisymmetrize(2,3)
sage: c.symmetries()
symmetry: (0, 1);  antisymmetry: (2, 3)
sage: s = b.contract(0, c, 2) ; s
Tensor field of type (3,1) on the 2-dimensional differentiable
 manifold M
sage: s.symmetries()
symmetry: (1, 2);  no antisymmetry

Case of a scalar field result:

sage: a = M.one_form('a')
sage: a[eU,:] = [y, 1+x]
sage: a.add_comp_by_continuation(eV, W, chart=c_uv)
sage: b = M.vector_field('b')
sage: b[eU,:] = [x, y^2]
sage: b.add_comp_by_continuation(eV, W, chart=c_uv)
sage: a.display(eU)
a = y dx + (x + 1) dy
sage: b.display(eU)
b = x d/dx + y^2 d/dy
sage: s = a.contract(b) ; s
Scalar field on the 2-dimensional differentiable manifold M
sage: s.display()
M --> R
on U: (x, y) |--> (x + 1)*y^2 + x*y
on V: (u, v) |--> 1/8*u^3 - 1/8*u*v^2 + 1/8*v^3 + 1/2*u^2 - 1/8*(u^2 + 4*u)*v
sage: s == a['_i']*b['^i'] # use of index notation
True
sage: s == b.contract(a)
True

Case of a vanishing scalar field result:

sage: b = M.vector_field('b')
sage: b[eU,:] = [1+x, -y]
sage: b.add_comp_by_continuation(eV, W, chart=c_uv)
sage: s = a.contract(b) ; s
Scalar field zero on the 2-dimensional differentiable manifold M
sage: s.display()
zero: M --> R
on U: (x, y) |--> 0
on V: (u, v) |--> 0
copy()

Return an exact copy of self.

Note

The name and the derived quantities are not copied.

EXAMPLES:

Copy of a type-\((1,1)\) tensor field on a 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: xy_to_uv = c_xy.transition_map(c_uv, (x+y, x-y),
....:                    intersection_name='W', restrictions1= x>0,
....:                    restrictions2= u+v>0)
sage: uv_to_xy = xy_to_uv.inverse()
sage: e_xy = c_xy.frame(); e_uv = c_uv.frame()
sage: t = M.tensor_field(1, 1, name='t')
sage: t[e_xy,:] = [[x+y, 0], [2, 1-y]]
sage: t.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
sage: s = t.copy(); s
Tensor field of type (1,1) on
 the 2-dimensional differentiable manifold M
sage: s.display(e_xy)
(x + y) d/dx*dx + 2 d/dy*dx + (-y + 1) d/dy*dy
sage: s == t
True

If the original tensor field is modified, the copy is not:

sage: t[e_xy,0,0] = -1
sage: t.display(e_xy)
t = -d/dx*dx + 2 d/dy*dx + (-y + 1) d/dy*dy
sage: s.display(e_xy)
(x + y) d/dx*dx + 2 d/dy*dx + (-y + 1) d/dy*dy
sage: s == t
False
dalembertian(metric=None)

Return the d’Alembertian of self with respect to a given Lorentzian metric.

The d’Alembertian of a tensor field \(t\) with respect to a Lorentzian metric \(g\) is nothing but the Laplace-Beltrami operator of \(g\) applied to \(t\) (see laplacian()); if self a tensor field \(t\) of type \((k,l)\), the d’Alembertian of \(t\) with respect to \(g\) is then the tensor field of type \((k,l)\) defined by

\[(\Box t)^{a_1\ldots a_k}_{\phantom{a_1\ldots a_k}\,{b_1\ldots b_k}} = \nabla_i \nabla^i t^{a_1\ldots a_k}_{\phantom{a_1\ldots a_k}\,{b_1\ldots b_k}}\]

where \(\nabla\) is the Levi-Civita connection of \(g\) (cf. LeviCivitaConnection) and \(\nabla^i := g^{ij} \nabla_j\).

Note

If the metric \(g\) is not Lorentzian, the name d’Alembertian is not appropriate and one should use laplacian() instead.

INPUT:

  • metric – (default: None) the Lorentzian metric \(g\) involved in the definition of the d’Alembertian; if none is provided, the domain of self is supposed to be endowed with a default Lorentzian metric (i.e. is supposed to be Lorentzian manifold, see PseudoRiemannianManifold) and the latter is used to define the d’Alembertian

OUTPUT:

  • instance of TensorField representing the d’Alembertian of self

EXAMPLES:

d’Alembertian of a vector field in Minkowski spacetime, representing the electric field of a simple plane electromagnetic wave:

sage: M = Manifold(4, 'M', structure='Lorentzian')
sage: X.<t,x,y,z> = M.chart()
sage: g = M.metric()
sage: g[0,0], g[1,1], g[2,2], g[3,3] = -1, 1, 1, 1
sage: e = M.vector_field(name='e')
sage: e[1] = cos(t-z)
sage: e.display()  # plane wave propagating in the z direction
e = cos(t - z) d/dx
sage: De = e.dalembertian(); De
Vector field Box(e) on the 4-dimensional Lorentzian manifold M

The function dalembertian() from the operators module can be used instead of the method dalembertian():

sage: from sage.manifolds.operators import dalembertian
sage: dalembertian(e) == De
True

We check that the electric field obeys the wave equation:

sage: De.display()
Box(e) = 0
disp(basis=None, chart=None)

Display the tensor field in terms of its expansion with respect to a given vector frame.

The output is either text-formatted (console mode) or LaTeX-formatted (notebook mode).

INPUT:

  • basis – (default: None) vector frame with respect to which the tensor is expanded; if None, the default frame of the domain of definition of the tensor field is assumed
  • chart – (default: None) chart with respect to which the components of the tensor field in the selected frame are expressed; if None, the default chart of the vector frame domain is assumed

EXAMPLES:

Display of a type-\((1,1)\) tensor field defined on two open subsets:

sage: M = Manifold(2, 'M')
sage: U = M.open_subset('U')
sage: c_xy.<x, y> = U.chart()
sage: e = U.default_frame() ; e
Coordinate frame (U, (d/dx,d/dy))
sage: V = M.open_subset('V')
sage: c_uv.<u, v> = V.chart()
sage: f = V.default_frame() ; f
Coordinate frame (V, (d/du,d/dv))
sage: M.declare_union(U,V)   # M is the union of U and V
sage: t = M.tensor_field(1,1, name='t')
sage: t[e,0,0] = - x + y^3
sage: t[e,0,1] = 2+x
sage: t[f,1,1] = - u*v
sage: t.display(e)
t = (y^3 - x) d/dx*dx + (x + 2) d/dx*dy
sage: t.display(f)
t = -u*v d/dv*dv

Since e is M’s default frame, the argument e can be omitted:

sage: e is M.default_frame()
True
sage: t.display()
t = (y^3 - x) d/dx*dx + (x + 2) d/dx*dy

Similarly, since f is V’s default frame, the argument f can be omitted when considering the restriction of t to V:

sage: t.restrict(V).display()
t = -u*v d/dv*dv

Display with respect to a frame in which t has not been initialized (automatic use of a change-of-frame formula):

sage: a = V.automorphism_field()
sage: a[:] = [[1+v, -u^2], [0, 1-u]]
sage: h = f.new_frame(a, 'h')
sage: t.display(h)
t = -u^3*v/(v + 1) h_0*h^1 - u*v h_1*h^1

A shortcut of display() is disp():

sage: t.disp(h)
t = -u^3*v/(v + 1) h_0*h^1 - u*v h_1*h^1
display(basis=None, chart=None)

Display the tensor field in terms of its expansion with respect to a given vector frame.

The output is either text-formatted (console mode) or LaTeX-formatted (notebook mode).

INPUT:

  • basis – (default: None) vector frame with respect to which the tensor is expanded; if None, the default frame of the domain of definition of the tensor field is assumed
  • chart – (default: None) chart with respect to which the components of the tensor field in the selected frame are expressed; if None, the default chart of the vector frame domain is assumed

EXAMPLES:

Display of a type-\((1,1)\) tensor field defined on two open subsets:

sage: M = Manifold(2, 'M')
sage: U = M.open_subset('U')
sage: c_xy.<x, y> = U.chart()
sage: e = U.default_frame() ; e
Coordinate frame (U, (d/dx,d/dy))
sage: V = M.open_subset('V')
sage: c_uv.<u, v> = V.chart()
sage: f = V.default_frame() ; f
Coordinate frame (V, (d/du,d/dv))
sage: M.declare_union(U,V)   # M is the union of U and V
sage: t = M.tensor_field(1,1, name='t')
sage: t[e,0,0] = - x + y^3
sage: t[e,0,1] = 2+x
sage: t[f,1,1] = - u*v
sage: t.display(e)
t = (y^3 - x) d/dx*dx + (x + 2) d/dx*dy
sage: t.display(f)
t = -u*v d/dv*dv

Since e is M’s default frame, the argument e can be omitted:

sage: e is M.default_frame()
True
sage: t.display()
t = (y^3 - x) d/dx*dx + (x + 2) d/dx*dy

Similarly, since f is V’s default frame, the argument f can be omitted when considering the restriction of t to V:

sage: t.restrict(V).display()
t = -u*v d/dv*dv

Display with respect to a frame in which t has not been initialized (automatic use of a change-of-frame formula):

sage: a = V.automorphism_field()
sage: a[:] = [[1+v, -u^2], [0, 1-u]]
sage: h = f.new_frame(a, 'h')
sage: t.display(h)
t = -u^3*v/(v + 1) h_0*h^1 - u*v h_1*h^1

A shortcut of display() is disp():

sage: t.disp(h)
t = -u^3*v/(v + 1) h_0*h^1 - u*v h_1*h^1
display_comp(frame=None, chart=None, coordinate_labels=True, only_nonzero=True, only_nonredundant=False)

Display the tensor components with respect 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 with respect to which the tensor field components are defined; if None, then
    • if chart is not None, the coordinate frame associated to chart is used
    • otherwise, the default basis of the vector field module on which the tensor field is defined is used
  • chart – (default: None) chart specifying the coordinate expression of the components; if None, the default chart of the tensor field domain is used
  • coordinate_labels – (default: True) boolean; if True, coordinate symbols are used by default (instead of integers) as index labels whenever frame is a coordinate frame
  • only_nonzero – (default: True) boolean; if True, only nonzero components are displayed
  • only_nonredundant – (default: False) boolean; if True, only nonredundant components are displayed in case of symmetries

EXAMPLES:

Display of the components of a type-\((1,1)\) tensor field defined on two open subsets:

sage: M = Manifold(2, 'M')
sage: U = M.open_subset('U')
sage: c_xy.<x, y> = U.chart()
sage: e = U.default_frame()
sage: V = M.open_subset('V')
sage: c_uv.<u, v> = V.chart()
sage: f = V.default_frame()
sage: M.declare_union(U,V)   # M is the union of U and V
sage: t = M.tensor_field(1,1, name='t')
sage: t[e,0,0] = - x + y^3
sage: t[e,0,1] = 2+x
sage: t[f,1,1] = - u*v
sage: t.display_comp(e)
t^x_x = y^3 - x
t^x_y = x + 2
sage: t.display_comp(f)
t^v_v = -u*v

Components in a chart frame:

sage: t.display_comp(chart=c_xy)
t^x_x = y^3 - x
t^x_y = x + 2
sage: t.display_comp(chart=c_uv)
t^v_v = -u*v

See documentation of sage.manifolds.differentiable.tensorfield_paral.TensorFieldParal.display_comp() for more options.

div(metric=None)

Return the divergence of self (with respect to a given metric).

The divergence is taken on the last index: if self is a tensor field \(t\) of type \((k,0)\) with \(k\geq 1\), the divergence of \(t\) with respect to the metric \(g\) is the tensor field of type \((k-1,0)\) defined by

\[(\mathrm{div}\, t)^{a_1\ldots a_{k-1}} = \nabla_i t^{a_1\ldots a_{k-1} i} = (\nabla t)^{a_1\ldots a_{k-1} i}_{\phantom{a_1\ldots a_{k-1} i}\, i}\]

where \(\nabla\) is the Levi-Civita connection of \(g\) (cf. LeviCivitaConnection).

This definition is extended to tensor fields of type \((k,l)\) with \(k\geq 0\) and \(l\geq 1\), by raising the last index with the metric \(g\): \(\mathrm{div}\, t\) is then the tensor field of type \((k,l-1)\) defined by

\[(\mathrm{div}\, t)^{a_1\ldots a_k}_{\phantom{a_1\ldots a_k}\, b_1 \ldots b_{l-1}} = \nabla_i (g^{ij} t^{a_1\ldots a_k}_{\phantom{a_1 \ldots a_k}\, b_1\ldots b_{l-1} j}) = (\nabla t^\sharp)^{a_1\ldots a_k i}_{\phantom{a_1\ldots a_k i}\, b_1\ldots b_{l-1} i}\]

where \(t^\sharp\) is the tensor field deduced from \(t\) by raising the last index with the metric \(g\) (see up()).

INPUT:

  • metric – (default: None) the pseudo-Riemannian metric \(g\) involved in the definition of the divergence; if none is provided, the domain of self is supposed to be endowed with a default metric (i.e. is supposed to be pseudo-Riemannian manifold, see PseudoRiemannianManifold) and the latter is used to define the divergence.

OUTPUT:

  • instance of either DiffScalarField if \((k,l)=(1,0)\) (self is a vector field) or \((k,l)=(0,1)\) (self is a 1-form) or of TensorField if \(k+l\geq 2\) representing the divergence of self with respect to metric

EXAMPLES:

Divergence of a vector field in the Euclidean plane:

sage: M.<x,y> = EuclideanSpace()
sage: v = M.vector_field(x, y, name='v')
sage: s = v.divergence(); s
Scalar field div(v) on the Euclidean plane E^2
sage: s.display()
div(v): E^2 --> R
   (x, y) |--> 2

A shortcut alias of divergence is div:

sage: v.div() == s
True

The function div() from the operators module can be used instead of the method divergence():

sage: from sage.manifolds.operators import div
sage: div(v) == s
True

The divergence can be taken with respect to a metric tensor that is not the default one:

sage: h = M.lorentzian_metric('h')
sage: h[1,1], h[2,2] = -1, 1/(1+x^2+y^2)
sage: s = v.div(h); s
Scalar field div_h(v) on the Euclidean plane E^2
sage: s.display()
div_h(v): E^2 --> R
   (x, y) |--> (x^2 + y^2 + 2)/(x^2 + y^2 + 1)

The standard formula

\[\mathrm{div}_h \, v = \frac{1}{\sqrt{|\det h|}} \frac{\partial}{\partial x^i} \left( \sqrt{|\det h|} \, v^i \right)\]

is checked as follows:

sage: sqrth = h.sqrt_abs_det().expr(); sqrth
1/sqrt(x^2 + y^2 + 1)
sage: s == 1/sqrth * sum( (sqrth*v[i]).diff(i) for i in M.irange())
True

A divergence-free vector:

sage: w = M.vector_field(-y, x, name='w')
sage: w.div().display()
div(w): E^2 --> R
   (x, y) |--> 0
sage: w.div(h).display()
div_h(w): E^2 --> R
   (x, y) |--> 0

Divergence of a type-(2,0) tensor field:

sage: t = v*w; t
Tensor field v*w of type (2,0) on the Euclidean plane E^2
sage: s = t.div(); s
Vector field div(v*w) on the Euclidean plane E^2
sage: s.display()
div(v*w) = -y e_x + x e_y
divergence(metric=None)

Return the divergence of self (with respect to a given metric).

The divergence is taken on the last index: if self is a tensor field \(t\) of type \((k,0)\) with \(k\geq 1\), the divergence of \(t\) with respect to the metric \(g\) is the tensor field of type \((k-1,0)\) defined by

\[(\mathrm{div}\, t)^{a_1\ldots a_{k-1}} = \nabla_i t^{a_1\ldots a_{k-1} i} = (\nabla t)^{a_1\ldots a_{k-1} i}_{\phantom{a_1\ldots a_{k-1} i}\, i}\]

where \(\nabla\) is the Levi-Civita connection of \(g\) (cf. LeviCivitaConnection).

This definition is extended to tensor fields of type \((k,l)\) with \(k\geq 0\) and \(l\geq 1\), by raising the last index with the metric \(g\): \(\mathrm{div}\, t\) is then the tensor field of type \((k,l-1)\) defined by

\[(\mathrm{div}\, t)^{a_1\ldots a_k}_{\phantom{a_1\ldots a_k}\, b_1 \ldots b_{l-1}} = \nabla_i (g^{ij} t^{a_1\ldots a_k}_{\phantom{a_1 \ldots a_k}\, b_1\ldots b_{l-1} j}) = (\nabla t^\sharp)^{a_1\ldots a_k i}_{\phantom{a_1\ldots a_k i}\, b_1\ldots b_{l-1} i}\]

where \(t^\sharp\) is the tensor field deduced from \(t\) by raising the last index with the metric \(g\) (see up()).

INPUT:

  • metric – (default: None) the pseudo-Riemannian metric \(g\) involved in the definition of the divergence; if none is provided, the domain of self is supposed to be endowed with a default metric (i.e. is supposed to be pseudo-Riemannian manifold, see PseudoRiemannianManifold) and the latter is used to define the divergence.

OUTPUT:

  • instance of either DiffScalarField if \((k,l)=(1,0)\) (self is a vector field) or \((k,l)=(0,1)\) (self is a 1-form) or of TensorField if \(k+l\geq 2\) representing the divergence of self with respect to metric

EXAMPLES:

Divergence of a vector field in the Euclidean plane:

sage: M.<x,y> = EuclideanSpace()
sage: v = M.vector_field(x, y, name='v')
sage: s = v.divergence(); s
Scalar field div(v) on the Euclidean plane E^2
sage: s.display()
div(v): E^2 --> R
   (x, y) |--> 2

A shortcut alias of divergence is div:

sage: v.div() == s
True

The function div() from the operators module can be used instead of the method divergence():

sage: from sage.manifolds.operators import div
sage: div(v) == s
True

The divergence can be taken with respect to a metric tensor that is not the default one:

sage: h = M.lorentzian_metric('h')
sage: h[1,1], h[2,2] = -1, 1/(1+x^2+y^2)
sage: s = v.div(h); s
Scalar field div_h(v) on the Euclidean plane E^2
sage: s.display()
div_h(v): E^2 --> R
   (x, y) |--> (x^2 + y^2 + 2)/(x^2 + y^2 + 1)

The standard formula

\[\mathrm{div}_h \, v = \frac{1}{\sqrt{|\det h|}} \frac{\partial}{\partial x^i} \left( \sqrt{|\det h|} \, v^i \right)\]

is checked as follows:

sage: sqrth = h.sqrt_abs_det().expr(); sqrth
1/sqrt(x^2 + y^2 + 1)
sage: s == 1/sqrth * sum( (sqrth*v[i]).diff(i) for i in M.irange())
True

A divergence-free vector:

sage: w = M.vector_field(-y, x, name='w')
sage: w.div().display()
div(w): E^2 --> R
   (x, y) |--> 0
sage: w.div(h).display()
div_h(w): E^2 --> R
   (x, y) |--> 0

Divergence of a type-(2,0) tensor field:

sage: t = v*w; t
Tensor field v*w of type (2,0) on the Euclidean plane E^2
sage: s = t.div(); s
Vector field div(v*w) on the Euclidean plane E^2
sage: s.display()
div(v*w) = -y e_x + x e_y
domain()

Return the manifold on which self is defined.

OUTPUT:

EXAMPLES:

sage: M = Manifold(2, 'M')
sage: c_xy.<x,y> = M.chart()
sage: t = M.tensor_field(1,2)
sage: t.domain()
2-dimensional differentiable manifold M
sage: U = M.open_subset('U', coord_def={c_xy: x<0})
sage: h = t.restrict(U)
sage: h.domain()
Open subset U of the 2-dimensional differentiable manifold M
down(metric, pos=None)

Compute a metric dual of the tensor field by lowering some index with a given metric.

If \(T\) is the tensor field, \((k,l)\) its type and \(p\) the position of a contravariant index (i.e. \(0\leq p < k\)), this method called with pos \(=p\) yields the tensor field \(T^\flat\) of type \((k-1,l+1)\) whose components are

\[(T^\flat)^{a_1\ldots a_{k-1}}_{\phantom{a_1\ldots a_{k-1}} \, b_1 \ldots b_{l+1}} = g_{b_1 i} \, T^{a_1\ldots a_{p} \, i \, a_{p+1}\ldots a_{k-1}}_{\phantom{a_1 \ldots a_{p} \, i \, a_{p+1}\ldots a_{k-1}}\, b_2 \ldots b_{l+1}},\]

\(g_{ab}\) being the components of the metric tensor.

The reverse operation is TensorField.up().

INPUT:

  • metric – metric \(g\), as an instance of PseudoRiemannianMetric
  • pos – (default: None) position of the index (with the convention pos=0 for the first index); if None, the lowering is performed over all the contravariant indices, starting from the last one

OUTPUT:

  • the tensor field \(T^\flat\) resulting from the index lowering operation

EXAMPLES:

Lowering the index of a vector field results in a 1-form:

sage: M = Manifold(2, 'M', start_index=1)
sage: c_xy.<x,y> = M.chart()
sage: g = M.metric('g')
sage: g[1,1], g[1,2], g[2,2] = 1+x, x*y, 1-y
sage: v = M.vector_field()
sage: v[:] = [-1,2]
sage: w = v.down(g) ; w
1-form on the 2-dimensional differentiable manifold M
sage: w.display()
(2*x*y - x - 1) dx + (-(x + 2)*y + 2) dy

Using the index notation instead of down():

sage: w == g['_ab']*v['^b']
True

The reverse operation:

sage: v1 = w.up(g) ; v1
Vector field on the 2-dimensional differentiable manifold M
sage: v1 == v
True

Lowering the indices of a tensor field of type (2,0):

sage: t = M.tensor_field(2, 0)
sage: t[:] = [[1,2], [3,4]]
sage: td0 = t.down(g, 0) ; td0  # lowering the first index
Tensor field of type (1,1) on the 2-dimensional differentiable
 manifold M
sage: td0 == g['_ac']*t['^cb'] # the same operation in index notation
True
sage: td0[:]
[  3*x*y + x + 1   (x - 3)*y + 3]
[4*x*y + 2*x + 2 2*(x - 2)*y + 4]
sage: tdd0 = td0.down(g) ; tdd0 # the two indices have been lowered, starting from the first one
Tensor field of type (0,2) on the 2-dimensional differentiable
 manifold M
sage: tdd0 == g['_ac']*td0['^c_b'] # the same operation in index notation
True
sage: tdd0[:]
[      4*x^2*y^2 + x^2 + 5*(x^2 + x)*y + 2*x + 1 2*(x^2 - 2*x)*y^2 + (x^2 + 2*x - 3)*y + 3*x + 3]
[(3*x^2 - 4*x)*y^2 + (x^2 + 3*x - 2)*y + 2*x + 2           (x^2 - 5*x + 4)*y^2 + (5*x - 8)*y + 4]
sage: td1 = t.down(g, 1) ; td1  # lowering the second index
Tensor field of type (1,1) on the 2-dimensional differentiable
 manifold M
sage: td1 == g['_ac']*t['^bc'] # the same operation in index notation
True
sage: td1[:]
[  2*x*y + x + 1   (x - 2)*y + 2]
[4*x*y + 3*x + 3 (3*x - 4)*y + 4]
sage: tdd1 = td1.down(g) ; tdd1 # the two indices have been lowered, starting from the second one
Tensor field of type (0,2) on the 2-dimensional differentiable
 manifold M
sage: tdd1 == g['_ac']*td1['^c_b'] # the same operation in index notation
True
sage: tdd1[:]
[      4*x^2*y^2 + x^2 + 5*(x^2 + x)*y + 2*x + 1 (3*x^2 - 4*x)*y^2 + (x^2 + 3*x - 2)*y + 2*x + 2]
[2*(x^2 - 2*x)*y^2 + (x^2 + 2*x - 3)*y + 3*x + 3           (x^2 - 5*x + 4)*y^2 + (5*x - 8)*y + 4]
sage: tdd1 == tdd0   # the order of index lowering is important
False
sage: tdd = t.down(g) ; tdd  # both indices are lowered, starting from the last one
Tensor field of type (0,2) on the 2-dimensional differentiable
 manifold M
sage: tdd[:]
[      4*x^2*y^2 + x^2 + 5*(x^2 + x)*y + 2*x + 1 (3*x^2 - 4*x)*y^2 + (x^2 + 3*x - 2)*y + 2*x + 2]
[2*(x^2 - 2*x)*y^2 + (x^2 + 2*x - 3)*y + 3*x + 3           (x^2 - 5*x + 4)*y^2 + (5*x - 8)*y + 4]
sage: tdd0 == tdd  # to get tdd0, indices have been lowered from the first one, contrary to tdd
False
sage: tdd1 == tdd  # the same order for index lowering has been applied
True
sage: u0tdd = tdd.up(g, 0) ; u0tdd # the first index is raised again
Tensor field of type (1,1) on the 2-dimensional differentiable
 manifold M
sage: uu0tdd = u0tdd.up(g) ; uu0tdd # the second index is then raised
Tensor field of type (2,0) on the 2-dimensional differentiable
 manifold M
sage: u1tdd = tdd.up(g, 1) ; u1tdd  # raising operation, starting from the last index
Tensor field of type (1,1) on the 2-dimensional differentiable
 manifold M
sage: uu1tdd = u1tdd.up(g) ; uu1tdd
Tensor field of type (2,0) on the 2-dimensional differentiable
 manifold M
sage: uutdd = tdd.up(g) ; uutdd  # both indices are raised, starting from the first one
Tensor field of type (2,0) on the 2-dimensional differentiable
 manifold M
sage: uutdd == t  # should be true
True
sage: uu0tdd == t # should be true
True
sage: uu1tdd == t # not true, because of the order of index raising to get uu1tdd
False
laplacian(metric=None)

Return the Laplacian of self with respect to a given metric (Laplace-Beltrami operator).

If self is a tensor field \(t\) of type \((k,l)\), the Laplacian of \(t\) with respect to the metric \(g\) is the tensor field of type \((k,l)\) defined by

\[(\Delta t)^{a_1\ldots a_k}_{\phantom{a_1\ldots a_k}\,{b_1\ldots b_k}} = \nabla_i \nabla^i t^{a_1\ldots a_k}_{\phantom{a_1\ldots a_k}\,{b_1\ldots b_k}}\]

where \(\nabla\) is the Levi-Civita connection of \(g\) (cf. LeviCivitaConnection) and \(\nabla^i := g^{ij} \nabla_j\). The operator \(\Delta = \nabla_i \nabla^i\) is called the Laplace-Beltrami operator of metric \(g\).

INPUT:

  • metric – (default: None) the pseudo-Riemannian metric \(g\) involved in the definition of the Laplacian; if none is provided, the domain of self is supposed to be endowed with a default metric (i.e. is supposed to be pseudo-Riemannian manifold, see PseudoRiemannianManifold) and the latter is used to define the Laplacian

OUTPUT:

  • instance of TensorField representing the Laplacian of self

EXAMPLES:

Laplacian of a vector field in the Euclidean plane:

sage: M.<x,y> = EuclideanSpace()
sage: v = M.vector_field(x^3 + y^2, x*y, name='v')
sage: Dv = v.laplacian(); Dv
Vector field Delta(v) on the Euclidean plane E^2
sage: Dv.display()
Delta(v) = (6*x + 2) e_x

The function laplacian() from the operators module can be used instead of the method laplacian():

sage: from sage.manifolds.operators import laplacian
sage: laplacian(v) == Dv
True

In the present case (Euclidean metric and Cartesian coordinates), the components of the Laplacian are the Laplacians of the components:

sage: all([Dv[[i]] == laplacian(v[[i]]) for i in M.irange()])
True

The Laplacian can be taken with respect to a metric tensor that is not the default one:

sage: h = M.lorentzian_metric('h')
sage: h[1,1], h[2,2] = -1, 1+x^2
sage: Dv = v.laplacian(h); Dv
Vector field Delta_h(v) on the Euclidean plane E^2
sage: Dv.display()
Delta_h(v) = -(8*x^5 - 2*x^4 - x^2*y^2 + 15*x^3 - 4*x^2 + 6*x
 - 2)/(x^4 + 2*x^2 + 1) e_x - 3*x^3*y/(x^4 + 2*x^2 + 1) e_y
lie_der(vector)

Lie derivative of self with respect to a vector field.

INPUT:

  • vector – vector field with respect to which the Lie derivative is to be taken

OUTPUT:

  • the tensor field that is the Lie derivative of the current tensor field with respect to vector

EXAMPLES:

Lie derivative of a type-\((1,1)\) tensor field along a vector field on the 2-sphere:

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: xy_to_uv = c_xy.transition_map(c_uv, (x+y, x-y),
....:                    intersection_name='W', restrictions1= x>0,
....:                    restrictions2= u+v>0)
sage: uv_to_xy = xy_to_uv.inverse()
sage: e_xy = c_xy.frame(); e_uv = c_uv.frame()
sage: t = M.tensor_field(1,1, name='t')
sage: t[e_xy,:] = [[x, 1], [y, 0]]
sage: t.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
sage: w = M.vector_field(name='w')
sage: w[e_xy,:] = [-y, x]
sage: w.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
sage: lt = t.lie_derivative(w); lt
Tensor field of type (1,1) on the 2-dimensional differentiable
 manifold M
sage: lt.display(e_xy)
d/dx*dx - x d/dx*dy + (-y - 1) d/dy*dy
sage: lt.display(e_uv)
-1/2*u d/du*du + (1/2*u + 1) d/du*dv + (-1/2*v + 1) d/dv*du + 1/2*v d/dv*dv

The result is cached:

sage: t.lie_derivative(w) is lt
True

An alias is lie_der:

sage: t.lie_der(w) is t.lie_derivative(w)
True

Lie derivative of a vector field:

sage: a = M.vector_field(name='a')
sage: a[e_xy,:] = [1-x, x-y]
sage: a.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
sage: a.lie_der(w)
Vector field on the 2-dimensional differentiable manifold M
sage: a.lie_der(w).display(e_xy)
x d/dx + (-y - 1) d/dy
sage: a.lie_der(w).display(e_uv)
(v - 1) d/du + (u + 1) d/dv

The Lie derivative is antisymmetric:

sage: a.lie_der(w) == - w.lie_der(a)
True

and it coincides with the commutator of the two vector fields:

sage: f = M.scalar_field({c_xy: 3*x-1, c_uv:  3/2*(u+v)-1})
sage: a.lie_der(w)(f) == w(a(f)) - a(w(f))  # long time
True
lie_derivative(vector)

Lie derivative of self with respect to a vector field.

INPUT:

  • vector – vector field with respect to which the Lie derivative is to be taken

OUTPUT:

  • the tensor field that is the Lie derivative of the current tensor field with respect to vector

EXAMPLES:

Lie derivative of a type-\((1,1)\) tensor field along a vector field on the 2-sphere:

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: xy_to_uv = c_xy.transition_map(c_uv, (x+y, x-y),
....:                    intersection_name='W', restrictions1= x>0,
....:                    restrictions2= u+v>0)
sage: uv_to_xy = xy_to_uv.inverse()
sage: e_xy = c_xy.frame(); e_uv = c_uv.frame()
sage: t = M.tensor_field(1,1, name='t')
sage: t[e_xy,:] = [[x, 1], [y, 0]]
sage: t.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
sage: w = M.vector_field(name='w')
sage: w[e_xy,:] = [-y, x]
sage: w.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
sage: lt = t.lie_derivative(w); lt
Tensor field of type (1,1) on the 2-dimensional differentiable
 manifold M
sage: lt.display(e_xy)
d/dx*dx - x d/dx*dy + (-y - 1) d/dy*dy
sage: lt.display(e_uv)
-1/2*u d/du*du + (1/2*u + 1) d/du*dv + (-1/2*v + 1) d/dv*du + 1/2*v d/dv*dv

The result is cached:

sage: t.lie_derivative(w) is lt
True

An alias is lie_der:

sage: t.lie_der(w) is t.lie_derivative(w)
True

Lie derivative of a vector field:

sage: a = M.vector_field(name='a')
sage: a[e_xy,:] = [1-x, x-y]
sage: a.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
sage: a.lie_der(w)
Vector field on the 2-dimensional differentiable manifold M
sage: a.lie_der(w).display(e_xy)
x d/dx + (-y - 1) d/dy
sage: a.lie_der(w).display(e_uv)
(v - 1) d/du + (u + 1) d/dv

The Lie derivative is antisymmetric:

sage: a.lie_der(w) == - w.lie_der(a)
True

and it coincides with the commutator of the two vector fields:

sage: f = M.scalar_field({c_xy: 3*x-1, c_uv:  3/2*(u+v)-1})
sage: a.lie_der(w)(f) == w(a(f)) - a(w(f))  # long time
True
restrict(subdomain, dest_map=None)

Return the restriction of self to some subdomain.

If the restriction has not been defined yet, it is constructed here.

INPUT:

  • subdomainDifferentiableManifold; open subset \(U\) of the tensor field domain \(S\)
  • dest_mapDiffMap (default: None); destination map \(\Psi:\ U \rightarrow V\), where \(V\) is an open subset of the manifold \(M\) where the tensor field takes it values; if None, the restriction of \(\Phi\) to \(U\) is used, \(\Phi\) being the differentiable map \(S \rightarrow M\) associated with the tensor field

OUTPUT:

EXAMPLES:

Restrictions of a vector field on the 2-sphere:

sage: M = Manifold(2, 'S^2', start_index=1)
sage: U = M.open_subset('U') # the complement of the North pole
sage: stereoN.<x,y> = U.chart()  # stereographic coordinates from the North pole
sage: eN = stereoN.frame() # the associated vector frame
sage: V =  M.open_subset('V') # the complement of the South pole
sage: stereoS.<u,v> = V.chart()  # stereographic coordinates from the South pole
sage: eS = stereoS.frame() # the associated vector frame
sage: transf = stereoN.transition_map(stereoS, (x/(x^2+y^2), y/(x^2+y^2)),
....:               intersection_name='W', restrictions1= x^2+y^2!=0,
....:               restrictions2= u^2+v^2!=0)
sage: inv = transf.inverse() # transformation from stereoS to stereoN
sage: W = U.intersection(V) # the complement of the North and South poles
sage: stereoN_W = W.atlas()[0]  # restriction of stereographic coord. from North pole to W
sage: stereoS_W = W.atlas()[1]  # restriction of stereographic coord. from South pole to W
sage: eN_W = stereoN_W.frame() ; eS_W = stereoS_W.frame()
sage: v = M.vector_field('v')
sage: v.set_comp(eN)[1] = 1  # given the default settings, this can be abriged to v[1] = 1
sage: v.display()
v = d/dx
sage: vU = v.restrict(U) ; vU
Vector field v on the Open subset U of the 2-dimensional
 differentiable manifold S^2
sage: vU.display()
v = d/dx
sage: vU == eN[1]
True
sage: vW = v.restrict(W) ; vW
Vector field v on the Open subset W of the 2-dimensional
 differentiable manifold S^2
sage: vW.display()
v = d/dx
sage: vW.display(eS_W, stereoS_W)
v = (-u^2 + v^2) d/du - 2*u*v d/dv
sage: vW == eN_W[1]
True

At this stage, defining the restriction of v to the open subset V fully specifies v:

sage: v.restrict(V)[1] = vW[eS_W, 1, stereoS_W].expr()  # note that eS is the default frame on V
sage: v.restrict(V)[2] = vW[eS_W, 2, stereoS_W].expr()
sage: v.display(eS, stereoS)
v = (-u^2 + v^2) d/du - 2*u*v d/dv
sage: v.restrict(U).display()
v = d/dx
sage: v.restrict(V).display()
v = (-u^2 + v^2) d/du - 2*u*v d/dv

The restriction of the vector field to its own domain is of course itself:

sage: v.restrict(M) is v
True
sage: vU.restrict(U) is vU
True
set_comp(basis=None)

Return the components of self in a given vector frame for assignment.

The components with respect to other frames having the same domain as the provided vector frame are deleted, in order to avoid any inconsistency. To keep them, use the method add_comp() instead.

INPUT:

  • basis – (default: None) vector frame in which the components are defined; if none is provided, the components are assumed to refer to the tensor field domain’s default frame

OUTPUT:

  • components in the given frame, as a Components; if such components did not exist previously, they are created

EXAMPLES:

sage: M = Manifold(2, 'M') # the 2-dimensional sphere S^2
sage: U = M.open_subset('U') # complement of the North pole
sage: c_xy.<x,y> = U.chart() # stereographic coordinates from the North pole
sage: V = M.open_subset('V') # complement of the South pole
sage: c_uv.<u,v> = V.chart() # stereographic coordinates from the South pole
sage: M.declare_union(U,V)   # S^2 is the union of U and V
sage: e_uv = c_uv.frame()
sage: t = M.tensor_field(1, 2, name='t')
sage: t.set_comp(e_uv)
3-indices components w.r.t. Coordinate frame (V, (d/du,d/dv))
sage: t.set_comp(e_uv)[1,0,1] = u+v
sage: t.display(e_uv)
t = (u + v) d/dv*du*dv

Setting the components in a new frame (e):

sage: e = V.vector_frame('e')
sage: t.set_comp(e)
3-indices components w.r.t. Vector frame (V, (e_0,e_1))
sage: t.set_comp(e)[0,1,1] = u*v
sage: t.display(e)
t = u*v e_0*e^1*e^1

Since the frames e and e_uv are defined on the same domain, the components w.r.t. e_uv have been erased:

sage: t.display(c_uv.frame())
Traceback (most recent call last):
...
ValueError: no basis could be found for computing the components
 in the Coordinate frame (V, (d/du,d/dv))
set_name(name=None, latex_name=None)

Set (or change) the text name and LaTeX name of self.

INPUT:

  • name – string (default: None); name given to the tensor field
  • latex_name – string (default: None); LaTeX symbol to denote the tensor field; if None while name is provided, the LaTeX symbol is set to name

EXAMPLES:

sage: M = Manifold(2, 'M')
sage: t = M.tensor_field(1, 3); t
Tensor field of type (1,3) on the 2-dimensional differentiable
 manifold M
sage: t.set_name(name='t')
sage: t
Tensor field t of type (1,3) on the 2-dimensional differentiable
 manifold M
sage: latex(t)
t
sage: t.set_name(latex_name=r'\tau')
sage: latex(t)
\tau
sage: t.set_name(name='a')
sage: t
Tensor field a of type (1,3) on the 2-dimensional differentiable
 manifold M
sage: latex(t)
a
set_restriction(rst)

Define a restriction of self to some subdomain.

INPUT:

  • rstTensorField of the same type and symmetries as the current tensor field self, defined on a subdomain of the domain of self

EXAMPLES:

sage: M = Manifold(2, 'M') # the 2-dimensional sphere S^2
sage: U = M.open_subset('U') # complement of the North pole
sage: c_xy.<x,y> = U.chart() # stereographic coordinates from the North pole
sage: V = M.open_subset('V') # complement of the South pole
sage: c_uv.<u,v> = V.chart() # stereographic coordinates from the South pole
sage: M.declare_union(U,V)   # S^2 is the union of U and V
sage: t = M.tensor_field(1, 2, name='t')
sage: s = U.tensor_field(1, 2)
sage: s[0,0,1] = x+y
sage: t.set_restriction(s)
sage: t.display(c_xy.frame())
t = (x + y) d/dx*dx*dy
sage: t.restrict(U) == s
True
symmetries()

Print the list of symmetries and antisymmetries.

EXAMPLES:

sage: M = Manifold(2, 'S^2')
sage: t = M.tensor_field(1,2)
sage: t.symmetries()
no symmetry;  no antisymmetry
sage: t = M.tensor_field(1,2, sym=(1,2))
sage: t.symmetries()
symmetry: (1, 2);  no antisymmetry
sage: t = M.tensor_field(2,2, sym=(0,1), antisym=(2,3))
sage: t.symmetries()
symmetry: (0, 1);  antisymmetry: (2, 3)
sage: t = M.tensor_field(2,2, antisym=[(0,1),(2,3)])
sage: t.symmetries()
no symmetry;  antisymmetries: [(0, 1), (2, 3)]
symmetrize(*pos)

Symmetrization over some arguments.

INPUT:

  • pos – (default: None) list of argument positions involved in the symmetrization (with the convention position=0 for the first argument); if None, the symmetrization is performed over all the arguments

OUTPUT:

  • the symmetrized tensor field (instance of TensorField)

EXAMPLES:

Symmetrization of a type-\((0,2)\) tensor field on a 2-dimensional non-parallelizable 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: a = M.tensor_field(0,2, name='a')
sage: a[eU,:] = [[1,x], [2,y]]
sage: a.add_comp_by_continuation(eV, W, chart=c_uv)
sage: a[eV,:]
[ 1/4*u + 3/4 -1/4*u + 3/4]
[ 1/4*v - 1/4 -1/4*v - 1/4]
sage: s = a.symmetrize() ; s
Field of symmetric bilinear forms on the 2-dimensional
 differentiable manifold M
sage: s[eU,:]
[        1 1/2*x + 1]
[1/2*x + 1         y]
sage: s[eV,:]
[         1/4*u + 3/4 -1/8*u + 1/8*v + 1/4]
[-1/8*u + 1/8*v + 1/4         -1/4*v - 1/4]
sage: s == a.symmetrize(0,1)  # explicit positions
True

See also

For more details and examples, see sage.tensor.modules.free_module_tensor.FreeModuleTensor.symmetrize().

tensor_rank()

Return the tensor rank of self.

OUTPUT:

  • integer \(k+l\), where \(k\) is the contravariant rank and \(l\) is the covariant rank

EXAMPLES:

sage: M = Manifold(2, 'S^2')
sage: t = M.tensor_field(1,2)
sage: t.tensor_rank()
3
sage: v = M.vector_field()
sage: v.tensor_rank()
1
tensor_type()

Return the tensor type of self.

OUTPUT:

  • pair \((k,l)\), where \(k\) is the contravariant rank and \(l\) is the covariant rank

EXAMPLES:

sage: M = Manifold(2, 'S^2')
sage: t = M.tensor_field(1,2)
sage: t.tensor_type()
(1, 2)
sage: v = M.vector_field()
sage: v.tensor_type()
(1, 0)
trace(pos1=0, pos2=1)

Trace (contraction) on two slots of the tensor field.

INPUT:

  • pos1 – (default: 0) position of the first index for the contraction, with the convention pos1=0 for the first slot
  • pos2 – (default: 1) position of the second index for the contraction, with the same convention as for pos1. The variance type of pos2 must be opposite to that of pos1

OUTPUT:

  • tensor field resulting from the (pos1, pos2) contraction

EXAMPLES:

Trace of a type-\((1,1)\) tensor field on a 2-dimensional non-parallelizable 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: xy_to_uv = c_xy.transition_map(c_uv, (x+y, x-y),
....:                    intersection_name='W', restrictions1= x>0,
....:                    restrictions2= u+v>0)
sage: uv_to_xy = xy_to_uv.inverse()
sage: e_xy = c_xy.frame(); e_uv = c_uv.frame()
sage: W = U.intersection(V)
sage: a = M.tensor_field(1,1, name='a')
sage: a[e_xy,:] = [[1,x], [2,y]]
sage: a.add_comp_by_continuation(e_uv, W, chart=c_uv)
sage: s = a.trace() ; s
Scalar field on the 2-dimensional differentiable manifold M
sage: s.display()
M --> R
on U: (x, y) |--> y + 1
on V: (u, v) |--> 1/2*u - 1/2*v + 1
sage: s == a.trace(0,1) # explicit mention of the positions
True

Instead of the explicit call to the method trace(), one may use the index notation with Einstein convention (summation over repeated indices); it suffices to pass the indices as a string inside square brackets:

sage: a['^i_i']
Scalar field on the 2-dimensional differentiable manifold M
sage: a['^i_i'] == s
True

Any letter can be used to denote the repeated index:

sage: a['^b_b'] == s
True

Trace of a type-\((1,2)\) tensor field:

sage: b = M.tensor_field(1,2, name='b') ; b
Tensor field b of type (1,2) on the 2-dimensional differentiable
 manifold M
sage: b[e_xy,:] = [[[0,x+y], [y,0]], [[0,2], [3*x,-2]]]
sage: b.add_comp_by_continuation(e_uv, W, chart=c_uv)  # long time
sage: s = b.trace(0,1) ; s # contraction on first and second slots
1-form on the 2-dimensional differentiable manifold M
sage: s.display(e_xy)
3*x dx + (x + y - 2) dy
sage: s.display(e_uv)  # long time
(5/4*u + 3/4*v - 1) du + (1/4*u + 3/4*v + 1) dv

Use of the index notation:

sage: b['^k_ki']
1-form on the 2-dimensional differentiable manifold M
sage: b['^k_ki'] == s  # long time
True

Indices not involved in the contraction may be replaced by dots:

sage: b['^k_k.'] == s  # long time
True

The symbol ^ may be omitted:

sage: b['k_k.'] == s  # long time
True

LaTeX notations are allowed:

sage: b['^{k}_{ki}'] == s  # long time
True

Contraction on first and third slots:

sage: s = b.trace(0,2) ; s
1-form on the 2-dimensional differentiable manifold M
sage: s.display(e_xy)
2 dx + (y - 2) dy
sage: s.display(e_uv)  # long time
(1/4*u - 1/4*v) du + (-1/4*u + 1/4*v + 2) dv

Use of index notation:

sage: b['^k_.k'] == s  # long time
True
up(metric, pos=None)

Compute a metric dual of the tensor field by raising some index with a given metric.

If \(T\) is the tensor field, \((k,l)\) its type and \(p\) the position of a covariant index (i.e. \(k\leq p < k+l\)), this method called with pos \(=p\) yields the tensor field \(T^\sharp\) of type \((k+1,l-1)\) whose components are

\[(T^\sharp)^{a_1\ldots a_{k+1}}_{\phantom{a_1\ldots a_{k+1}}\, b_1 \ldots b_{l-1}} = g^{a_{k+1} i} \, T^{a_1\ldots a_k}_{\phantom{a_1\ldots a_k}\, b_1 \ldots b_{p-k} \, i \, b_{p-k+1}\ldots b_{l-1}},\]

\(g^{ab}\) being the components of the inverse metric.

The reverse operation is TensorField.down().

INPUT:

  • metric – metric \(g\), as an instance of PseudoRiemannianMetric
  • pos – (default: None) position of the index (with the convention pos=0 for the first index); if None, the raising is performed over all the covariant indices, starting from the first one

OUTPUT:

  • the tensor field \(T^\sharp\) resulting from the index raising operation

EXAMPLES:

Raising the index of a 1-form results in a vector field:

sage: M = Manifold(2, 'M', start_index=1)
sage: c_xy.<x,y> = M.chart()
sage: g = M.metric('g')
sage: g[1,1], g[1,2], g[2,2] = 1+x, x*y, 1-y
sage: w = M.one_form()
sage: w[:] = [-1, 2]
sage: v = w.up(g) ; v
Vector field on the 2-dimensional differentiable manifold M
sage: v.display()
((2*x - 1)*y + 1)/(x^2*y^2 + (x + 1)*y - x - 1) d/dx
 - (x*y + 2*x + 2)/(x^2*y^2 + (x + 1)*y - x - 1) d/dy
sage: ig = g.inverse(); ig[:]
[ (y - 1)/(x^2*y^2 + (x + 1)*y - x - 1)      x*y/(x^2*y^2 + (x + 1)*y - x - 1)]
[     x*y/(x^2*y^2 + (x + 1)*y - x - 1) -(x + 1)/(x^2*y^2 + (x + 1)*y - x - 1)]

Using the index notation instead of up():

sage: v == ig['^ab']*w['_b']
True

The reverse operation:

sage: w1 = v.down(g) ; w1
1-form on the 2-dimensional differentiable manifold M
sage: w1.display()
-dx + 2 dy
sage: w1 == w
True

The reverse operation in index notation:

sage: g['_ab']*v['^b'] == w
True

Raising the indices of a tensor field of type (0,2):

sage: t = M.tensor_field(0, 2)
sage: t[:] = [[1,2], [3,4]]
sage: tu0 = t.up(g, 0) ; tu0  # raising the first index
Tensor field of type (1,1) on the 2-dimensional differentiable
 manifold M
sage: tu0[:]
[  ((3*x + 1)*y - 1)/(x^2*y^2 + (x + 1)*y - x - 1) 2*((2*x + 1)*y - 1)/(x^2*y^2 + (x + 1)*y - x - 1)]
[    (x*y - 3*x - 3)/(x^2*y^2 + (x + 1)*y - x - 1)   2*(x*y - 2*x - 2)/(x^2*y^2 + (x + 1)*y - x - 1)]
sage: tu0 == ig['^ac']*t['_cb'] # the same operation in index notation
True
sage: tuu0 = tu0.up(g) ; tuu0 # the two indices have been raised, starting from the first one
Tensor field of type (2,0) on the 2-dimensional differentiable
 manifold M
sage: tuu0 == tu0['^a_c']*ig['^cb'] # the same operation in index notation
True
sage: tu1 = t.up(g, 1) ; tu1 # raising the second index
Tensor field of type (1,1) on the 2-dimensional differentiable
 manifold M
sage: tu1 == ig['^ac']*t['_bc'] # the same operation in index notation
True
sage: tu1[:]
[((2*x + 1)*y - 1)/(x^2*y^2 + (x + 1)*y - x - 1) ((4*x + 3)*y - 3)/(x^2*y^2 + (x + 1)*y - x - 1)]
[  (x*y - 2*x - 2)/(x^2*y^2 + (x + 1)*y - x - 1) (3*x*y - 4*x - 4)/(x^2*y^2 + (x + 1)*y - x - 1)]
sage: tuu1 = tu1.up(g) ; tuu1 # the two indices have been raised, starting from the second one
Tensor field of type (2,0) on the 2-dimensional differentiable
 manifold M
sage: tuu1 == tu1['^a_c']*ig['^cb'] # the same operation in index notation
True
sage: tuu0 == tuu1 # the order of index raising is important
False
sage: tuu = t.up(g) ; tuu # both indices are raised, starting from the first one
Tensor field of type (2,0) on the 2-dimensional differentiable
 manifold M
sage: tuu0 == tuu # the same order for index raising has been applied
True
sage: tuu1 == tuu # to get tuu1, indices have been raised from the last one, contrary to tuu
False
sage: d0tuu = tuu.down(g, 0) ; d0tuu # the first index is lowered again
Tensor field of type (1,1) on the 2-dimensional differentiable
 manifold M
sage: dd0tuu = d0tuu.down(g) ; dd0tuu  # the second index is then lowered
Tensor field of type (0,2) on the 2-dimensional differentiable
 manifold M
sage: d1tuu = tuu.down(g, 1) ; d1tuu # lowering operation, starting from the last index
Tensor field of type (1,1) on the 2-dimensional differentiable
 manifold M
sage: dd1tuu = d1tuu.down(g) ; dd1tuu
Tensor field of type (0,2) on the 2-dimensional differentiable
 manifold M
sage: ddtuu = tuu.down(g) ; ddtuu # both indices are lowered, starting from the last one
Tensor field of type (0,2) on the 2-dimensional differentiable
 manifold M
sage: ddtuu == t # should be true
True
sage: dd0tuu == t # not true, because of the order of index lowering to get dd0tuu
False
sage: dd1tuu == t # should be true
True