def _new_comp(self, frame_name): r""" Create some components in the given frame. """ from component import Components, CompFullyAntiSym if self.rank == 1: return Components(self.manifold, 1, frame_name) else: return CompFullyAntiSym(self.manifold, self.rank, frame_name)
def _new_comp(self, frame): r""" Create some components in the given frame. """ from component import Components, CompFullyAntiSym if self.rank == 1: return Components(frame, 1) else: return CompFullyAntiSym(frame, self.rank)
def pushforward(self, tensor): r""" Pushforward operator associated with the embedding. INPUT: - ``tensor`` -- instance of :class:`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:`TensorField` representing a field of fully contravariant tensors of the ambient manifold, field defined on the domain occupied by the submanifold. EXAMPLES: Pushforward of a vector field defined on `S^2`, submanifold of `\RR^3`:: sage: M = Manifold(3, 'R^3', r'\RR^3', start_index=1) sage: c_cart.<x,y,z> = M.chart('x y z') # Cartesian coordinates on R^3 sage: S = Submanifold(M, 2, 'S^2', start_index=1) sage: U = S.open_domain('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( DiffMapping(S, M, [sin(th)*cos(ph), sin(th)*sin(ph), cos(th)], name='i', latex_name=r'\iota') ) sage: v = VectorField(S, 'v') sage: v[2] = 1 ; v.view() # azimuthal vector field on S^2 v = d/dph sage: iv = S.pushforward(v) ; iv vector field 'i_*(v)' on the domain 'S^2' on the 3-dimensional manifold 'R^3' sage: iv.view(c_cart.frame, c_spher) # the pushforward expanded on the Cartesian frame, with components expressed in terms of (th,ph) coordinates 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(c_cart.frame)[[1]] scalar field on the open domain 'U' on 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 = Submanifold(M, 1, 'H') sage: c_t.<t> = H.chart('t') sage: H.def_embedding( DiffMapping(H, M, [cos(t), sin(t), t], name='iH') ) sage: u = VectorField(H, 'u') sage: u[0] = 1 ; u.view() # tangent vector to the helix u = d/dt sage: iu = H.pushforward(u) ; iu vector field 'iH_*(u)' on the domain 'H' on the 3-dimensional manifold 'R^3' sage: iu.view(c_cart.frame, c_t) iH_*(u) = -sin(t) d/dx + cos(t) d/dy + d/dz """ from component import Components, CompWithSym, CompFullySym, \ CompFullyAntiSym if tensor.manifold != self: raise TypeError("The tensor field is not defined on the " + "submanifold.") (ncon, ncov) = tensor.tensor_type if ncov != 0: raise TypeError("The pushforward cannot be taken on a tensor " + "with some covariant part.") dom1 = tensor.domain 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.") # 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 = self.ambient_manifold.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.") frame1 = chart1.frame frame2 = chart2.frame # Computation at the component level: tcomp = tensor.components[frame1] # Construction of the pushforward components (ptcomp): if isinstance(tcomp, CompFullySym): ptcomp = CompFullySym(frame2, ncon) elif isinstance(tcomp, CompFullyAntiSym): ptcomp = CompFullyAntiSym(frame2, ncon) elif isinstance(tcomp, CompWithSym): ptcomp = CompWithSym(frame2, ncon, sym=tcomp.sym, antisym=tcomp.antisym) else: ptcomp = Components(frame2, ncon) 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 = ptcomp.tensor_field((ncon, 0), name=resu_name, latex_name=resu_latex_name) resu.domain = self.domain_amb # the domain is resctrited to the submanifold return resu
def exterior_der(self, chart_name=None): r""" Compute the exterior derivative of the differential form. INPUT: - ``chart_name`` -- (default: None): name of the chart used for the computation; if none is provided, the computation is performed in a coordinate basis where the components are known, or computable by a component transformation, privileging the domain's default chart. 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('t x y z', 'coord_txyz') sage: a = OneForm(m, '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.view() 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.view() ddA = 0 sage: dda == 0 True """ from sage.calculus.functional import diff from utilities import format_unop_txt, format_unop_latex if self._exterior_derivative is None: # A new computation is necessary: if chart_name is None: frame_name = self.pick_a_coord_basis() if frame_name is None: raise ValueError("No coordinate basis could be found for " + "the differential form components.") chart_name = frame_name[:-2] chart = self.domain.atlas[chart_name] else: chart = self.domain.atlas[chart_name] frame_name = chart.frame.name if frame_name not in self.components: raise ValueError("Components in the frame " + frame_name + " have not been defined.") n = self.manifold.dim si = self.manifold.sindex sc = self.components[frame_name] dc = CompFullyAntiSym(self.domain.frames[frame_name], self.rank+1) for ind, val in sc._comp.items(): for i in range(n): ind_d = (i+si,) + ind if len(ind_d) == len(set(ind_d)): # all indices are different dc[[ind_d]] += val.function_chart(chart_name).diff(chart.xx[i]) dc._del_zeros() # Name and LaTeX name of the result (rname and rlname): rname = format_unop_txt('d', self.name) rlname = format_unop_latex(r'\mathrm{d}', self.latex_name) # Final result self._exterior_derivative = DiffForm(self.domain, self.rank+1, rname, rlname) self._exterior_derivative.components[frame_name] = dc return self._exterior_derivative
def pullback(self, tensor): r""" Pullback operator associated with the differentiable mapping. INPUT: - ``tensor`` -- instance of :class:`TensorField` representing a fully covariant tensor field `T` on the *arrival* domain, i.e. a tensor field of type (0,p), with p a positive or zero integer. The case p=0 corresponds to a scalar field. OUTPUT: - instance of :class:`TensorField` representing a fully covariant tensor field on the *start* domain that is the pullback of `T` given by ``self``. EXAMPLES: Pullback on `S^2` of a scalar field defined on `R^3`:: sage: m = Manifold(2, 'S^2', start_index=1) sage: c_spher.<th,ph> = m.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') # spherical coord. on S^2 sage: n = Manifold(3, 'R^3', r'\RR^3', start_index=1) sage: c_cart.<x,y,z> = n.chart('x y z') # Cartesian coord. on R^3 sage: Phi = DiffMapping(m, n, (sin(th)*cos(ph), sin(th)*sin(ph), cos(th)), name='Phi', latex_name=r'\Phi') sage: f = ScalarField(n, x*y*z, name='f') ; f scalar field 'f' on the 3-dimensional manifold 'R^3' sage: f.view() f: (x, y, z) |--> x*y*z sage: pf = Phi.pullback(f) ; pf scalar field 'Phi_*(f)' on the 2-dimensional manifold 'S^2' sage: pf.view() Phi_*(f): (th, ph) |--> cos(ph)*cos(th)*sin(ph)*sin(th)^2 Pullback on `S^2` of the standard Euclidean metric on `R^3`:: sage: g = SymBilinFormField(n, 'g') sage: g[1,1], g[2,2], g[3,3] = 1, 1, 1 sage: g.view() g = dx*dx + dy*dy + dz*dz sage: pg = Phi.pullback(g) ; pg field of symmetric bilinear forms 'Phi_*(g)' on the 2-dimensional manifold 'S^2' sage: pg.view() Phi_*(g) = dth*dth + sin(th)^2 dph*dph Pullback on `S^2` of a 3-form on `R^3`:: sage: a = DiffForm(n, 3, 'A') sage: a[1,2,3] = f sage: a.view() A = x*y*z dx/\dy/\dz sage: pa = Phi.pullback(a) ; pa 3-form 'Phi_*(A)' on the 2-dimensional manifold 'S^2' sage: pa.view() # should be zero (as any 3-form on a 2-dimensional manifold) Phi_*(A) = 0 """ from scalarfield import ScalarField from vectorframe import CoordFrame from rank2field import SymBilinFormField from diffform import DiffForm, OneForm if not isinstance(tensor, TensorField): raise TypeError("The argument 'tensor' must be a tensor field.") dom1 = self.domain1 dom2 = self.domain2 if not tensor.domain.is_subdomain(dom2): raise TypeError("The tensor field is not defined on the mapping " + "arrival domain.") (ncon, ncov) = tensor.tensor_type if ncon != 0: raise TypeError("The pullback cannot be taken on a tensor " + "with some contravariant part.") 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 if ncov == 0: # Case of a scalar field # ---------------------- resu = ScalarField(dom1, name=resu_name, latex_name=resu_latex_name) for chart2 in tensor.express: for chart1 in dom1.atlas: if (chart1, chart2) in self.coord_expression: phi = self.coord_expression[(chart1, chart2)] coord1 = chart1.xx ff = tensor.express[chart2] resu.add_expr( ff(*(phi(*coord1))), chart1) return resu else: # Case of tensor field of rank >= 1 # --------------------------------- if isinstance(tensor, OneForm): resu = OneForm(dom1, name=resu_name, latex_name=resu_latex_name) elif isinstance(tensor, DiffForm): resu = DiffForm(dom1, ncov, name=resu_name, latex_name=resu_latex_name) elif isinstance(tensor, SymBilinFormField): resu = SymBilinFormField(dom1, name=resu_name, latex_name=resu_latex_name) else: resu = TensorField(dom1, 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, chart2) in self.coord_expression: # Computation at the component level: frame1 = chart1.frame tcomp = tensor.components[frame2] if isinstance(tcomp, CompFullySym): ptcomp = CompFullySym(frame1, ncov) elif isinstance(tcomp, CompFullyAntiSym): ptcomp = CompFullyAntiSym(frame1, ncov) elif isinstance(tcomp, CompWithSym): ptcomp = CompWithSym(frame1, ncov, sym=tcomp.sym, antisym=tcomp.antisym) else: ptcomp = Components(frame1, ncov) phi = self.coord_expression[(chart1, chart2)] jacob = phi.jacobian() # X2 coordinates expressed in terms of X1 ones via the mapping: coord2_1 = phi(*(chart1.xx)) si1 = dom1.manifold.sindex si2 = dom2.manifold.sindex for ind_new in ptcomp.non_redundant_index_generator(): res = 0 for ind_old in dom2.manifold.index_generator(ncov): ff = tcomp[[ind_old]].function_chart(chart2) t = FunctionChart(chart1, 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 wedge(self, other): r""" Compute the exterior product with another differential form. INPUT: - ``other``: another differential form OUTPUT: - the exterior product self/\\other. EXAMPLES: Exterior product of two 1-forms on a 3-dimensional manifold:: sage: m = Manifold(3, 'M') sage: c_xyz = Chart(m, 'x y z', 'coord_xyz') sage: a = OneForm(m, 'A') sage: a[:] = (x, y, z) sage: b = OneForm(m, 'B') sage: b[2] = z^2 sage: a.show() ; b.show() A = x dx + y dy + z dz B = z^2 dz sage: h = a.wedge(b) ; h 2-form 'A/\B' on the 3-dimensional manifold 'M' sage: h.show() A/\B = x*z^2 dx/\dz + y*z^2 dy/\dz sage: latex(h) A\wedge B sage: latex(h.show()) A\wedge B = x z^{2} \mathrm{d} x\wedge\mathrm{d} z + y z^{2} \mathrm{d} y\wedge\mathrm{d} z The exterior product of two 1-forms is antisymmetric:: sage: (b.wedge(a)).show() B/\A = -x*z^2 dx/\dz - y*z^2 dy/\dz sage: a.wedge(b) == - b.wedge(a) True sage: (a.wedge(b) + b.wedge(a))[:] # for the skeptical mind [0 0 0] [0 0 0] [0 0 0] sage: a.wedge(a) == 0 True The exterior product of a 2-form by a 1-form:: sage: c = OneForm(m, 'C') sage: c[1] = y^3 ; c.show() C = y^3 dy sage: g = h.wedge(c) ; g 3-form 'A/\B/\C' on the 3-dimensional manifold 'M' sage: g.show() A/\B/\C = -x*y^3*z^2 dx/\dy/\dz sage: g[:] [[[0, 0, 0], [0, 0, -x*y^3*z^2], [0, x*y^3*z^2, 0]], [[0, 0, x*y^3*z^2], [0, 0, 0], [-x*y^3*z^2, 0, 0]], [[0, -x*y^3*z^2, 0], [x*y^3*z^2, 0, 0], [0, 0, 0]]] The exterior product of a 2-form by a 1-form is symmetric:: sage: h.wedge(c) == c.wedge(h) True """ from utilities import is_atomic if not isinstance(other, DiffForm): raise TypeError("The second argument for the exterior product " + "must be a differential form.") if other.rank == 0: return other * self if self.rank == 0: return self * other frame_name = self.common_frame(other) if frame_name is None: raise ValueError("No common frame for the exterior product.") rank_r = self.rank + other.rank cmp_s = self.components[frame_name] cmp_o = other.components[frame_name] cmp_r = CompFullyAntiSym(self.manifold, rank_r, frame_name) 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 = DiffForm(self.manifold, rank_r) result.components[frame_name] = 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 exterior_der(self, chart_name=None): r""" Compute the exterior derivative of the differential form. INPUT: - ``chart_name`` -- (default: None): name of the chart used for the computation; if none is provided, the computation is performed in a coordinate basis where the components are known, or computable by a component transformation, privileging the manifold's default chart. 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 = Chart(m, 't x y z', 'coord_txyz') sage: a = OneForm(m, '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.show() 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.show() ddA = 0 sage: dda == 0 True """ from sage.calculus.functional import diff from utilities import format_unop_txt, format_unop_latex if self._exterior_derivative is None: # A new computation is necessary: if chart_name is None: frame_name = self.pick_a_coord_basis() if frame_name is None: raise ValueError( "No coordinate basis could be found for " + "the differential form components.") chart_name = frame_name[:-2] chart = self.manifold.atlas[chart_name] else: chart = self.manifold.atlas[chart_name] frame_name = chart.frame.name if frame_name not in self.components: raise ValueError("Components in the frame " + frame_name + " have not been defined.") n = self.manifold.dim si = self.manifold.sindex sc = self.components[frame_name] dc = CompFullyAntiSym(self.manifold, self.rank + 1, frame_name) for ind, val in sc._comp.items(): for i in range(n): ind_d = (i + si, ) + ind if len(ind_d) == len( set(ind_d)): # all indices are different dc[[ind_d]] += val.function_chart(chart_name).diff( chart.xx[i]) dc._del_zeros() # Name and LaTeX name of the result (rname and rlname): rname = format_unop_txt('d', self.name) rlname = format_unop_latex(r'\mathrm{d}', self.latex_name) # Final result self._exterior_derivative = DiffForm(self.manifold, self.rank + 1, rname, rlname) self._exterior_derivative.components[frame_name] = dc return self._exterior_derivative
def pullback(self, tensor): r""" Pullback operator associated with the differentiable mapping. INPUT: - ``tensor`` -- instance of :class:`TensorField` representing a fully covariant tensor field `T` on the *arrival* manifold, i.e. a tensor field of type (0,p), with p a positive or zero integer. The case p=0 corresponds to a scalar field. OUTPUT: - instance of :class:`TensorField` representing a fully covariant tensor field on the *start* manifold that is the pullback of `T` given by ``self``. EXAMPLES: Pullback on `S^2` of a scalar field defined on `R^3`:: sage: m = Manifold(2, 'S^2', start_index=1) sage: c_spher = Chart(m, r'th:[0,pi]:\theta ph:[0,2*pi):\phi', 'spher') # spherical coord. on S^2 sage: n = Manifold(3, 'R^3', r'\RR^3', start_index=1) sage: c_cart = Chart(n, 'x y z', 'cart') # Cartesian coord. on R^3 sage: Phi = DiffMapping(m, n, (sin(th)*cos(ph), sin(th)*sin(ph), cos(th)), name='Phi', latex_name=r'\Phi') sage: f = ScalarField(n, x*y*z, name='f') ; f scalar field 'f' on the 3-dimensional manifold 'R^3' sage: f.show() f: (x, y, z) |--> x*y*z sage: pf = Phi.pullback(f) ; pf scalar field 'Phi_*(f)' on the 2-dimensional manifold 'S^2' sage: pf.show() Phi_*(f): (th, ph) |--> cos(ph)*cos(th)*sin(ph)*sin(th)^2 Pullback on `S^2` of the standard Euclidean metric on `R^3`:: sage: g = SymBilinFormField(n, 'g') sage: g[1,1], g[2,2], g[3,3] = 1, 1, 1 sage: g.show() g = dx*dx + dy*dy + dz*dz sage: pg = Phi.pullback(g) ; pg field of symmetric bilinear forms 'Phi_*(g)' on the 2-dimensional manifold 'S^2' sage: pg.show() Phi_*(g) = dth*dth + sin(th)^2 dph*dph Pullback on `S^2` of a 3-form on `R^3`:: sage: a = DiffForm(n, 3, 'A') sage: a[1,2,3] = f sage: a.show() A = x*y*z dx/\dy/\dz sage: pa = Phi.pullback(a) ; pa 3-form 'Phi_*(A)' on the 2-dimensional manifold 'S^2' sage: pa.show() # should be zero (as any 3-form on a 2-dimensional manifold) Phi_*(A) = 0 """ from scalarfield import ScalarField if not isinstance(tensor, TensorField): raise TypeError("The argument 'tensor' must be a tensor field.") manif1 = self.manifold1 manif2 = self.manifold2 if tensor.manifold != manif2: raise TypeError("The tensor field is not defined on the mapping " + "arrival manifold.") (ncon, ncov) = tensor.tensor_type if ncon != 0: raise TypeError("The pullback cannot be taken on a tensor " + "with some contravariant part.") 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 chart1_name = None chart2_name = None def_chart1 = manif1.def_chart def_chart1_name = def_chart1.name def_chart2 = manif2.def_chart def_chart2_name = def_chart2.name if ncov == 0: # Case of a scalar field # ---------------------- # A pair of chart (chart1_name, chart2_name) where the computation # is feasable is searched, privileging the default chart of the # start manifold for chart1_name if def_chart2_name in tensor.express and \ (def_chart1_name, def_chart2_name) in self.coord_expression: chart1_name = def_chart1_name chart2_name = def_chart2_name else: for (chart1n, chart2n) in self.coord_expression: if chart1n == def_chart1_name and chart2n in tensor.express: chart1_name = def_chart1_name chart2_name = chart2n break if chart1_name is None: # It is not possible to have def_chart1 as chart for # expressing the result; any other chart is then looked for: for (chart1n, chart2n) in self.coord_expression: if chart2n in tensor.express: chart1_name = chart1n chart2_name = chart2n break if chart1_name is None: raise ValueError("No common chart could be find to compute " + "the pullback of the scalar field.") phi = self.coord_expression[(chart1_name, chart2_name)] coord1 = manif1.atlas[chart1_name].xx ff = tensor.express[chart2_name] return ScalarField(manif1, ff(*(phi(*coord1))), chart1_name, resu_name, resu_latex_name) else: # Case of tensor field of rank >= 1 # --------------------------------- # A pair of chart (chart1_name, chart2_name) where the computation # is feasable is searched, privileging the default chart of the # start manifold for chart1_name if def_chart2.frame.name in tensor.components and \ (def_chart1_name, def_chart2_name) in self.coord_expression: chart1_name = def_chart1_name chart2_name = def_chart2_name else: for (chart1n, chart2n) in self.coord_expression: if chart1n == def_chart1_name and \ manif2.atlas[chart2n].frame.name in tensor.components: chart1_name = def_chart1_name chart2_name = chart2n break if chart1_name is None: # It is not possible to have def_chart1 as chart for # expressing the result; any other chart is then looked for: for (chart1n, chart2n) in self.coord_expression: if manif2.atlas[chart2n].frame.name in tensor.components: chart1_name = chart1n chart2_name = chart2n break if chart1_name is None: raise ValueError("No common chart could be find to compute " + "the pullback of the tensor field.") chart1 = manif1.atlas[chart1_name] chart2 = manif2.atlas[chart2_name] frame1_name = chart1.frame.name frame2_name = chart2.frame.name # Computation at the component level: tcomp = tensor.components[frame2_name] if isinstance(tcomp, CompFullySym): ptcomp = CompFullySym(manif1, ncov, frame1_name) elif isinstance(tcomp, CompFullyAntiSym): ptcomp = CompFullyAntiSym(manif1, ncov, frame1_name) elif isinstance(tcomp, CompWithSym): ptcomp = CompWithSym(manif1, ncov, frame1_name, sym=tcomp.sym, antisym=tcomp.antisym) else: ptcomp = Components(manif1, ncov, frame1_name) phi = self.coord_expression[(chart1_name, chart2_name)] jacob = phi.jacobian() # X2 coordinates expressed in terms of X1 ones via the mapping: coord2_1 = phi(*(chart1.xx)) si1 = manif1.sindex si2 = manif2.sindex for ind_new in ptcomp.non_redundant_index_generator(): res = 0 for ind_old in manif2.index_generator(ncov): ff = tcomp[[ind_old]].function_chart(chart2_name) t = FunctionChart(chart1, 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 = tensor_field_from_comp(ptcomp, (0, ncov)) resu.set_name(resu_name, resu_latex_name) return resu