Differential Forms#

Let \(U\) and \(M\) be two differentiable manifolds. Given a positive integer \(p\) and a differentiable map \(\Phi: U \rightarrow M\), a differential form of degree \(p\), or \(p\)-form, along \(U\) with values on \(M\) is a field along \(U\) of alternating multilinear forms of degree \(p\) in the tangent spaces to \(M\). The standard case of a differential form 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\)).

Two classes implement differential forms, depending whether the manifold \(M\) is parallelizable:

AUTHORS:

  • Eric Gourgoulhon, Michal Bejger (2013, 2014): initial version

  • Joris Vankerschaver (2010): developed a previous class, DifferentialForm (cf. github issue #24444), which inspired the storage of the non-zero components as a dictionary whose keys are the indices.

  • Travis Scrimshaw (2016): review tweaks

REFERENCES:

class sage.manifolds.differentiable.diff_form.DiffForm(vector_field_module, degree, name=None, latex_name=None)#

Bases: TensorField

Differential form with values on a generic (i.e. a priori not parallelizable) differentiable manifold.

Given a differentiable manifold \(U\), a differentiable map \(\Phi: U \rightarrow M\) to a differentiable manifold \(M\) and a positive integer \(p\), a differential form of degree \(p\) (or \(p\)-form) along \(U\) with values on \(M\supset\Phi(U)\) is a differentiable map

\[a:\ U \longrightarrow T^{(0,p)}M\]

(\(T^{(0,p)}M\) being the tensor bundle of type \((0,p)\) over \(M\)) such that

\[\forall x \in U,\quad a(x) \in \Lambda^p(T_{\Phi(x)}^* M) ,\]

where \(T_{\Phi(x)}^* M\) is the dual of the tangent space to \(M\) at \(\Phi(x)\) and \(\Lambda^p\) stands for the exterior power of degree \(p\) (cf. ExtPowerDualFreeModule). In other words, \(a(x)\) is an alternating multilinear form of degree \(p\) of the tangent vector space \(T_{\Phi(x)} M\).

The standard case of a differential form on a manifold \(M\) 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\)).

Note

If \(M\) is parallelizable, the class DiffFormParal must be used instead.

INPUT:

  • vector_field_module – module \(\mathfrak{X}(U,\Phi)\) of vector fields along \(U\) with values on \(M\) via the map \(\Phi\)

  • degree – the degree of the differential form (i.e. its tensor rank)

  • name – (default: None) name given to the differential form

  • latex_name – (default: None) LaTeX symbol to denote the differential form; if none is provided, the LaTeX symbol is set to name

EXAMPLES:

Differential form of degree 2 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: 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: W = U.intersection(V)
sage: eU = c_xy.frame() ; eV = c_uv.frame()
sage: a = M.diff_form(2, name='a') ; a
2-form a on the 2-dimensional differentiable manifold M
sage: a.parent()
Module Omega^2(M) of 2-forms on the 2-dimensional differentiable
 manifold M
sage: a.degree()
2

Setting the components of a:

sage: a[eU,0,1] = x*y^2 + 2*x
sage: a.add_comp_by_continuation(eV, W, c_uv)
sage: a.display(eU)
a = (x*y^2 + 2*x) dx∧dy
sage: a.display(eV)
a = (-1/16*u^3 + 1/16*u*v^2 - 1/16*v^3
 + 1/16*(u^2 - 8)*v - 1/2*u) du∧dv

A 1-form on M:

sage: a = M.one_form(name='a') ; a
1-form a on the 2-dimensional differentiable manifold M
sage: a.parent()
Module Omega^1(M) of 1-forms on the 2-dimensional differentiable
 manifold M
sage: a.degree()
1

Setting the components of the 1-form in a consistent way:

sage: a[eU,:] = [-y, x]
sage: a.add_comp_by_continuation(eV, W, c_uv)
sage: a.display(eU)
a = -y dx + x dy
sage: a.display(eV)
a = 1/2*v du - 1/2*u dv

It is also possible to set the components at the 1-form definition, via a dictionary whose keys are the vector frames:

sage: a1 = M.one_form({eU: [-y, x], eV: [v/2, -u/2]}, name='a')
sage: a1 == a
True

The exterior derivative of the 1-form is a 2-form:

sage: da = a.exterior_derivative() ; da
2-form da on the 2-dimensional differentiable manifold M
sage: da.display(eU)
da = 2 dx∧dy
sage: da.display(eV)
da = -du∧dv

The exterior derivative can also be obtained by applying the function diff to a differentiable form:

sage: diff(a) is a.exterior_derivative()
True

Another 1-form defined by its components in eU:

sage: b = M.one_form(1+x*y, x^2, frame=eU, name='b')

Since eU is the default vector frame on M, it can be omitted in the definition:

sage: b = M.one_form(1+x*y, x^2, name='b')
sage: b.add_comp_by_continuation(eV, W, c_uv)

Adding two 1-forms results in another 1-form:

sage: s = a + b ; s
1-form a+b on the 2-dimensional differentiable manifold M
sage: s.display(eU)
a+b = ((x - 1)*y + 1) dx + (x^2 + x) dy
sage: s.display(eV)
a+b = (1/4*u^2 + 1/4*(u + 2)*v + 1/2) du
 + (-1/4*u*v - 1/4*v^2 - 1/2*u + 1/2) dv

The exterior product of two 1-forms is a 2-form:

sage: s = a.wedge(b) ; s
2-form a∧b on the 2-dimensional differentiable manifold M
sage: s.display(eU)
a∧b = (-2*x^2*y - x) dx∧dy
sage: s.display(eV)
a∧b = (1/8*u^3 - 1/8*u*v^2 - 1/8*v^3 + 1/8*(u^2 + 2)*v + 1/4*u) du∧dv

Multiplying a 1-form by a scalar field results in another 1-form:

sage: f = M.scalar_field({c_xy: (x+y)^2, c_uv: u^2}, name='f')
sage: s = f*a ; s
1-form f*a on the 2-dimensional differentiable manifold M
sage: s.display(eU)
f*a = (-x^2*y - 2*x*y^2 - y^3) dx + (x^3 + 2*x^2*y + x*y^2) dy
sage: s.display(eV)
f*a = 1/2*u^2*v du - 1/2*u^3 dv

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 a 2-form \(a\) as above:

sage: a = M.diff_form(2, name='a')
sage: a[eU,0,1] = x*y^2 + 2*x
sage: a.add_comp_by_continuation(eV, W, c_uv)
sage: a.display(eU)
a = (x*y**2 + 2*x) dx∧dy
sage: a.display(eV)
a = (-u**3/16 + u**2*v/16 + u*v**2/16 - u/2 - v**3/16 - v/2) du∧dv

A 1-form on M:

sage: a = M.one_form(-y, x, name='a')
sage: a.add_comp_by_continuation(eV, W, c_uv)
sage: a.display(eU)
a = -y dx + x dy
sage: a.display(eV)
a = v/2 du - u/2 dv

The exterior derivative of a:

sage: da = a.exterior_derivative()
sage: da.display(eU)
da = 2 dx∧dy
sage: da.display(eV)
da = -du∧dv

Another 1-form:

sage: b = M.one_form(1+x*y, x^2, name='b')
sage: b.add_comp_by_continuation(eV, W, c_uv)

Adding two 1-forms:

sage: s = a + b
sage: s.display(eU)
a+b = (x*y - y + 1) dx + x*(x + 1) dy
sage: s.display(eV)
a+b = (u**2/4 + u*v/4 + v/2 + 1/2) du + (-u*v/4 - u/2 - v**2/4 + 1/2) dv

The exterior product of two 1-forms:

sage: s = a.wedge(b)
sage: s.display(eU)
a∧b = x*(-2*x*y - 1) dx∧dy
sage: s.display(eV)
a∧b = (u**3/8 + u**2*v/8 - u*v**2/8 + u/4 - v**3/8 + v/4) du∧dv

Multiplying a 1-form by a scalar field:

sage: f = M.scalar_field({c_xy: (x+y)^2, c_uv: u^2}, name='f')
sage: s = f*a
sage: s.display(eU)
f*a = y*(-x**2 - 2*x*y - y**2) dx + x*(x**2 + 2*x*y + y**2) dy
sage: s.display(eV)
f*a = u**2*v/2 du - u**3/2 dv
degree()#

Return the degree of self.

OUTPUT:

  • integer \(p\) such that the differential form is a \(p\)-form

EXAMPLES:

sage: M = Manifold(3, 'M')
sage: a = M.diff_form(2); a
2-form on the 3-dimensional differentiable manifold M
sage: a.degree()
2
sage: b = M.diff_form(1); b
1-form on the 3-dimensional differentiable manifold M
sage: b.degree()
1
derivative()#

Compute the exterior derivative of self.

OUTPUT:

  • instance of DiffForm representing the exterior derivative of the differential form

EXAMPLES:

Exterior derivative of a 1-form on the 2-sphere:

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: 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: e_xy = c_xy.frame(); e_uv = c_uv.frame()

The 1-form:

sage: a = M.one_form({e_xy: [-y^2, x^2]}, name='a')
sage: a.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
sage: a.display(e_xy)
a = -y^2 dx + x^2 dy
sage: a.display(e_uv)
a = -(2*u^3*v - u^2*v^2 + v^4)/(u^8 + 4*u^6*v^2 + 6*u^4*v^4 + 4*u^2*v^6 + v^8) du
 + (u^4 - u^2*v^2 + 2*u*v^3)/(u^8 + 4*u^6*v^2 + 6*u^4*v^4 + 4*u^2*v^6 + v^8) dv

Its exterior derivative:

sage: da = a.exterior_derivative(); da
2-form da on the 2-dimensional differentiable manifold M
sage: da.display(e_xy)
da = (2*x + 2*y) dx∧dy
sage: da.display(e_uv)
da = -2*(u + v)/(u^6 + 3*u^4*v^2 + 3*u^2*v^4 + v^6) du∧dv

The result is cached, i.e. is not recomputed unless a is changed:

sage: a.exterior_derivative() is da
True

Instead of invoking the method exterior_derivative(), one may use the global function diff:

sage: diff(a) is a.exterior_derivative()
True

Let us check Cartan’s identity:

sage: v = M.vector_field({e_xy: [-y, x]}, name='v')
sage: v.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
sage: a.lie_der(v) == v.contract(diff(a)) + diff(a(v))  # long time
True
exterior_derivative()#

Compute the exterior derivative of self.

OUTPUT:

  • instance of DiffForm representing the exterior derivative of the differential form

EXAMPLES:

Exterior derivative of a 1-form on the 2-sphere:

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: 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: e_xy = c_xy.frame(); e_uv = c_uv.frame()

The 1-form:

sage: a = M.one_form({e_xy: [-y^2, x^2]}, name='a')
sage: a.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
sage: a.display(e_xy)
a = -y^2 dx + x^2 dy
sage: a.display(e_uv)
a = -(2*u^3*v - u^2*v^2 + v^4)/(u^8 + 4*u^6*v^2 + 6*u^4*v^4 + 4*u^2*v^6 + v^8) du
 + (u^4 - u^2*v^2 + 2*u*v^3)/(u^8 + 4*u^6*v^2 + 6*u^4*v^4 + 4*u^2*v^6 + v^8) dv

Its exterior derivative:

sage: da = a.exterior_derivative(); da
2-form da on the 2-dimensional differentiable manifold M
sage: da.display(e_xy)
da = (2*x + 2*y) dx∧dy
sage: da.display(e_uv)
da = -2*(u + v)/(u^6 + 3*u^4*v^2 + 3*u^2*v^4 + v^6) du∧dv

The result is cached, i.e. is not recomputed unless a is changed:

sage: a.exterior_derivative() is da
True

Instead of invoking the method exterior_derivative(), one may use the global function diff:

sage: diff(a) is a.exterior_derivative()
True

Let us check Cartan’s identity:

sage: v = M.vector_field({e_xy: [-y, x]}, name='v')
sage: v.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
sage: a.lie_der(v) == v.contract(diff(a)) + diff(a(v))  # long time
True
hodge_dual(nondegenerate_tensor=None, minus_eigenvalues_convention=False)#

Compute the Hodge dual of the differential form with respect to some non-degenerate bilinear form (Riemannian metric or symplectic form).

If the differential form is a \(p\)-form \(A\), its Hodge dual with respect to the non-degenerate form \(g\) is the \((n-p)\)-form \(*A\) defined by

\[*A_{i_1\ldots i_{n-p}} = \frac{1}{p!} A^{k_1\ldots k_p} \epsilon_{k_1\ldots k_p\, i_1\ldots i_{n-p}}\]

where \(n\) is the manifold’s dimension, \(\epsilon\) is the volume \(n\)-form associated with \(g\) (see volume_form()) and the indices \(k_1,\ldots, k_p\) are raised with \(g\). If \(g\) is a pseudo-Riemannian metric, sometimes an additional multiplicative factor of \((-1)^s\) is introduced on the right-hand side, where \(s\) is the number of negative eigenvalues of \(g\). This convention can be enforced by setting the option minus_eigenvalues_convention.

INPUT:

  • nondegenerate_tensor: a non-degenerate bilinear form defined on the same manifold as the current differential form; must be an instance of PseudoRiemannianMetric or SymplecticForm. If none is provided, the ambient domain of self is supposed to be endowed with a default metric and this metric is then used.

  • minus_eigenvalues_convention – if \(true\), a factor of \((-1)^s\) is

    introduced with \(s\) being the number of negative eigenvalues of the nondegenerate_tensor.

OUTPUT:

  • the \((n-p)\)-form \(*A\)

EXAMPLES:

Hodge dual of a 1-form on the 2-sphere equipped with the standard metric: we first construct \(\mathbb{S}^2\) and its metric \(g\):

sage: M = Manifold(2, 'S^2', start_index=1)
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 coord. (North and South)
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) # The complement of the two poles
sage: eU = c_xy.frame() ; eV = c_uv.frame()
sage: g = M.metric('g')
sage: g[eU,1,1], g[eU,2,2] = 4/(1+x^2+y^2)^2, 4/(1+x^2+y^2)^2
sage: g[eV,1,1], g[eV,2,2] = 4/(1+u^2+v^2)^2, 4/(1+u^2+v^2)^2

We endow \(S^2\) with the orientation defined by the stereographic frame from the North pole, i.e. eU; eV is then left-handed and in order to define an orientation on the whole manifold, we introduce a vector frame on V by swapping eV’s vectors:

sage: f = V.vector_frame('f', (eV[2], eV[1]))
sage: M.set_orientation([eU, f])

Then we construct the 1-form and take its Hodge dual w.r.t. \(g\):

sage: a = M.one_form({eU: [-y, x]}, name='a')
sage: a.add_comp_by_continuation(eV, W, c_uv)
sage: a.display(eU)
a = -y dx + x dy
sage: a.display(eV)
a = -v/(u^4 + 2*u^2*v^2 + v^4) du + u/(u^4 + 2*u^2*v^2 + v^4) dv
sage: sa = a.hodge_dual(g); sa
1-form *a on the 2-dimensional differentiable manifold S^2
sage: sa.display(eU)
*a = -x dx - y dy
sage: sa.display(eV)
*a = u/(u^4 + 2*u^2*v^2 + v^4) du + v/(u^4 + 2*u^2*v^2 + v^4) dv

Instead of calling the method hodge_dual() on the differential form, one can invoke the method hodge_star() of the metric:

sage: a.hodge_dual(g) == g.hodge_star(a)
True

For a 1-form and a Riemannian metric in dimension 2, the Hodge dual applied twice is minus the identity:

sage: ssa = sa.hodge_dual(g); ssa
1-form **a on the 2-dimensional differentiable manifold S^2
sage: ssa == -a
True

The Hodge dual of the metric volume 2-form is the constant scalar field 1 (considered as a 0-form):

sage: eps = g.volume_form(); eps
2-form eps_g on the 2-dimensional differentiable manifold S^2
sage: eps.display(eU)
eps_g = 4/(x^4 + y^4 + 2*(x^2 + 1)*y^2 + 2*x^2 + 1) dx∧dy
sage: eps.display(eV)
eps_g = -4/(u^4 + v^4 + 2*(u^2 + 1)*v^2 + 2*u^2 + 1) du∧dv
sage: seps = eps.hodge_dual(g); seps
Scalar field *eps_g on the 2-dimensional differentiable manifold S^2
sage: seps.display()
*eps_g: S^2 → ℝ
on U: (x, y) ↦ 1
on V: (u, v) ↦ 1

Hodge dual of a 1-form in the Euclidean space \(R^3\):

sage: M = Manifold(3, 'M', start_index=1)
sage: X.<x,y,z> = M.chart()
sage: g = M.metric('g')  # the Euclidean metric
sage: g[1,1], g[2,2], g[3,3] = 1, 1, 1
sage: var('Ax Ay Az')
(Ax, Ay, Az)
sage: a = M.one_form(Ax, Ay, Az, name='A')
sage: sa = a.hodge_dual(g) ; sa
2-form *A on the 3-dimensional differentiable manifold M
sage: sa.display()
*A = Az dx∧dy - Ay dx∧dz + Ax dy∧dz
sage: ssa = sa.hodge_dual(g) ; ssa
1-form **A on the 3-dimensional differentiable manifold M
sage: ssa.display()
**A = Ax dx + Ay dy + Az dz
sage: ssa == a  # must hold for a Riemannian metric in dimension 3
True

See the documentation of hodge_star() for more examples.

interior_product(qvect)#

Interior product with a multivector field.

If self is a differential form \(A\) of degree \(p\) and \(B\) is a multivector field of degree \(q\geq p\) on the same manifold, the interior product of \(A\) by \(B\) is the multivector field \(\iota_A B\) of degree \(q-p\) defined by

\[(\iota_A B)^{i_1\ldots i_{q-p}} = A_{k_1\ldots k_p} B^{k_1\ldots k_p i_1\ldots i_{q-p}}\]

Note

A.interior_product(B) yields the same result as A.contract(0,..., p-1, B, 0,..., p-1) (cf. contract()), but interior_product is more efficient, the alternating character of \(A\) being not used to reduce the computation in contract()

INPUT:

  • qvect – multivector field \(B\) (instance of MultivectorField); the degree of \(B\) must be at least equal to the degree of self

OUTPUT:

  • scalar field (case \(p=q\)) or MultivectorField (case \(p<q\)) representing the interior product \(\iota_A B\), where \(A\) is self

See also

interior_product() for the interior product of a multivector field with a differential form

EXAMPLES:

Interior product of a 1-form with a 2-vector field on the 2-sphere:

sage: M = Manifold(2, 'S^2', start_index=1) # the sphere S^2
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() # stereographic coord. North
sage: c_uv.<u,v> = V.chart() # stereographic coord. South
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) # The complement of the two poles
sage: e_xy = c_xy.frame() ; e_uv = c_uv.frame()
sage: a = M.one_form({e_xy: [y, x]}, name='a')
sage: a.add_comp_by_continuation(e_uv, W, c_uv)
sage: b = M.multivector_field(2, name='b')
sage: b[e_xy,1,2] = x*y
sage: b.add_comp_by_continuation(e_uv, W, c_uv)
sage: s = a.interior_product(b); s
Vector field i_a b on the 2-dimensional differentiable manifold S^2
sage: s.display(e_xy)
i_a b = -x^2*y ∂/∂x + x*y^2 ∂/∂y
sage: s.display(e_uv)
i_a b = (u^4*v - 3*u^2*v^3)/(u^6 + 3*u^4*v^2 + 3*u^2*v^4 + v^6) ∂/∂u
 + (3*u^3*v^2 - u*v^4)/(u^6 + 3*u^4*v^2 + 3*u^2*v^4 + v^6) ∂/∂v
sage: s == a.contract(b)
True

Interior product of a 2-form with a 2-vector field:

sage: a = M.diff_form(2, name='a')
sage: a[e_xy,1,2] = 4/(x^2+y^2+1)^2   # the standard area 2-form
sage: a.add_comp_by_continuation(e_uv, W, c_uv)
sage: s = a.interior_product(b); s
Scalar field i_a b on the 2-dimensional differentiable manifold S^2
sage: s.display()
i_a b: S^2 → ℝ
on U: (x, y) ↦ 8*x*y/(x^4 + y^4 + 2*(x^2 + 1)*y^2 + 2*x^2 + 1)
on V: (u, v) ↦ 8*u*v/(u^4 + v^4 + 2*(u^2 + 1)*v^2 + 2*u^2 + 1)

Some checks:

sage: s == a.contract(0, 1, b, 0, 1)
True
sage: s.restrict(U) == 2 * a[[e_xy,1,2]] * b[[e_xy,1,2]]
True
sage: s.restrict(V) == 2 * a[[e_uv,1,2]] * b[[e_uv,1,2]]
True
wedge(other)#

Exterior product with another differential form.

INPUT:

  • other – another differential form (on the same manifold)

OUTPUT:

  • instance of DiffForm representing the exterior product self other

EXAMPLES:

Exterior product of two 1-forms on the 2-sphere:

sage: M = Manifold(2, 'S^2', start_index=1) # the 2-dimensional sphere S^2
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 coord. (North and South)
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) # The complement of the two poles
sage: e_xy = c_xy.frame() ; e_uv = c_uv.frame()
sage: a = M.one_form({e_xy: [y, x]}, name='a')
sage: a.add_comp_by_continuation(e_uv, W, c_uv)
sage: b = M.one_form({e_xy: [x^2 + y^2, y]}, name='b')
sage: b.add_comp_by_continuation(e_uv, W, c_uv)
sage: c = a.wedge(b); c
2-form a∧b on the 2-dimensional differentiable manifold S^2
sage: c.display(e_xy)
a∧b = (-x^3 - (x - 1)*y^2) dx∧dy
sage: c.display(e_uv)
a∧b = -(v^2 - u)/(u^8 + 4*u^6*v^2 + 6*u^4*v^4 + 4*u^2*v^6 + v^8) du∧dv

If one of the two operands is unnamed, the result is unnamed too:

sage: b1 = M.diff_form(1)  # no name set
sage: b1[e_xy,:] = x^2 + y^2, y
sage: b1.add_comp_by_continuation(e_uv, W, c_uv)
sage: c1 = a.wedge(b1); c1
2-form on the 2-dimensional differentiable manifold S^2
sage: c1.display(e_xy)
(-x^3 - (x - 1)*y^2) dx∧dy

To give a name to the result, one shall use the method set_name():

sage: c1.set_name('c');  c1
2-form c on the 2-dimensional differentiable manifold S^2
sage: c1.display(e_xy)
c = (-x^3 - (x - 1)*y^2) dx∧dy

Wedging with scalar fields yields the multiplication from right:

sage: f = M.scalar_field(x, name='f')
sage: f.add_expr_by_continuation(c_uv, W)
sage: t = a.wedge(f)
sage: t.display()
f*a = x*y dx + x^2 dy
class sage.manifolds.differentiable.diff_form.DiffFormParal(vector_field_module: VectorFieldModule, degree: int, name: str | None = None, latex_name: str | None = None)#

Bases: FreeModuleAltForm, TensorFieldParal, DiffForm

Differential form with values on a parallelizable manifold.

Given a differentiable manifold \(U\), a differentiable map \(\Phi: U \rightarrow M\) to a parallelizable manifold \(M\) and a positive integer \(p\), a differential form of degree \(p\) (or \(p\)-form) along \(U\) with values on \(M\supset\Phi(U)\) is a differentiable map

\[a:\ U \longrightarrow T^{(0,p)}M\]

(\(T^{(0,p)}M\) being the tensor bundle of type \((0,p)\) over \(M\)) such that

\[\forall x \in U,\quad a(x) \in \Lambda^p(T_{\Phi(x)}^* M) ,\]

where \(T_{\Phi(x)}^* M\) is the dual of the tangent space to \(M\) at \(\Phi(x)\) and \(\Lambda^p\) stands for the exterior power of degree \(p\) (cf. ExtPowerDualFreeModule). In other words, \(a(x)\) is an alternating multilinear form of degree \(p\) of the tangent vector space \(T_{\Phi(x)} M\).

The standard case of a differential form on a manifold \(M\) 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\)).

Note

If \(M\) is not parallelizable, the class DiffForm must be used instead.

INPUT:

  • vector_field_module – free module \(\mathfrak{X}(U,\Phi)\) of vector fields along \(U\) with values on \(M\) via the map \(\Phi\)

  • degree – the degree of the differential form (i.e. its tensor rank)

  • name – (default: None) name given to the differential form

  • latex_name – (default: None) LaTeX symbol to denote the differential form; if none is provided, the LaTeX symbol is set to name

EXAMPLES:

A 2-form on a 4-dimensional manifold:

sage: M = Manifold(4, 'M')
sage: c_txyz.<t,x,y,z> = M.chart()
sage: a = M.diff_form(2, name='a') ; a
2-form a on the 4-dimensional differentiable manifold M
sage: a.parent()
Free module Omega^2(M) of 2-forms on the 4-dimensional differentiable
 manifold M

A differential form is a tensor field of purely covariant type:

sage: a.tensor_type()
(0, 2)

It is antisymmetric, its components being CompFullyAntiSym:

sage: a.symmetries()
no symmetry;  antisymmetry: (0, 1)
sage: a[0,1] = 2
sage: a[1,0]
-2
sage: a.comp()
Fully antisymmetric 2-indices components w.r.t. Coordinate frame (M, (∂/∂t,∂/∂x,∂/∂y,∂/∂z))
sage: type(a.comp())
<class 'sage.tensor.modules.comp.CompFullyAntiSym'>

Setting a component with repeated indices to a non-zero value results in an error:

sage: a[1,1] = 3
Traceback (most recent call last):
...
ValueError: by antisymmetry, the component cannot have a nonzero value
 for the indices (1, 1)
sage: a[1,1] = 0  # OK, albeit useless
sage: a[1,2] = 3  # OK

The expansion of a differential form with respect to a given coframe is displayed via the method display():

sage: a.display() # expansion with respect to the default coframe (dt, dx, dy, dz)
a = 2 dt∧dx + 3 dx∧dy
sage: latex(a.display()) # output for the notebook
a = 2 \mathrm{d} t\wedge \mathrm{d} x
 + 3 \mathrm{d} x\wedge \mathrm{d} y

Differential forms can be added or subtracted:

sage: b = M.diff_form(2)
sage: b[0,1], b[0,2], b[0,3] = (1,2,3)
sage: s = a + b ; s
2-form on the 4-dimensional differentiable manifold M
sage: a[:], b[:], s[:]
(
[ 0  2  0  0]  [ 0  1  2  3]  [ 0  3  2  3]
[-2  0  3  0]  [-1  0  0  0]  [-3  0  3  0]
[ 0 -3  0  0]  [-2  0  0  0]  [-2 -3  0  0]
[ 0  0  0  0], [-3  0  0  0], [-3  0  0  0]
)
sage: s = a - b ; s
2-form on the 4-dimensional differentiable manifold M
sage: s[:]
[ 0  1 -2 -3]
[-1  0  3  0]
[ 2 -3  0  0]
[ 3  0  0  0]

An example of 3-form is the volume element on \(\RR^3\) in Cartesian coordinates:

sage: M = Manifold(3, 'R3', latex_name=r'\RR^3', start_index=1)
sage: c_cart.<x,y,z> = M.chart()
sage: eps = M.diff_form(3, name='epsilon', latex_name=r'\epsilon')
sage: eps[1,2,3] = 1  # the only independent component
sage: eps[:] # all the components are set from the previous line:
[[[0, 0, 0], [0, 0, 1], [0, -1, 0]], [[0, 0, -1], [0, 0, 0], [1, 0, 0]],
 [[0, 1, 0], [-1, 0, 0], [0, 0, 0]]]
sage: eps.display()
epsilon = dx∧dy∧dz

Spherical components of the volume element from the tensorial change-of-frame formula:

sage: c_spher.<r,th,ph> = M.chart(r'r:[0,+oo) th:[0,pi]:\theta ph:[0,2*pi):\phi')
sage: spher_to_cart = c_spher.transition_map(c_cart,
....:                [r*sin(th)*cos(ph), r*sin(th)*sin(ph), r*cos(th)])
sage: cart_to_spher = spher_to_cart.set_inverse(sqrt(x^2+y^2+z^2),
....:                              atan2(sqrt(x^2+y^2),z), atan2(y, x))
Check of the inverse coordinate transformation:
  r == r  *passed*
  th == arctan2(r*sin(th), r*cos(th))  **failed**
  ph == arctan2(r*sin(ph)*sin(th), r*cos(ph)*sin(th))  **failed**
  x == x  *passed*
  y == y  *passed*
  z == z  *passed*
NB: a failed report can reflect a mere lack of simplification.
sage: eps.comp(c_spher.frame()) # computation of the components in the spherical frame
Fully antisymmetric 3-indices components w.r.t. Coordinate frame
 (R3, (∂/∂r,∂/∂th,∂/∂ph))
sage: eps.comp(c_spher.frame())[1,2,3, c_spher]
r^2*sin(th)
sage: eps.display(c_spher.frame())
epsilon = sqrt(x^2 + y^2 + z^2)*sqrt(x^2 + y^2) dr∧dth∧dph
sage: eps.display(c_spher.frame(), c_spher)
epsilon = r^2*sin(th) dr∧dth∧dph

As a shortcut of the above command, on can pass just the chart c_spher to display, the vector frame being then assumed to be the coordinate frame associated with the chart:

sage: eps.display(c_spher)
epsilon = r^2*sin(th) dr∧dth∧dph

The exterior product of two differential forms is performed via the method wedge():

sage: a = M.one_form(x*y*z, -z*x, y*z, name='A')
sage: b = M.one_form(cos(z), sin(x), cos(y), name='B')
sage: ab = a.wedge(b) ; ab
2-form A∧B on the 3-dimensional differentiable manifold R3
sage: ab[:]
[                         0  x*y*z*sin(x) + x*z*cos(z)  x*y*z*cos(y) - y*z*cos(z)]
[-x*y*z*sin(x) - x*z*cos(z)                          0   -(x*cos(y) + y*sin(x))*z]
[-x*y*z*cos(y) + y*z*cos(z)    (x*cos(y) + y*sin(x))*z                          0]
sage: ab.display()
A∧B = (x*y*z*sin(x) + x*z*cos(z)) dx∧dy + (x*y*z*cos(y) - y*z*cos(z)) dx∧dz
 - (x*cos(y) + y*sin(x))*z dy∧dz

Let us check the formula relating the exterior product to the tensor product for 1-forms:

sage: a.wedge(b) == a*b - b*a
True

The tensor product of a 1-form and a 2-form is not a 3-form but a tensor field of type \((0,3)\) with less symmetries:

sage: c = a*ab ; c
Tensor field A⊗(A∧B) of type (0,3) on the 3-dimensional differentiable
 manifold R3
sage: c.symmetries()  # the antisymmetry is only w.r.t. the last 2 arguments:
no symmetry;  antisymmetry: (1, 2)
sage: d = ab*a ; d
Tensor field (A∧B)⊗A of type (0,3) on the 3-dimensional differentiable
 manifold R3
sage: d.symmetries()  # the antisymmetry is only w.r.t. the first 2 arguments:
no symmetry;  antisymmetry: (0, 1)

The exterior derivative of a differential form is obtained by means of the method exterior_derivative():

sage: da = a.exterior_derivative() ; da
2-form dA on the 3-dimensional differentiable manifold R3
sage: da.display()
dA = -(x + 1)*z dx∧dy - x*y dx∧dz + (x + z) dy∧dz
sage: db = b.exterior_derivative() ; db
2-form dB on the 3-dimensional differentiable manifold R3
sage: db.display()
dB = cos(x) dx∧dy + sin(z) dx∧dz - sin(y) dy∧dz
sage: dab = ab.exterior_derivative() ; dab
3-form d(A∧B) on the 3-dimensional differentiable manifold R3

or by applying the function diff to the differential form:

sage: diff(a) is a.exterior_derivative()
True

As a 3-form over a 3-dimensional manifold, d(A∧B) is necessarily proportional to the volume 3-form:

sage: dab == dab[[1,2,3]]/eps[[1,2,3]]*eps
True

We may also check that the classical anti-derivation formula is fulfilled:

sage: dab == da.wedge(b) - a.wedge(db)
True

The Lie derivative of a 2-form is a 2-form:

sage: v = M.vector_field(y*z, -x*z, x*y, name='v')
sage: ab.lie_der(v)  # long time
2-form on the 3-dimensional differentiable manifold R3

Let us check Cartan formula, which expresses the Lie derivative in terms of exterior derivatives:

sage: ab.lie_der(v) == (v.contract(ab.exterior_derivative())  # long time
....:                   + v.contract(ab).exterior_derivative())
True

A 1-form on a \(\RR^3\):

sage: om = M.one_form(name='omega', latex_name=r'\omega'); om
1-form omega on the 3-dimensional differentiable manifold R3

A 1-form is of course a differential form:

sage: isinstance(om, sage.manifolds.differentiable.diff_form.DiffFormParal)
True
sage: om.parent()
Free module Omega^1(R3) of 1-forms on the 3-dimensional differentiable
 manifold R3
sage: om.tensor_type()
(0, 1)

Setting the components with respect to the manifold’s default frame:

sage: om[:] = (2*z, x, x-y)
sage: om[:]
[2*z, x, x - y]
sage: om.display()
omega = 2*z dx + x dy + (x - y) dz

A 1-form acts on vector fields:

sage: v = M.vector_field(x, 2*y, 3*z, name='V')
sage: om(v)
Scalar field omega(V) on the 3-dimensional differentiable manifold R3
sage: om(v).display()
omega(V): R3 → ℝ
   (x, y, z) ↦ 2*x*y + (5*x - 3*y)*z
   (r, th, ph) ↦ 2*r^2*cos(ph)*sin(ph)*sin(th)^2 + r^2*(5*cos(ph)
                    - 3*sin(ph))*cos(th)*sin(th)
sage: latex(om(v))
\omega\left(V\right)

The tensor product of two 1-forms is a tensor field of type \((0,2)\):

sage: a = M.one_form(1, 2, 3, name='A')
sage: b = M.one_form(6, 5, 4, name='B')
sage: c = a*b ; c
Tensor field A⊗B of type (0,2) on the 3-dimensional differentiable
 manifold R3
sage: c[:]
[ 6  5  4]
[12 10  8]
[18 15 12]
sage: c.symmetries()    # c has no symmetries:
no symmetry;  no antisymmetry
derivative()#

Compute the exterior derivative of self.

OUTPUT:

  • a DiffFormParal representing the exterior derivative of the differential form

EXAMPLES:

Exterior derivative of a 1-form on a 4-dimensional manifold:

sage: M = Manifold(4, 'M')
sage: c_txyz.<t,x,y,z> = M.chart()
sage: a = M.one_form(t*x*y*z, z*y**2, x*z**2, x**2 + y**2, name='A')
sage: da = a.exterior_derivative() ; da
2-form dA on the 4-dimensional differentiable manifold M
sage: da.display()
dA = -t*y*z dt∧dx - t*x*z dt∧dy - t*x*y dt∧dz
 + (-2*y*z + z^2) dx∧dy + (-y^2 + 2*x) dx∧dz
 + (-2*x*z + 2*y) dy∧dz
sage: latex(da)
\mathrm{d}A

The result is cached, i.e. is not recomputed unless a is changed:

sage: a.exterior_derivative() is da
True

Instead of invoking the method exterior_derivative(), one may use the global function diff:

sage: diff(a) is a.exterior_derivative()
True

The exterior derivative is nilpotent:

sage: dda = da.exterior_derivative() ; dda
3-form ddA on the 4-dimensional differentiable manifold M
sage: dda.display()
ddA = 0
sage: dda == 0
True

Let us check Cartan’s identity:

sage: v = M.vector_field(-y, x, t, z, name='v')
sage: a.lie_der(v) == v.contract(diff(a)) + diff(a(v)) # long time
True
exterior_derivative()#

Compute the exterior derivative of self.

OUTPUT:

  • a DiffFormParal representing the exterior derivative of the differential form

EXAMPLES:

Exterior derivative of a 1-form on a 4-dimensional manifold:

sage: M = Manifold(4, 'M')
sage: c_txyz.<t,x,y,z> = M.chart()
sage: a = M.one_form(t*x*y*z, z*y**2, x*z**2, x**2 + y**2, name='A')
sage: da = a.exterior_derivative() ; da
2-form dA on the 4-dimensional differentiable manifold M
sage: da.display()
dA = -t*y*z dt∧dx - t*x*z dt∧dy - t*x*y dt∧dz
 + (-2*y*z + z^2) dx∧dy + (-y^2 + 2*x) dx∧dz
 + (-2*x*z + 2*y) dy∧dz
sage: latex(da)
\mathrm{d}A

The result is cached, i.e. is not recomputed unless a is changed:

sage: a.exterior_derivative() is da
True

Instead of invoking the method exterior_derivative(), one may use the global function diff:

sage: diff(a) is a.exterior_derivative()
True

The exterior derivative is nilpotent:

sage: dda = da.exterior_derivative() ; dda
3-form ddA on the 4-dimensional differentiable manifold M
sage: dda.display()
ddA = 0
sage: dda == 0
True

Let us check Cartan’s identity:

sage: v = M.vector_field(-y, x, t, z, name='v')
sage: a.lie_der(v) == v.contract(diff(a)) + diff(a(v)) # long time
True
interior_product(qvect)#

Interior product with a multivector field.

If self is a differential form \(A\) of degree \(p\) and \(B\) is a multivector field of degree \(q\geq p\) on the same manifold, the interior product of \(A\) by \(B\) is the multivector field \(\iota_A B\) of degree \(q-p\) defined by

\[(\iota_A B)^{i_1\ldots i_{q-p}} = A_{k_1\ldots k_p} B^{k_1\ldots k_p i_1\ldots i_{q-p}}\]

Note

A.interior_product(B) yields the same result as A.contract(0,..., p-1, B, 0,..., p-1) (cf. contract()), but interior_product is more efficient, the alternating character of \(A\) being not used to reduce the computation in contract()

INPUT:

  • qvect – multivector field \(B\) (instance of MultivectorFieldParal); the degree of \(B\) must be at least equal to the degree of self

OUTPUT:

  • scalar field (case \(p=q\)) or MultivectorFieldParal (case \(p<q\)) representing the interior product \(\iota_A B\), where \(A\) is self

See also

interior_product() for the interior product of a multivector field with a differential form

EXAMPLES:

Interior product of a 1-form with a 2-vector field on a 3-dimensional manifold:

sage: M = Manifold(3, 'M', start_index=1)
sage: X.<x,y,z> = M.chart()
sage: a = M.one_form(2, 1+x, y*z, name='a')
sage: b = M.multivector_field(2, name='b')
sage: b[1,2], b[1,3], b[2,3] = y^2, z+x, -z^2
sage: s = a.interior_product(b); s
Vector field i_a b on the 3-dimensional differentiable
 manifold M
sage: s.display()
i_a b = (-(x + 1)*y^2 - x*y*z - y*z^2) ∂/∂x
 + (y*z^3 + 2*y^2) ∂/∂y + (-(x + 1)*z^2 + 2*x + 2*z) ∂/∂z
sage: s == a.contract(b)
True

Interior product of a 2-form with a 2-vector field:

sage: a = M.diff_form(2, name='a')
sage: a[1,2], a[1,3], a[2,3] = x*y, -3, z
sage: s = a.interior_product(b); s
Scalar field i_a b on the 3-dimensional differentiable manifold M
sage: s.display()
i_a b: M → ℝ
   (x, y, z) ↦ 2*x*y^3 - 2*z^3 - 6*x - 6*z
sage: s == a.contract(0,1,b,0,1)
True
wedge(other)#

Exterior product of self with another differential form.

INPUT:

  • other – another differential form

OUTPUT:

  • instance of DiffFormParal representing the exterior product self other

EXAMPLES:

Exterior product of a 1-form and a 2-form on a 3-dimensional manifold:

sage: M = Manifold(3, 'M', start_index=1)
sage: X.<x,y,z> = M.chart()
sage: a = M.one_form(2, 1+x, y*z, name='a')
sage: b = M.diff_form(2, name='b')
sage: b[1,2], b[1,3], b[2,3] = y^2, z+x, z^2
sage: a.display()
a = 2 dx + (x + 1) dy + y*z dz
sage: b.display()
b = y^2 dx∧dy + (x + z) dx∧dz + z^2 dy∧dz
sage: s = a.wedge(b); s
3-form a∧b on the 3-dimensional differentiable manifold M
sage: s.display()
a∧b = (-x^2 + (y^3 - x - 1)*z + 2*z^2 - x) dx∧dy∧dz

Check:

sage: s[1,2,3] == a[1]*b[2,3] + a[2]*b[3,1] + a[3]*b[1,2]
True

Wedging with scalar fields yields the multiplication from right:

sage: f = M.scalar_field(x, name='f')
sage: t = a.wedge(f)
sage: t.display()
f*a = 2*x dx + (x^2 + x) dy + x*y*z dz