def _new_comp(self, basis): r""" Create some (uninitialized) components of ``self`` in a given basis. This method, which is already implemented in :meth:`FreeModuleTensor._new_comp`, is redefined here for efficiency. INPUT: - ``basis`` -- basis of the free module on which ``self`` is defined OUTPUT: - an instance of :class:`~sage.tensor.modules.comp.CompFullyAntiSym`, or of :class:`~sage.tensor.modules.comp.Components` if the degree of ``self`` is 1. EXAMPLES:: sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') sage: a = M.alternating_contravariant_tensor(2, name='a') sage: a._new_comp(e) Fully antisymmetric 2-indices components w.r.t. Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring sage: a = M.alternating_contravariant_tensor(1) sage: a._new_comp(e) 1-index components w.r.t. Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring """ fmodule = self._fmodule # the base free module if self._tensor_rank == 1: return Components(fmodule._ring, basis, 1, start_index=fmodule._sindex, output_formatter=fmodule._output_formatter) return CompFullyAntiSym(fmodule._ring, basis, self._tensor_rank, start_index=fmodule._sindex, output_formatter=fmodule._output_formatter)
def exterior_derivative(self): r""" Compute the exterior derivative of ``self``. OUTPUT: - a :class:`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('A') sage: a[:] = (t*x*y*z, z*y**2, x*z**2, x**2 + y**2) 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 :meth:`exterior_derivative`, one may use the global function :func:`~sage.manifolds.utilities.exterior_derivative` or its alias :func:`~sage.manifolds.utilities.xder`:: sage: from sage.manifolds.utilities import xder sage: xder(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(name='v') sage: v[:] = -y, x, t, z sage: a.lie_der(v) == v.contract(xder(a)) + xder(a(v)) # long time True """ from sage.calculus.functional import diff from sage.tensor.modules.format_utilities import (format_unop_txt, format_unop_latex) from sage.tensor.modules.comp import CompFullyAntiSym from sage.manifolds.differentiable.vectorframe import CoordFrame fmodule = self._fmodule # shortcut rname = format_unop_txt('d', self._name) rlname = format_unop_latex(r'\mathrm{d}', self._latex_name) resu = fmodule.alternating_form(self._tensor_rank + 1, name=rname, latex_name=rlname) # 1/ List of all coordinate frames in which the components of self # are known coord_frames = [] for frame in self._components: if isinstance(frame, CoordFrame): coord_frames.append(frame) if not coord_frames: # A coordinate frame is searched, at the price of a change of # frame, privileging the frame of the domain's default chart dom = self._domain def_coordf = dom._def_chart._frame for frame in self._components: if (frame, def_coordf) in dom._frame_changes: self.comp(def_coordf, from_basis=frame) coord_frames = [def_coordf] break if not coord_frames: for chart in dom._atlas: if chart != dom._def_chart: # the case def_chart is # treated above coordf = chart._frame for frame in self._components: if (frame, coordf) in dom._frame_changes: self.comp(coordf, from_basis=frame) coord_frames[coordf] break if coord_frames: break # 2/ The computation: for frame in coord_frames: chart = frame._chart sc = self._components[frame] dc = CompFullyAntiSym(fmodule._ring, frame, self._tensor_rank + 1, start_index=fmodule._sindex, output_formatter=fmodule._output_formatter) for ind, val in sc._comp.items(): for i in fmodule.irange(): ind_d = (i, ) + ind if len(ind_d) == len(set(ind_d)): # all indices are different dc[[ind_d]] += \ val.coord_function(chart).diff(i).scalar_field() resu._components[frame] = dc return resu
def wedge(self, other): r""" Exterior product of ``self`` with the alternating form ``other``. INPUT: - ``other`` -- an alternating form OUTPUT: - instance of :class:`FreeModuleAltForm` representing the exterior product ``self/\other`` EXAMPLES: Exterior product of two linear forms:: sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') sage: a = M.linear_form('A') sage: a[:] = [1,-3,4] sage: b = M.linear_form('B') sage: b[:] = [2,-1,2] sage: c = a.wedge(b) ; c Alternating form A/\B of degree 2 on the Rank-3 free module M over the Integer Ring sage: c.display() A/\B = 5 e^0/\e^1 - 6 e^0/\e^2 - 2 e^1/\e^2 sage: latex(c) A\wedge B sage: latex(c.display()) A\wedge B = 5 e^0\wedge e^1 -6 e^0\wedge e^2 -2 e^1\wedge e^2 Test of the computation:: sage: a.wedge(b) == a*b - b*a True Exterior product of a linear form and an alternating form of degree 2:: sage: d = M.linear_form('D') sage: d[:] = [-1,2,4] sage: s = d.wedge(c) ; s Alternating form D/\A/\B of degree 3 on the Rank-3 free module M over the Integer Ring sage: s.display() D/\A/\B = 34 e^0/\e^1/\e^2 Test of the computation:: sage: s[0,1,2] == d[0]*c[1,2] + d[1]*c[2,0] + d[2]*c[0,1] True Let us check that the exterior product is associative:: sage: d.wedge(a.wedge(b)) == (d.wedge(a)).wedge(b) True and that it is graded anticommutative:: sage: a.wedge(b) == - b.wedge(a) True sage: d.wedge(c) == c.wedge(d) True """ from .format_utilities import is_atomic if not isinstance(other, FreeModuleAltForm): raise TypeError("the second argument for the exterior product " + "must be an alternating form") if other._tensor_rank == 0: return other*self if self._tensor_rank == 0: return self*other fmodule = self._fmodule basis = self.common_basis(other) if basis is None: raise ValueError("no common basis for the exterior product") rank_r = self._tensor_rank + other._tensor_rank cmp_s = self._components[basis] cmp_o = other._components[basis] cmp_r = CompFullyAntiSym(fmodule._ring, basis, rank_r, start_index=fmodule._sindex, output_formatter=fmodule._output_formatter) for ind_s, val_s in six.iteritems(cmp_s._comp): for ind_o, val_o in six.iteritems(cmp_o._comp): ind_r = ind_s + ind_o if len(ind_r) == len(set(ind_r)): # all indices are different cmp_r[[ind_r]] += val_s * val_o result = fmodule.alternating_form(rank_r) result._components[basis] = cmp_r if self._name is not None and other._name is not None: sname = self._name oname = other._name if not is_atomic(sname): sname = '(' + sname + ')' if not is_atomic(oname): oname = '(' + oname + ')' result._name = sname + '/\\' + oname if self._latex_name is not None and other._latex_name is not None: slname = self._latex_name olname = other._latex_name if not is_atomic(slname): slname = '(' + slname + ')' if not is_atomic(olname): olname = '(' + olname + ')' result._latex_name = slname + r'\wedge ' + olname return result
def _pullback_chart(diff_map, tensor): r""" Helper function performing the pullback on chart domains only. INPUT: - ``diff_map`` -- a restriction of ``self``, whose both domain and codomain are chart domains - ``tensor`` -- a covariant tensor field, whose domain is the codomain of ``diff_map`` OUTPUT: - the pull back of ``tensor`` by ``diff_map`` """ dom1 = diff_map._domain dom2 = diff_map._codomain ncov = tensor._tensor_type[1] resu_name = None resu_latex_name = None if diff_map._name is not None and tensor._name is not None: resu_name = diff_map._name + '_*(' + tensor._name + ')' if (diff_map._latex_name is not None and tensor._latex_name is not None): resu_latex_name = '{' + diff_map._latex_name + '}_*' + \ tensor._latex_name fmodule1 = dom1.vector_field_module() ring1 = fmodule1._ring si1 = fmodule1._sindex of1 = fmodule1._output_formatter si2 = dom2._sindex resu = fmodule1.tensor((0,ncov), name=resu_name, latex_name=resu_latex_name, sym=tensor._sym, antisym=tensor._antisym) for frame2 in tensor._components: if isinstance(frame2, CoordFrame): chart2 = frame2._chart for chart1 in dom1._atlas: if (chart1._domain is dom1 and (chart1, chart2) in diff_map._coord_expression): # Computation at the component level: frame1 = chart1._frame tcomp = tensor._components[frame2] if isinstance(tcomp, CompFullySym): ptcomp = CompFullySym(ring1, frame1, ncov, start_index=si1, output_formatter=of1) elif isinstance(tcomp, CompFullyAntiSym): ptcomp = CompFullyAntiSym(ring1, frame1, ncov, start_index=si1, output_formatter=of1) elif isinstance(tcomp, CompWithSym): ptcomp = CompWithSym(ring1, frame1, ncov, start_index=si1, output_formatter=of1, sym=tcomp.sym, antisym=tcomp.antisym) else: ptcomp = Components(ring1, frame1, ncov, start_index=si1, output_formatter=of1) phi = diff_map._coord_expression[(chart1, chart2)] jacob = phi.jacobian() # X2 coordinates expressed in terms of # X1 ones via the diff. map: coord2_1 = phi(*(chart1._xx)) for ind_new in ptcomp.non_redundant_index_generator(): res = 0 for ind_old in dom2.manifold().index_generator(ncov): ff = tcomp[[ind_old]].coord_function(chart2) t = chart1.function(ff(*coord2_1)) for i in range(ncov): t *= jacob[ind_old[i]-si2, ind_new[i]-si1] res += t ptcomp[ind_new] = res resu._components[frame1] = ptcomp return resu
def pushforward(self, tensor): r""" Pushforward operator associated with ``self``. In what follows, let `\Phi` denote the differentiable map, `M` its domain and `N` its codomain. INPUT: - ``tensor`` -- :class:`~sage.manifolds.differentiable.tensorfield.TensorField`; a fully contrariant tensor field `T` on `M`, i.e. a tensor field of type `(p, 0)`, with `p` a positive integer OUTPUT: - a :class:`~sage.manifolds.differentiable.tensorfield.TensorField` representing a fully contravariant tensor field along `M` with values in `N`, which is the pushforward of `T` by `\Phi` EXAMPLES: Pushforward of a vector field on the 2-sphere `S^2` to the Euclidean 3-space `\RR^3`, via the standard embedding of `S^2`:: sage: S2 = Manifold(2, 'S^2', start_index=1) sage: U = S2.open_subset('U') # domain of spherical coordinates sage: spher.<th,ph> = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') sage: R3 = Manifold(3, 'R^3', start_index=1) sage: cart.<x,y,z> = R3.chart() sage: Phi = U.diff_map(R3, {(spher, cart): [sin(th)*cos(ph), ....: sin(th)*sin(ph), cos(th)]}, name='Phi', latex_name=r'\Phi') sage: v = U.vector_field(name='v') sage: v[:] = 0, 1 sage: v.display() v = d/dph sage: pv = Phi.pushforward(v); pv Vector field Phi^*(v) along the Open subset U of the 2-dimensional differentiable manifold S^2 with values on the 3-dimensional differentiable manifold R^3 sage: pv.display() Phi^*(v) = -sin(ph)*sin(th) d/dx + cos(ph)*sin(th) d/dy Pushforward of a vector field on the real line to the `\RR^3`, via a helix embedding:: sage: R.<t> = RealLine() sage: Psi = R.diff_map(R3, [cos(t), sin(t), t], name='Psi', ....: latex_name=r'\Psi') sage: u = R.vector_field(name='u') sage: u[0] = 1 sage: u.display() u = d/dt sage: pu = Psi.pushforward(u); pu Vector field Psi^*(u) along the Real number line R with values on the 3-dimensional differentiable manifold R^3 sage: pu.display() Psi^*(u) = -sin(t) d/dx + cos(t) d/dy + d/dz """ from sage.tensor.modules.comp import (Components, CompWithSym, CompFullySym, CompFullyAntiSym) from sage.manifolds.differentiable.tensorfield_paral import TensorFieldParal vmodule = tensor.base_module() dest_map = vmodule.destination_map() dom1 = tensor.domain() ambient_dom1 = dest_map.codomain() if not ambient_dom1.is_subset(self._domain): raise ValueError("the {} does not take its ".format(tensor) + "values on the domain of the {}".format(self)) (ncon, ncov) = tensor.tensor_type() if ncov != 0: raise ValueError("the pushforward cannot be taken on a tensor " + "with some covariant part") if ncon == 0: raise ValueError("the pushforward cannot be taken on a scalar " + "field") if dest_map != dom1.identity_map(): raise NotImplementedError("the case of a non-trivial destination" + " map is not implemented yet") if not isinstance(tensor, TensorFieldParal): raise NotImplementedError("the case of a non-parallelizable " + "domain is not implemented yet") # A pair of charts (chart1, chart2) where the computation # is feasable is searched, privileging the default chart of the # map's domain for chart1 chart1 = None; chart2 = None def_chart1 = dom1.default_chart() def_chart2 = self._codomain.default_chart() if (def_chart1._frame in tensor._components and (def_chart1, def_chart2) in self._coord_expression): chart1 = def_chart1 chart2 = def_chart2 else: for (chart1n, chart2n) in self._coord_expression: if (chart2n == def_chart2 and chart1n._frame in tensor._components): chart1 = chart1n chart2 = def_chart2 break if chart2 is None: # It is not possible to have def_chart2 as chart for # expressing the result; any other chart is then looked for: for (chart1n, chart2n) in self._coord_expression: if chart1n._frame in tensor._components: chart1 = chart1n chart2 = chart2n break if chart1 is None: # Computations of components are attempted chart1 = dom1.default_chart() tensor.comp(chart1.frame()) for chart2n in self._codomain.atlas(): try: self.coord_functions(chart1, chart2n) chart2 = chart2n break except ValueError: pass else: raise ValueError("no pair of charts could be found to " + "compute the pushforward of " + "the {} by the {}".format(tensor, self)) # Vector field module for the result: fmodule2 = dom1.vector_field_module(dest_map=self) # frame2 = fmodule2.basis(from_frame=chart2.frame()) si1 = dom1.start_index() si2 = fmodule2._sindex ring2 = fmodule2._ring of2 = fmodule2._output_formatter # Computation at the component level: tcomp = tensor._components[chart1.frame()] # Construction of the pushforward components (ptcomp): if isinstance(tcomp, CompFullySym): ptcomp = CompFullySym(ring2, frame2, ncon, start_index=si2, output_formatter=of2) elif isinstance(tcomp, CompFullyAntiSym): ptcomp = CompFullyAntiSym(ring2, frame2, ncon, start_index=si2, output_formatter=of2) elif isinstance(tcomp, CompWithSym): ptcomp = CompWithSym(ring2, frame2, ncon, start_index=si2, output_formatter=of2, sym=tcomp._sym, antisym=tcomp._antisym) else: ptcomp = Components(ring2, frame2, ncon, start_index=si2, output_formatter=of2) # Computation of the pushforward components: jacob = self.differential_functions(chart1=chart1, chart2=chart2) si2 = chart2.domain().start_index() for ind_new in ptcomp.non_redundant_index_generator(): res = 0 for ind_old in dom1.index_generator(ncon): t = tcomp[[ind_old]].coord_function(chart1) for i in range(ncon): t *= jacob[ind_new[i]-si2, ind_old[i]-si1] res += t ptcomp[ind_new] = res # Name of the result: resu_name = None resu_latex_name = None if self._name is not None and tensor._name is not None: resu_name = self._name + '^*(' + tensor._name + ')' if self._latex_name is not None and tensor._latex_name is not None: resu_latex_name = self._latex_name + '^*' + tensor._latex_name # Creation of the result with the components obtained above: resu = fmodule2.tensor_from_comp((ncon, 0), ptcomp, name=resu_name, latex_name=resu_latex_name) return resu
def wedge(self, other): r""" Exterior product of ``self`` with the alternating contravariant tensor ``other``. INPUT: - ``other`` -- an alternating contravariant tensor OUTPUT: - instance of :class:`AlternatingContrTensor` representing the exterior product ``self ∧ other`` EXAMPLES: Exterior product of two module elements:: sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') sage: a = M([1,-3,4], basis=e, name='A') sage: b = M([2,-1,2], basis=e, name='B') sage: c = a.wedge(b) ; c Alternating contravariant tensor A∧B of degree 2 on the Rank-3 free module M over the Integer Ring sage: c.display() A∧B = 5 e_0∧e_1 - 6 e_0∧e_2 - 2 e_1∧e_2 sage: latex(c) A\wedge B sage: latex(c.display()) A\wedge B = 5 e_{0}\wedge e_{1} -6 e_{0}\wedge e_{2} -2 e_{1}\wedge e_{2} Test of the computation:: sage: a.wedge(b) == a*b - b*a True Exterior product of a module element and an alternating contravariant tensor of degree 2:: sage: d = M([-1,2,4], basis=e, name='D') sage: s = d.wedge(c) ; s Alternating contravariant tensor D∧A∧B of degree 3 on the Rank-3 free module M over the Integer Ring sage: s.display() D∧A∧B = 34 e_0∧e_1∧e_2 Test of the computation:: sage: s[0,1,2] == d[0]*c[1,2] + d[1]*c[2,0] + d[2]*c[0,1] True Let us check that the exterior product is associative:: sage: d.wedge(a.wedge(b)) == (d.wedge(a)).wedge(b) True and that it is graded anticommutative:: sage: a.wedge(b) == - b.wedge(a) True sage: d.wedge(c) == c.wedge(d) True """ from sage.typeset.unicode_characters import unicode_wedge from .format_utilities import is_atomic if not isinstance(other, AlternatingContrTensor): raise TypeError("the second argument for the exterior product " + "must be an alternating contravariant tensor") if other._tensor_rank == 0: return other * self fmodule = self._fmodule basis = self.common_basis(other) if basis is None: raise ValueError("no common basis for the exterior product") rank_r = self._tensor_rank + other._tensor_rank cmp_s = self._components[basis] cmp_o = other._components[basis] cmp_r = CompFullyAntiSym(fmodule._ring, basis, rank_r, start_index=fmodule._sindex, output_formatter=fmodule._output_formatter) for ind_s, val_s in cmp_s._comp.items(): for ind_o, val_o in cmp_o._comp.items(): ind_r = ind_s + ind_o if len(ind_r) == len(set(ind_r)): # all indices are different cmp_r[[ind_r]] += val_s * val_o result = fmodule.alternating_contravariant_tensor(rank_r) result._components[basis] = cmp_r if self._name is not None and other._name is not None: sname = self._name oname = other._name if not is_atomic(sname): sname = '(' + sname + ')' if not is_atomic(oname): oname = '(' + oname + ')' result._name = sname + unicode_wedge + oname if self._latex_name is not None and other._latex_name is not None: slname = self._latex_name olname = other._latex_name if not is_atomic(slname): slname = r'\left(' + slname + r'\right)' if not is_atomic(olname): olname = r'\left(' + olname + r'\right)' result._latex_name = slname + r'\wedge ' + olname return result
def exterior_der(self): r""" Compute the exterior derivative of the differential form. OUTPUT: - the exterior derivative of ``self``. EXAMPLE: 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('A') sage: a[:] = (t*x*y*z, z*y**2, x*z**2, x**2 + y**2) sage: da = a.exterior_der() ; da 2-form 'dA' on the 4-dimensional 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 exterior derivative is nilpotent:: sage: dda = da.exterior_der() ; dda 3-form 'ddA' on the 4-dimensional manifold 'M' sage: dda.display() ddA = 0 sage: dda == 0 True """ from sage.calculus.functional import diff from sage.tensor.modules.format_utilities import format_unop_txt, \ format_unop_latex from sage.tensor.modules.comp import CompFullyAntiSym from vectorframe import CoordFrame if self._exterior_derivative is None: # A new computation is necessary: fmodule = self._fmodule # shortcut rname = format_unop_txt('d', self._name) rlname = format_unop_latex(r'\mathrm{d}', self._latex_name) self._exterior_derivative = fmodule.alternating_form( self._tensor_rank + 1, name=rname, latex_name=rlname) # 1/ List of all coordinate frames in which the components of self # are known coord_frames = [] for frame in self._components: if isinstance(frame, CoordFrame): coord_frames.append(frame) if coord_frames == []: # A coordinate frame is searched, at the price of a change of # frame, priveleging the frame of the domain's default chart dom = self._domain def_coordf = dom._def_chart._frame for frame in self._components: if (frame, def_coordf) in dom._frame_changes: self.comp(def_coordf, from_basis=frame) coord_frames = [def_coordf] break if coord_frames == []: for chart in dom._atlas: if chart != dom._def_chart: # the case def_chart is treated above coordf = chart._frame for frame in self._components: if (frame, coordf) in dom._frame_changes: self.comp(coordf, from_basis=frame) coord_frames[coordf] break if coord_frames != []: break # 2/ The computation: for frame in coord_frames: chart = frame._chart sc = self._components[frame] dc = CompFullyAntiSym( fmodule._ring, frame, self._tensor_rank + 1, start_index=fmodule._sindex, output_formatter=fmodule._output_formatter) for ind, val in sc._comp.iteritems(): for i in fmodule.irange(): ind_d = (i, ) + ind if len(ind_d) == len(set(ind_d)): # all indices are different dc[[ind_d]] += \ val.function_chart(chart).diff(i).scalar_field() self._exterior_derivative._components[frame] = dc return self._exterior_derivative
def pushforward(self, tensor): r""" Pushforward operator associated with the embedding. INPUT: - ``tensor`` -- instance of :class:`~sage.geometry.manifolds.tensorfield.TensorField` representing a fully contravariant tensor field `T` on the submanifold, i.e. a tensor field of type (k,0), with k a positive integer. The case k=0 corresponds to a scalar field. OUTPUT: - instance of :class:`~sage.geometry.manifolds.tensorfield.TensorField` representing a field of fully contravariant tensors of the ambient manifold, field defined on the subset occupied by the submanifold. EXAMPLES: Pushforward of a vector field defined on `S^2`, submanifold of `\RR^3`:: sage: Manifold._clear_cache_() # for doctests only sage: M = Manifold(3, 'R^3', r'\RR^3', start_index=1) sage: c_cart.<x,y,z> = M.chart() # Cartesian coordinates on R^3 sage: S = M.submanifold(2, 'S^2', start_index=1) sage: U = S.open_subset('U') # U = S minus two poles sage: c_spher.<th,ph> = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') # spherical coordinates on U sage: S.def_embedding( S.diff_mapping(M, [sin(th)*cos(ph), sin(th)*sin(ph), cos(th)], name='i', latex_name=r'\iota') ) sage: v = U.vector_field(name='v') sage: v[2] = 1 ; v.display() # azimuthal vector field on S^2 v = d/dph sage: iv = S.pushforward(v) ; iv vector field 'i_*(v)' along the open subset 'U' of the 2-dimensional submanifold 'S^2' of the 3-dimensional manifold 'R^3' with values on the 3-dimensional manifold 'R^3' sage: iv.display() i_*(v) = -sin(ph)*sin(th) d/dx + cos(ph)*sin(th) d/dy The components of the pushforward vector are scalar fields on the submanifold:: sage: iv.comp()[[1]] scalar field on the open subset 'U' of the 2-dimensional submanifold 'S^2' of the 3-dimensional manifold 'R^3' Pushforward of a tangent vector to a helix, submanifold of `\RR^3`:: sage: H = M.submanifold(1, 'H') sage: c_t.<t> = H.chart() sage: H.def_embedding( H.diff_mapping(M, [cos(t), sin(t), t], name='iH') ) sage: u = H.vector_field(name='u') sage: u[0] = 1 ; u.display() # tangent vector to the helix u = d/dt sage: iu = H.pushforward(u) ; iu vector field 'iH_*(u)' along the 1-dimensional submanifold 'H' of the 3-dimensional manifold 'R^3' with values on the 3-dimensional manifold 'R^3' sage: iu.display() iH_*(u) = -sin(t) d/dx + cos(t) d/dy + d/dz """ from tensorfield import TensorFieldParal from sage.tensor.modules.comp import Components, CompWithSym, \ CompFullySym, CompFullyAntiSym dom1 = tensor._domain if not dom1.is_subset(self): raise TypeError("The tensor field is not defined on " + str(self)) (ncon, ncov) = tensor._tensor_type if ncov != 0: raise TypeError("The pushforward cannot be taken on a tensor " + "with some covariant part.") embed = self._embedding resu_name = None ; resu_latex_name = None if embed._name is not None and tensor._name is not None: resu_name = embed._name + '_*(' + tensor._name + ')' if embed._latex_name is not None and tensor._latex_name is not None: resu_latex_name = embed._latex_name + '_*' + tensor._latex_name if ncon == 0: raise NotImplementedError("The case of a scalar field is not " + "implemented yet.") if not isinstance(tensor, TensorFieldParal): raise NotImplementedError("The case of a non-parallelizable " + "domain is not implemented yet.") # A pair of charts (chart1, chart2) where the computation # is feasable is searched, privileging the default chart of the # start domain for chart1 chart1 = None; chart2 = None def_chart1 = dom1._def_chart def_chart2 = embed._codomain._def_chart if def_chart1._frame in tensor._components and \ (def_chart1, def_chart2) in embed._coord_expression: chart1 = def_chart1 chart2 = def_chart2 else: for (chart1n, chart2n) in embed._coord_expression: if chart2n == def_chart2 and \ chart1n._frame in tensor._components: chart1 = chart1n chart2 = def_chart2 break if chart2 is None: # It is not possible to have def_chart2 as chart for # expressing the result; any other chart is then looked for: for (chart1n, chart2n) in self._coord_expression: if chart1n._frame in tensor._components: chart1 = chart1n chart2 = chart2n break if chart1 is None: raise ValueError("No common chart could be find to compute " + "the pushforward of the tensor field.") fmodule2 = dom1.vector_field_module(dest_map=embed) frame2 = fmodule2.basis(from_frame=chart2._frame) si1 = dom1._manifold._sindex si2 = fmodule2._sindex ring2 = fmodule2._ring of2 = fmodule2._output_formatter # Computation at the component level: tcomp = tensor._components[chart1._frame] # Construction of the pushforward components (ptcomp): if isinstance(tcomp, CompFullySym): ptcomp = CompFullySym(ring2, frame2, ncon, start_index=si2, output_formatter=of2) elif isinstance(tcomp, CompFullyAntiSym): ptcomp = CompFullyAntiSym(ring2, frame2, ncon, start_index=si2, output_formatter=of2) elif isinstance(tcomp, CompWithSym): ptcomp = CompWithSym(ring2, frame2, ncon, start_index=si2, output_formatter=of2, sym=tcomp._sym, antisym=tcomp._antisym) else: ptcomp = Components(ring2, frame2, ncon, start_index=si2, output_formatter=of2) phi = embed._coord_expression[(chart1, chart2)] jacob = phi.jacobian() # X2 coordinates expressed in terms of X1 ones via the mapping: # coord2_1 = phi(*(chart1._xx)) si1 = self._sindex si2 = self._ambient_manifold._sindex for ind_new in ptcomp.non_redundant_index_generator(): res = 0 for ind_old in self.index_generator(ncon): t = tcomp[[ind_old]].function_chart(chart1) for i in range(ncon): t *= jacob[ind_new[i]-si2][ind_old[i]-si1] res += t ptcomp[ind_new] = res resu = fmodule2.tensor_from_comp((ncon, 0), ptcomp, name=resu_name, latex_name=resu_latex_name) return resu