def sqrt_abs_det(self, frame=None): r""" Square root of the absolute value of the determinant of the metric components in the specified frame. INPUT: - ``frame`` -- (default: None) vector frame with respect to which the components `g_{ij}` of ``self`` are defined; if None, the domain's default frame is used. If a chart is provided, the associated coordinate frame is used OUTPUT: - `\sqrt{|\det (g_{ij})|}`, as an instance of :class:`ScalarField` EXAMPLES: Standard metric in the Euclidean space `\RR^3` with spherical coordinates:: sage: m = Manifold(3, 'M', start_index=1) sage: c_spher.<r,th,ph> = m.chart(r'r:[0,+oo) th:[0,pi]:\theta ph:[0,2*pi):\phi') sage: g = Metric(m, 'g') sage: g[1,1], g[2,2], g[3,3] = 1, r^2, (r*sin(th))^2 sage: g.view() g = dr*dr + r^2 dth*dth + r^2*sin(th)^2 dph*dph sage: g.sqrt_abs_det().expr() r^2*sin(th) Metric determinant on a 2-dimensional manifold:: sage: M = Manifold(2, 'M', start_index=1) sage: X.<x,y> = M.chart('x y') sage: g = Metric(M, 'g') sage: g[1,1], g[1, 2], g[2, 2] = 1+x, x*y , 1-y sage: g[:] [ x + 1 x*y] [ x*y -y + 1] sage: s = g.sqrt_abs_det() ; s scalar field on the 2-dimensional manifold 'M' sage: s.expr() sqrt(-x^2*y^2 - (x + 1)*y + x + 1) Determinant in a frame different from the default's one:: sage: Y.<u,v> = M.chart('u v') sage: ch_X_Y = CoordChange(X, Y, x+y, x-y) sage: ch_X_Y.inverse() coordinate change from chart (M, (u, v)) to chart (M, (x, y)) sage: g.comp(Y.frame)[:, Y] [ 1/8*u^2 - 1/8*v^2 + 1/4*v + 1/2 1/4*u] [ 1/4*u -1/8*u^2 + 1/8*v^2 + 1/4*v + 1/2] sage: g.sqrt_abs_det(Y.frame).expr() 1/2*sqrt(-x^2*y^2 - (x + 1)*y + x + 1) sage: g.sqrt_abs_det(Y.frame).expr(Y) 1/8*sqrt(-u^4 - v^4 + 2*(u^2 + 2)*v^2 - 4*u^2 + 16*v + 16) A chart can be passed instead of a frame:: sage: g.sqrt_abs_det(Y) is g.sqrt_abs_det(Y.frame) True The metric determinant depends on the frame:: sage: g.sqrt_abs_det(X.frame) == g.sqrt_abs_det(Y.frame) False """ from sage.functions.other import sqrt from scalarfield import ScalarField from utilities import simplify_chain manif = self.manifold dom = self.domain if frame is None: frame = dom.def_frame if frame in dom.atlas: # frame is actually a chart and is changed to the associated # coordinate frame: frame = frame.frame if frame not in self._sqrt_abs_dets: # a new computation is necessary detg = self.determinant(frame) resu = ScalarField(dom) for chart in detg.express: x = self._indic_signat * detg.express[chart].express # |g| x = simplify_chain(sqrt(x)) resu.add_expr(x, chart=chart) self._sqrt_abs_dets[frame] = resu return self._sqrt_abs_dets[frame]
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 determinant(self, frame=None): r""" Determinant of the metric components in the specified frame. INPUT: - ``frame`` -- (default: None) vector frame with respect to which the components `g_{ij}` of ``self`` are defined; if None, the domain's default frame is used. If a chart is provided, the associated coordinate frame is used OUTPUT: - the determinant `\det (g_{ij})`, as an instance of :class:`ScalarField` EXAMPLES: Metric determinant on a 2-dimensional manifold:: sage: M = Manifold(2, 'M', start_index=1) sage: X.<x,y> = M.chart('x y') sage: g = Metric(M, 'g') sage: g[1,1], g[1, 2], g[2, 2] = 1+x, x*y , 1-y sage: g[:] [ x + 1 x*y] [ x*y -y + 1] sage: s = g.determinant() # determinant in M's default frame sage: s.expr() -x^2*y^2 - (x + 1)*y + x + 1 Determinant in a frame different from the default's one:: sage: Y.<u,v> = M.chart('u v') sage: ch_X_Y = CoordChange(X, Y, x+y, x-y) sage: ch_X_Y.inverse() coordinate change from chart (M, (u, v)) to chart (M, (x, y)) sage: g.comp(Y.frame)[:, Y] [ 1/8*u^2 - 1/8*v^2 + 1/4*v + 1/2 1/4*u] [ 1/4*u -1/8*u^2 + 1/8*v^2 + 1/4*v + 1/2] sage: g.determinant(Y.frame).expr() -1/4*x^2*y^2 - 1/4*(x + 1)*y + 1/4*x + 1/4 sage: g.determinant(Y.frame).expr(Y) -1/64*u^4 - 1/64*v^4 + 1/32*(u^2 + 2)*v^2 - 1/16*u^2 + 1/4*v + 1/4 A chart can be passed instead of a frame:: sage: g.determinant(X) is g.determinant(X.frame) True sage: g.determinant(Y) is g.determinant(Y.frame) True The metric determinant depends on the frame:: sage: g.determinant(X.frame) == g.determinant(Y.frame) False """ from sage.matrix.constructor import matrix from scalarfield import ScalarField from utilities import simple_determinant, simplify_chain manif = self.manifold dom = self.domain if frame is None: frame = dom.def_frame if frame in dom.atlas: # frame is actually a chart and is changed to the associated # coordinate frame: frame = frame.frame if frame not in self._determinants: # a new computation is necessary resu = ScalarField(dom) gg = self.comp(frame) i1 = manif.sindex for chart in gg[[i1, i1]].express: gm = matrix( [[ gg[i, j, chart].express for j in manif.irange()] for i in manif.irange()] ) detgm = simplify_chain(simple_determinant(gm)) resu.add_expr(detgm, chart=chart) self._determinants[frame] = resu return self._determinants[frame]
def sqrt_abs_det(self, frame_name=None): r""" Square root of the absolute value of the determinant of the metric components in the specified frame. INPUT: - ``frame_name`` -- (default: None) name of the vector frame with respect to which the components `g_{ij}` of ``self`` are defined; if None, the manifold's default frame is used. If a chart name is provided, the associated coordinate basis is used OUTPUT: - `\sqrt{|\det (g_{ij})|}`, as an instance of :class:`ScalarField` EXAMPLES: Standard metric in the Euclidean space `\RR^3` with spherical coordinates:: sage: m = Manifold(3, 'M', start_index=1) sage: c_spher = Chart(m, r'r:positive th:positive:\theta ph:\phi', 'spher') sage: assume(th>=0); assume(th<=pi) sage: g = Metric(m, 'g') sage: g[1,1], g[2,2], g[3,3] = 1, r^2, (r*sin(th))^2 sage: g.show() g = dr*dr + r^2 dth*dth + r^2*sin(th)^2 dph*dph sage: g.sqrt_abs_det().expr() r^2*sin(th) Metric determinant on a 2-dimensional manifold:: sage: M = Manifold(2, 'M', start_index=1) sage: X = Chart(M, 'x y', 'xy') sage: g = Metric(M, 'g') sage: g[1,1], g[1, 2], g[2, 2] = 1+x, x*y , 1-y sage: g[:] [ x + 1 x*y] [ x*y -y + 1] sage: s = g.sqrt_abs_det() ; s scalar field on the 2-dimensional manifold 'M' sage: s.expr() sqrt(-x^2*y^2 - (x + 1)*y + x + 1) Determinant in a frame different from the default's one:: sage: Y = Chart(M, 'u v', 'uv') sage: ch_X_Y = CoordChange(X, Y, x+y, x-y) sage: ch_X_Y.inverse() coordinate change from chart 'uv' (u, v) to chart 'xy' (x, y) sage: g.comp('uv_b')[:, 'uv'] [ 1/8*u^2 - 1/8*v^2 + 1/4*v + 1/2 1/4*u] [ 1/4*u -1/8*u^2 + 1/8*v^2 + 1/4*v + 1/2] sage: g.sqrt_abs_det('uv_b').expr() 1/2*sqrt(-x^2*y^2 - (x + 1)*y + x + 1) sage: g.sqrt_abs_det('uv_b').expr('uv') 1/8*sqrt(-u^4 - 4*u^2 - v^4 + 2*(u^2 + 2)*v^2 + 16*v + 16) The name of a chart can be passed instead of the name of a frame:: sage: g.sqrt_abs_det('uv') is g.sqrt_abs_det('uv_b') True The metric determinant depends on the frame:: sage: g.sqrt_abs_det('xy_b') == g.sqrt_abs_det('uv_b') False """ from sage.functions.other import sqrt from scalarfield import ScalarField from utilities import simplify_chain manif = self.manifold if frame_name is None: frame_name = manif.def_frame.name if frame_name in manif.atlas: # frame_name is actually the name of a chart and is changed to the # name of the associated coordinate basis: frame_name = manif.atlas[frame_name].frame.name if frame_name not in self._sqrt_abs_dets: # a new computation is necessary detg = self.determinant(frame_name) resu = ScalarField(manif) for chart_name in detg.express: x = self._indic_signat * detg.express[chart_name].express # |g| x = simplify_chain(sqrt(x)) resu.set_expr(x, chart_name=chart_name, delete_others=False) self._sqrt_abs_dets[frame_name] = resu return self._sqrt_abs_dets[frame_name]
def determinant(self, frame_name=None): r""" Determinant of the metric components in the specified frame. INPUT: - ``frame_name`` -- (default: None) name of the vector frame with respect to which the components `g_{ij}` of ``self`` are defined; if None, the manifold's default frame is used. If a chart name is provided, the associated coordinate basis is used OUTPUT: - the determinant `\det (g_{ij})`, as an instance of :class:`ScalarField` EXAMPLES: Metric determinant on a 2-dimensional manifold:: sage: M = Manifold(2, 'M', start_index=1) sage: X = Chart(M, 'x y', 'xy') sage: g = Metric(M, 'g') sage: g[1,1], g[1, 2], g[2, 2] = 1+x, x*y , 1-y sage: g[:] [ x + 1 x*y] [ x*y -y + 1] sage: s = g.determinant() # determinant in M's default frame sage: s.expr() -x^2*y^2 - (x + 1)*y + x + 1 Determinant in a frame different from the default's one:: sage: Y = Chart(M, 'u v', 'uv') sage: ch_X_Y = CoordChange(X, Y, x+y, x-y) sage: ch_X_Y.inverse() coordinate change from chart 'uv' (u, v) to chart 'xy' (x, y) sage: g.comp('uv_b')[:, 'uv'] [ 1/8*u^2 - 1/8*v^2 + 1/4*v + 1/2 1/4*u] [ 1/4*u -1/8*u^2 + 1/8*v^2 + 1/4*v + 1/2] sage: g.determinant('uv_b').expr() -1/4*x^2*y^2 - 1/4*(x + 1)*y + 1/4*x + 1/4 sage: g.determinant('uv_b').expr('uv') -1/64*u^4 - 1/16*u^2 - 1/64*v^4 + 1/32*(u^2 + 2)*v^2 + 1/4*v + 1/4 The name of a chart can be passed instead of the name of a frame:: sage: g.determinant('xy') is g.determinant('xy_b') True sage: g.determinant('uv') is g.determinant('uv_b') True The metric determinant depends on the frame:: sage: g.determinant('xy_b') == g.determinant('uv_b') False """ from sage.matrix.constructor import matrix from scalarfield import ScalarField from utilities import simple_determinant, simplify_chain manif = self.manifold if frame_name is None: frame_name = manif.def_frame.name if frame_name in manif.atlas: # frame_name is actually the name of a chart and is changed to the # name of the associated coordinate basis: frame_name = manif.atlas[frame_name].frame.name if frame_name not in self._determinants: # a new computation is necessary resu = ScalarField(manif) gg = self.comp(frame_name) i1 = manif.sindex for chart_name in gg[[i1, i1]].express: gm = matrix( [[gg[i, j, chart_name].express for j in manif.irange()] for i in manif.irange()]) detgm = simplify_chain(simple_determinant(gm)) resu.set_expr(detgm, chart_name=chart_name, delete_others=False) self._determinants[frame_name] = resu return self._determinants[frame_name]
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