def __init__(self, metric, name, latex_name=None, init_coef=True): r""" Construct a Levi-Civita connection. TESTS: Levi-Civita connection of the hyperbolic plane:: sage: M = Manifold(2, 'M') sage: X.<r,ph> = M.chart(r'r:(0,+oo) ph:(0,2*pi)') sage: g = M.metric('g') sage: g[0,0], g[1,1] = 1/(1+r^2), r^2 sage: from sage.manifolds.differentiable.levi_civita_connection \ ....: import LeviCivitaConnection sage: nab = LeviCivitaConnection(g, 'nabla', latex_name=r'\nabla') sage: nab Levi-Civita connection nabla associated with the Riemannian metric g on the 2-dimensional differentiable manifold M sage: TestSuite(nab).run() """ AffineConnection.__init__(self, metric.domain(), name, latex_name) self._metric = metric # Initialization of the derived quantities: LeviCivitaConnection._init_derived(self) if init_coef: # Initialization of the Christoffel symbols in the top charts on # the domain (i.e. disregarding the subcharts) for chart in self._domain.top_charts(): self.coef(chart._frame)
def _del_derived(self): r""" Delete the derived quantities. TEST:: sage: M = Manifold(5, 'M') sage: g = M.metric('g') sage: nab = g.connection() sage: nab._del_derived() """ AffineConnection._del_derived(self)
def _del_derived(self): r""" Delete the derived quantities. TESTS:: sage: M = Manifold(5, 'M') sage: g = M.metric('g') sage: nab = g.connection() sage: nab._del_derived() """ AffineConnection._del_derived(self)
def _init_derived(self): r""" Initialize the derived quantities. TESTS:: sage: M = Manifold(5, 'M') sage: g = M.metric('g') sage: nab = g.connection() sage: nab._init_derived() """ AffineConnection._init_derived(self)
def riemann(self, name=None, latex_name=None): r""" Return the Riemann curvature tensor of the connection. This method redefines :meth:`sage.manifolds.differentiable.affine_connection.AffineConnection.riemann` to set some name and the latex_name to the output. The Riemann curvature tensor is the tensor field `R` of type (1,3) defined by .. MATH:: R(\omega, w, u, v) = \left\langle \omega, \nabla_u \nabla_v w - \nabla_v \nabla_u w - \nabla_{[u, v]} w \right\rangle for any 1-form `\omega` and any vector fields `u`, `v` and `w`. INPUT: - ``name`` -- (default: ``None``) name given to the Riemann tensor; if none, it is set to "Riem(g)", where "g" is the metric's name - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the Riemann tensor; if none, it is set to "\\mathrm{Riem}(g)", where "g" is the metric's name OUTPUT: - the Riemann curvature tensor `R`, as an instance of :class:`~sage.manifolds.differentiable.tensorfield.TensorField` EXAMPLES: Riemann tensor of the Levi-Civita connection associated with the metric of the hyperbolic plane (Poincare disk model):: sage: M = Manifold(2, 'M', start_index=1) sage: X.<x,y> = M.chart('x:(-1,1) y:(-1,1)') # Cartesian coord. on the Poincare disk sage: X.add_restrictions(x^2+y^2<1) sage: g = M.metric('g') sage: g[1,1], g[2,2] = 4/(1-x^2-y^2)^2, 4/(1-x^2-y^2)^2 sage: nab = g.connection() sage: riem = nab.riemann(); riem Tensor field Riem(g) of type (1,3) on the 2-dimensional differentiable manifold M sage: riem.display_comp() Riem(g)^x_yxy = -4/(x^4 + y^4 + 2*(x^2 - 1)*y^2 - 2*x^2 + 1) Riem(g)^x_yyx = 4/(x^4 + y^4 + 2*(x^2 - 1)*y^2 - 2*x^2 + 1) Riem(g)^y_xxy = 4/(x^4 + y^4 + 2*(x^2 - 1)*y^2 - 2*x^2 + 1) Riem(g)^y_xyx = -4/(x^4 + y^4 + 2*(x^2 - 1)*y^2 - 2*x^2 + 1) """ if self._riemann is None: AffineConnection.riemann(self) if name is None: self._riemann._name = "Riem(" + self._metric._name + ")" else: self._riemann._name = name if latex_name is None: self._riemann._latex_name = r"\mathrm{Riem}\left(" + \ self._metric._latex_name + r"\right)" else: self._riemann._latex_name = latex_name for rst in self._riemann._restrictions.values(): rst._name = self._riemann._name rst._latex_name = self._riemann._latex_name return self._riemann
def coef(self, frame=None): r""" Return the connection coefficients relative to the given frame. `n` being the manifold's dimension, the connection coefficients relative to the vector frame `(e_i)` are the `n^3` scalar fields `\Gamma^k_{\ \, ij}` defined by .. MATH:: \nabla_{e_j} e_i = \Gamma^k_{\ \, ij} e_k If the connection coefficients are not known already, they are computed * as Christoffel symbols if the frame `(e_i)` is a coordinate frame * from the above formula otherwise INPUT: - ``frame`` -- (default: ``None``) vector frame relative to which the connection coefficients are required; if none is provided, the domain's default frame is assumed OUTPUT: - connection coefficients relative to the frame ``frame``, as an instance of the class :class:`~sage.tensor.modules.comp.Components` with 3 indices ordered as `(k,i,j)`; for Christoffel symbols, an instance of the subclass :class:`~sage.tensor.modules.comp.CompWithSym` is returned. EXAMPLES: Christoffel symbols of the Levi-Civita connection associated to the Euclidean metric on `\RR^3` expressed in spherical coordinates:: sage: M = Manifold(3, 'R^3', 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 = M.metric('g') sage: g[1,1], g[2,2], g[3,3] = 1, r^2 , (r*sin(th))^2 sage: g.display() g = dr*dr + r^2 dth*dth + r^2*sin(th)^2 dph*dph sage: nab = g.connection() sage: gam = nab.coef() ; gam 3-indices components w.r.t. Coordinate frame (R^3, (d/dr,d/dth,d/dph)), with symmetry on the index positions (1, 2) sage: gam[:] [[[0, 0, 0], [0, -r, 0], [0, 0, -r*sin(th)^2]], [[0, 1/r, 0], [1/r, 0, 0], [0, 0, -cos(th)*sin(th)]], [[0, 0, 1/r], [0, 0, cos(th)/sin(th)], [1/r, cos(th)/sin(th), 0]]] sage: # The only non-zero Christoffel symbols: sage: gam[1,2,2], gam[1,3,3] (-r, -r*sin(th)^2) sage: gam[2,1,2], gam[2,3,3] (1/r, -cos(th)*sin(th)) sage: gam[3,1,3], gam[3,2,3] (1/r, cos(th)/sin(th)) Connection coefficients of the same connection with respect to the orthonormal frame associated to spherical coordinates:: sage: ch_basis = M.automorphism_field() sage: ch_basis[1,1], ch_basis[2,2], ch_basis[3,3] = 1, 1/r, 1/(r*sin(th)) sage: e = c_spher.frame().new_frame(ch_basis, 'e') sage: gam_e = nab.coef(e) ; gam_e 3-indices components w.r.t. Vector frame (R^3, (e_1,e_2,e_3)) sage: gam_e[:] [[[0, 0, 0], [0, -1/r, 0], [0, 0, -1/r]], [[0, 1/r, 0], [0, 0, 0], [0, 0, -cos(th)/(r*sin(th))]], [[0, 0, 1/r], [0, 0, cos(th)/(r*sin(th))], [0, 0, 0]]] sage: # The only non-zero connection coefficients: sage: gam_e[1,2,2], gam_e[2,1,2] (-1/r, 1/r) sage: gam_e[1,3,3], gam_e[3,1,3] (-1/r, 1/r) sage: gam_e[2,3,3], gam_e[3,2,3] (-cos(th)/(r*sin(th)), cos(th)/(r*sin(th))) """ from sage.manifolds.differentiable.vectorframe import CoordFrame if frame is None: frame = self._domain._def_frame if frame not in self._coefficients: # the coefficients must be computed # # Check whether frame is a subframe of a frame in which the # coefficients are already known: for oframe in self._coefficients: if frame in oframe._subframes: self._coefficients[frame] = self._new_coef(frame) comp_store = self._coefficients[frame]._comp ocomp_store = self._coefficients[oframe]._comp for ind, value in ocomp_store.items(): comp_store[ind] = value.restrict(frame._domain) break else: # If not, the coefficients must be computed from scratch: manif = self._domain if isinstance(frame, CoordFrame): # Christoffel symbols chart = frame._chart gam = self._new_coef(frame) gg = self._metric.comp(frame) ginv = self._metric.inverse().comp(frame) if Parallelism().get('tensor') != 1: # parallel computation nproc = Parallelism().get('tensor') lol = lambda lst, sz: [ lst[i:i + sz] for i in range(0, len(lst), sz) ] ind_list = [] for ind in gam.non_redundant_index_generator(): i, j, k = ind ind_list.append((i, j, k)) ind_step = max(1, int(len(ind_list) / nproc / 2)) local_list = lol(ind_list, ind_step) # definition of the list of input parameters listParalInput = [] for ind_part in local_list: listParalInput.append( (ind_part, chart, ginv, gg, manif)) # definition of the parallel function @parallel(p_iter='multiprocessing', ncpus=nproc) def make_Connect(local_list_ijk, chart, ginv, gg, manif): partial = [] for i, j, k in local_list_ijk: rsum = 0 for s in manif.irange(): if ginv[i, s, chart] != 0: rsum += ginv[i, s, chart] * ( gg[s, k, chart].diff(j) + gg[j, s, chart].diff(k) - gg[j, k, chart].diff(s)) partial.append([i, j, k, rsum / 2]) return partial # Computation and Assignation of values for ii, val in make_Connect(listParalInput): for jj in val: gam[jj[0], jj[1], jj[2], ii[0][1]] = jj[3] else: # sequential for ind in gam.non_redundant_index_generator(): i, j, k = ind # The computation is performed at the CoordFunction level: rsum = 0 for s in manif.irange(): rsum += ginv[i, s, chart] * ( gg[s, k, chart].diff(j) + gg[j, s, chart].diff(k) - gg[j, k, chart].diff(s)) gam[i, j, k, chart] = rsum / 2 # Assignation of results self._coefficients[frame] = gam else: # Computation from the formula defining the connection coef. return AffineConnection.coef(self, frame) return self._coefficients[frame]
def riemann(self, name=None, latex_name=None): r""" Return the Riemann curvature tensor of the connection. This method redefines :meth:`sage.manifolds.differentiable.affine_connection.AffineConnection.riemann` to set some name and the latex_name to the output. The Riemann curvature tensor is the tensor field `R` of type (1,3) defined by .. MATH:: R(\omega, w, u, v) = \left\langle \omega, \nabla_u \nabla_v w - \nabla_v \nabla_u w - \nabla_{[u, v]} w \right\rangle for any 1-form `\omega` and any vector fields `u`, `v` and `w`. INPUT: - ``name`` -- (default: ``None``) name given to the Riemann tensor; if none, it is set to "Riem(g)", where "g" is the metric's name - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the Riemann tensor; if none, it is set to "\\mathrm{Riem}(g)", where "g" is the metric's name OUTPUT: - the Riemann curvature tensor `R`, as an instance of :class:`~sage.manifolds.differentiable.tensorfield.TensorField` EXAMPLES: Riemann tensor of the Levi-Civita connection associated with the metric of the hyperbolic plane (Poincaré disk model):: sage: M = Manifold(2, 'M', start_index=1) sage: X.<x,y> = M.chart('x:(-1,1) y:(-1,1)') # Cartesian coord. on the Poincaré disk sage: X.add_restrictions(x^2+y^2<1) sage: g = M.metric('g') sage: g[1,1], g[2,2] = 4/(1-x^2-y^2)^2, 4/(1-x^2-y^2)^2 sage: nab = g.connection() sage: riem = nab.riemann(); riem Tensor field Riem(g) of type (1,3) on the 2-dimensional differentiable manifold M sage: riem.display_comp() Riem(g)^x_yxy = -4/(x^4 + y^4 + 2*(x^2 - 1)*y^2 - 2*x^2 + 1) Riem(g)^x_yyx = 4/(x^4 + y^4 + 2*(x^2 - 1)*y^2 - 2*x^2 + 1) Riem(g)^y_xxy = 4/(x^4 + y^4 + 2*(x^2 - 1)*y^2 - 2*x^2 + 1) Riem(g)^y_xyx = -4/(x^4 + y^4 + 2*(x^2 - 1)*y^2 - 2*x^2 + 1) """ if self._riemann is None: AffineConnection.riemann(self) if name is None: self._riemann._name = "Riem(" + self._metric._name + ")" else: self._riemann._name = name if latex_name is None: self._riemann._latex_name = r"\mathrm{Riem}\left(" + \ self._metric._latex_name + r"\right)" else: self._riemann._latex_name = latex_name for rst in self._riemann._restrictions.values(): rst._name = self._riemann._name rst._latex_name = self._riemann._latex_name return self._riemann
def coef(self, frame=None): r""" Return the connection coefficients relative to the given frame. `n` being the manifold's dimension, the connection coefficients relative to the vector frame `(e_i)` are the `n^3` scalar fields `\Gamma^k_{\ \, ij}` defined by .. MATH:: \nabla_{e_j} e_i = \Gamma^k_{\ \, ij} e_k If the connection coefficients are not known already, they are computed * as Christoffel symbols if the frame `(e_i)` is a coordinate frame * from the above formula otherwise INPUT: - ``frame`` -- (default: ``None``) vector frame relative to which the connection coefficients are required; if none is provided, the domain's default frame is assumed OUTPUT: - connection coefficients relative to the frame ``frame``, as an instance of the class :class:`~sage.tensor.modules.comp.Components` with 3 indices ordered as `(k,i,j)`; for Christoffel symbols, an instance of the subclass :class:`~sage.tensor.modules.comp.CompWithSym` is returned. EXAMPLES: Christoffel symbols of the Levi-Civita connection associated to the Euclidean metric on `\RR^3` expressed in spherical coordinates:: sage: M = Manifold(3, 'R^3', 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 = M.metric('g') sage: g[1,1], g[2,2], g[3,3] = 1, r^2 , (r*sin(th))^2 sage: g.display() g = dr*dr + r^2 dth*dth + r^2*sin(th)^2 dph*dph sage: nab = g.connection() sage: gam = nab.coef() ; gam 3-indices components w.r.t. Coordinate frame (R^3, (d/dr,d/dth,d/dph)), with symmetry on the index positions (1, 2) sage: gam[:] [[[0, 0, 0], [0, -r, 0], [0, 0, -r*sin(th)^2]], [[0, 1/r, 0], [1/r, 0, 0], [0, 0, -cos(th)*sin(th)]], [[0, 0, 1/r], [0, 0, cos(th)/sin(th)], [1/r, cos(th)/sin(th), 0]]] sage: # The only non-zero Christoffel symbols: sage: gam[1,2,2], gam[1,3,3] (-r, -r*sin(th)^2) sage: gam[2,1,2], gam[2,3,3] (1/r, -cos(th)*sin(th)) sage: gam[3,1,3], gam[3,2,3] (1/r, cos(th)/sin(th)) Connection coefficients of the same connection with respect to the orthonormal frame associated to spherical coordinates:: sage: ch_basis = M.automorphism_field() sage: ch_basis[1,1], ch_basis[2,2], ch_basis[3,3] = 1, 1/r, 1/(r*sin(th)) sage: e = c_spher.frame().new_frame(ch_basis, 'e') sage: gam_e = nab.coef(e) ; gam_e 3-indices components w.r.t. Vector frame (R^3, (e_1,e_2,e_3)) sage: gam_e[:] [[[0, 0, 0], [0, -1/r, 0], [0, 0, -1/r]], [[0, 1/r, 0], [0, 0, 0], [0, 0, -cos(th)/(r*sin(th))]], [[0, 0, 1/r], [0, 0, cos(th)/(r*sin(th))], [0, 0, 0]]] sage: # The only non-zero connection coefficients: sage: gam_e[1,2,2], gam_e[2,1,2] (-1/r, 1/r) sage: gam_e[1,3,3], gam_e[3,1,3] (-1/r, 1/r) sage: gam_e[2,3,3], gam_e[3,2,3] (-cos(th)/(r*sin(th)), cos(th)/(r*sin(th))) """ from sage.manifolds.differentiable.vectorframe import CoordFrame if frame is None: frame = self._domain._def_frame if frame not in self._coefficients: # the coefficients must be computed # # Check whether frame is a subframe of a frame in which the # coefficients are already known: for oframe in self._coefficients: if frame in oframe._subframes: self._coefficients[frame] = self._new_coef(frame) comp_store = self._coefficients[frame]._comp ocomp_store = self._coefficients[oframe]._comp for ind, value in ocomp_store.items(): comp_store[ind] = value.restrict(frame._domain) break else: # If not, the coefficients must be computed from scratch: manif = self._domain if isinstance(frame, CoordFrame): # Christoffel symbols chart = frame._chart gam = self._new_coef(frame) gg = self._metric.comp(frame) ginv = self._metric.inverse().comp(frame) if Parallelism().get('tensor') != 1: # parallel computation nproc = Parallelism().get('tensor') lol = lambda lst, sz: [lst[i:i+sz] for i in range(0, len(lst), sz)] ind_list = [] for ind in gam.non_redundant_index_generator(): i, j, k = ind ind_list.append((i,j,k)) ind_step = max(1,int(len(ind_list)/nproc/2)) local_list = lol(ind_list,ind_step) # definition of the list of input parameters listParalInput = [] for ind_part in local_list: listParalInput.append((ind_part,chart,ginv,gg,manif)) # definition of the parallel function @parallel(p_iter='multiprocessing',ncpus=nproc) def make_Connect(local_list_ijk,chart,ginv,gg,manif): partial = [] for i,j,k in local_list_ijk: rsum = 0 for s in manif.irange(): if ginv[i,s, chart]!=0: rsum += ginv[i,s, chart] * ( gg[s,k, chart].diff(j) + gg[j,s, chart].diff(k) - gg[j,k, chart].diff(s) ) partial.append([i,j,k,rsum / 2]) return partial # Computation and Assignation of values for ii, val in make_Connect(listParalInput): for jj in val: gam[jj[0],jj[1],jj[2],ii[0][1]] = jj[3] else: # sequential for ind in gam.non_redundant_index_generator(): i, j, k = ind # The computation is performed at the ChartFunction level: rsum = 0 for s in manif.irange(): rsum += ginv[i,s, chart] * ( gg[s,k, chart].diff(j) + gg[j,s, chart].diff(k) - gg[j,k, chart].diff(s) ) gam[i,j,k, chart] = rsum / 2 # Assignation of results self._coefficients[frame] = gam else: # Computation from the formula defining the connection coef. return AffineConnection.coef(self, frame) return self._coefficients[frame]