Path: blob/master/src/sage/geometry/riemannian_manifolds/parametrized_surface3d.py
8817 views
"""1Differential Geometry of Parametrized Surfaces.23AUTHORS:4- Mikhail Malakhaltsev (2010-09-25): initial version5- Joris Vankerschaver (2010-10-25): implementation, doctests67"""8#*****************************************************************************9# Copyright (C) 2010 Mikhail Malakhaltsev <[email protected]>10# Copyright (C) 2010 Joris Vankerschaver <[email protected]>11#12# Distributed under the terms of the GNU General Public License (GPL)13# http://www.gnu.org/licenses/14#*****************************************************************************1516from itertools import product1718from sage.structure.sage_object import SageObject19from sage.modules.free_module_element import vector20from sage.matrix.constructor import matrix21from sage.calculus.functional import diff22from sage.functions.other import sqrt23from sage.misc.cachefunc import cached_method24from sage.symbolic.ring import SR25from sage.symbolic.constants import pi26from sage.symbolic.assumptions import assume2728def _simplify_full_rad(f):29"""30Helper function to conveniently call :meth:`simplify_full` and31:meth:`simplify_radical` in succession.3233INPUT:3435- ``f`` - a symbolic expression.3637EXAMPLES::3839sage: from sage.geometry.riemannian_manifolds.parametrized_surface3d import _simplify_full_rad40sage: _simplify_full_rad(sqrt(x^2)/x)4114243"""44return f.simplify_full().simplify_radical()454647class ParametrizedSurface3D(SageObject):48r"""49Class representing a parametrized two-dimensional surface in50Euclidian three-space. Provides methods for calculating the main51geometrical objects related to such a surface, such as the first52and the second fundamental form, the total (Gaussian) and the mean53curvature, the geodesic curves, parallel transport, etc.545556INPUT:5758- ``surface_equation`` -- a 3-tuple of functions specifying a parametric59representation of the surface.6061- ``variables`` -- a 2-tuple of intrinsic coordinates `(u, v)` on the62surface, with `u` and `v` symbolic variables, or a 2-tuple of triples63$(u, u_{min}, u_{max})$,64$(v, v_{min}, v_{max})$ when the parameter range65for the coordinates is known.6667- ``name`` -- name of the surface (optional).686970.. note::7172Throughout the documentation, we use the Einstein summation73convention: whenever an index appears twice, once as a74subscript, and once as a superscript, summation over that index75is implied. For instance, `g_{ij} g^{jk}` stands for `\sum_j76g_{ij}g^{jk}`.777879EXAMPLES:8081We give several examples of standard surfaces in differential82geometry. First, let's construct an elliptic paraboloid by83explicitly specifying its parametric equation::8485sage: u, v = var('u,v', domain='real')86sage: eparaboloid = ParametrizedSurface3D((u, v, u^2 + v^2), (u, v),'elliptic paraboloid'); eparaboloid87Parametrized surface ('elliptic paraboloid') with equation (u, v, u^2 + v^2)8889When the ranges for the intrinsic coordinates are known, they can be90specified explicitly. This is mainly useful for plotting. Here we91construct half of an ellipsoid::9293sage: u1, u2 = var ('u1, u2', domain='real');94sage: coords = ((u1, -pi/2, pi/2), (u2, 0, pi))95sage: ellipsoid_eq = (cos(u1)*cos(u2), 2*sin(u1)*cos(u2), 3*sin(u2))96sage: ellipsoid = ParametrizedSurface3D(ellipsoid_eq, coords, 'ellipsoid'); ellipsoid97Parametrized surface ('ellipsoid') with equation (cos(u1)*cos(u2), 2*cos(u2)*sin(u1), 3*sin(u2))98sage: ellipsoid.plot()99100Standard surfaces can be constructed using the ``surfaces`` generator::101102sage: klein = surfaces.Klein(); klein103Parametrized surface ('Klein bottle') with equation (-(sin(1/2*u)*sin(2*v) - cos(1/2*u)*sin(v) - 1)*cos(u), -(sin(1/2*u)*sin(2*v) - cos(1/2*u)*sin(v) - 1)*sin(u), cos(1/2*u)*sin(2*v) + sin(1/2*u)*sin(v))104105Latex representation of the surfaces::106107sage: u, v = var('u, v', domain='real')108sage: sphere = ParametrizedSurface3D((cos(u)*cos(v), sin(u)*cos(v), sin(v)), (u, v), 'sphere')109sage: print latex(sphere)110\left(\cos\left(u\right) \cos\left(v\right), \cos\left(v\right) \sin\left(u\right), \sin\left(v\right)\right)111sage: print sphere._latex_()112\left(\cos\left(u\right) \cos\left(v\right), \cos\left(v\right) \sin\left(u\right), \sin\left(v\right)\right)113sage: print sphere114Parametrized surface ('sphere') with equation (cos(u)*cos(v), cos(v)*sin(u), sin(v))115116To plot a parametric surface, use the :meth:`plot` member function::117118sage: enneper = surfaces.Enneper(); enneper119Parametrized surface ('Enneper's surface') with equation (-1/9*(u^2 - 3*v^2 - 3)*u, -1/9*(3*u^2 - v^2 + 3)*v, 1/3*u^2 - 1/3*v^2)120sage: enneper.plot(aspect_ratio='automatic')121122We construct an ellipsoid whose axes are given by symbolic variables `a`,123`b` and `c`, and find the natural frame of tangent vectors,124expressed in intrinsic coordinates. Note that the result is a125dictionary of vector fields::126127sage: a, b, c = var('a, b, c', domain='real')128sage: u1, u2 = var('u1, u2', domain='real')129sage: ellipsoid_eq = (a*cos(u1)*cos(u2), b*sin(u1)*cos(u2), c*sin(u2))130sage: ellipsoid = ParametrizedSurface3D(ellipsoid_eq, (u1, u2), 'Symbolic ellipsoid'); ellipsoid131Parametrized surface ('Symbolic ellipsoid') with equation (a*cos(u1)*cos(u2), b*cos(u2)*sin(u1), c*sin(u2))132133sage: ellipsoid.natural_frame()134{1: (-a*cos(u2)*sin(u1), b*cos(u1)*cos(u2), 0), 2: (-a*cos(u1)*sin(u2), -b*sin(u1)*sin(u2), c*cos(u2))}135136We find the normal vector field to the surface. The normal vector137field is the vector product of the vectors of the natural frame,138and is given by::139140sage: ellipsoid.normal_vector()141(b*c*cos(u1)*cos(u2)^2, a*c*cos(u2)^2*sin(u1), a*b*cos(u2)*sin(u2))142143By default, the normal vector field is not normalized. To obtain144the unit normal vector field of the elliptic paraboloid, we put::145146sage: u, v = var('u,v', domain='real')147sage: eparaboloid = ParametrizedSurface3D([u,v,u^2+v^2],[u,v],'elliptic paraboloid')148sage: eparaboloid.normal_vector(normalized=True)149(-2*u/sqrt(4*u^2 + 4*v^2 + 1), -2*v/sqrt(4*u^2 + 4*v^2 + 1), 1/sqrt(4*u^2 + 4*v^2 + 1))150151Now let us compute the coefficients of the first fundamental form of the torus::152153sage: u, v = var('u, v', domain='real')154sage: a, b = var('a, b', domain='real')155sage: torus = ParametrizedSurface3D(((a + b*cos(u))*cos(v),(a + b*cos(u))*sin(v), b*sin(u)),[u,v],'torus')156sage: torus.first_fundamental_form_coefficients()157{(1, 2): 0, (1, 1): b^2, (2, 1): 0, (2, 2): b^2*cos(u)^2 + 2*a*b*cos(u) + a^2}158159The first fundamental form can be used to compute the length of a160curve on the surface. For example, let us find the length of the161curve $u^1 = t$, $u^2 = t$, $t \in [0,2\pi]$, on the ellipsoid162with axes $a=1$, $b=1.5$ and $c=1$. So we take the curve::163164sage: t = var('t', domain='real')165sage: u1 = t166sage: u2 = t167168Then find the tangent vector::169170sage: du1 = diff(u1,t)171sage: du2 = diff(u2,t)172sage: du = vector([du1, du2]); du173(1, 1)174175Once we specify numerical values for the axes of the ellipsoid, we can176determine the numerical value of the length integral::177178sage: L = sqrt(ellipsoid.first_fundamental_form(du, du).substitute(u1=u1,u2=u2))179sage: print numerical_integral(L.substitute(a=2, b=1.5, c=1),0,1)[0]1802.00127905972181182We find the area of the sphere of radius $R$::183184sage: R = var('R', domain='real');185sage: u, v = var('u,v', domain='real');186sage: assume(R>0)187sage: assume(cos(v)>0)188sage: sphere = ParametrizedSurface3D([R*cos(u)*cos(v),R*sin(u)*cos(v),R*sin(v)],[u,v],'sphere')189sage: integral(integral(sphere.area_form(),u,0,2*pi),v,-pi/2,pi/2)1904*pi*R^2191192We can find an orthonormal frame field $\{e_1, e_2\}$ of a surface193and calculate its structure functions. Let us first determine the194orthonormal frame field for the elliptic paraboloid::195196sage: u, v = var('u,v', domain='real')197sage: eparaboloid = ParametrizedSurface3D([u,v,u^2+v^2],[u,v],'elliptic paraboloid')198sage: eparaboloid.orthonormal_frame()199{1: (1/sqrt(4*u^2 + 1), 0, 2*u/sqrt(4*u^2 + 1)), 2: (-4*u*v/(sqrt(4*u^2 + 4*v^2 + 1)*sqrt(4*u^2 + 1)), sqrt(4*u^2 + 1)/sqrt(4*u^2 + 4*v^2 + 1), 2*v/(sqrt(4*u^2 + 4*v^2 + 1)*sqrt(4*u^2 + 1)))}200201We can express the orthogonal frame field both in exterior202coordinates (i.e. expressed as vector field fields in the ambient203space $\RR^3$, the default) or in intrinsic coordinates204(with respect to the natural frame). Here we use intrinsic205coordinates::206207sage: eparaboloid.orthonormal_frame(coordinates='int')208{1: (1/sqrt(4*u^2 + 1), 0), 2: (-4*u*v/(sqrt(4*u^2 + 4*v^2 + 1)*sqrt(4*u^2 + 1)), sqrt(4*u^2 + 1)/sqrt(4*u^2 + 4*v^2 + 1))}209210Using the orthonormal frame in interior coordinates, we can calculate211the structure functions $c^k_{ij}$ of the surface, defined by212$[e_i,e_j] = c^k_{ij} e_k$, where $[e_i, e_j]$ represents the Lie213bracket of two frame vector fields $e_i, e_j$. For the214elliptic paraboloid, we get::215216sage: EE = eparaboloid.orthonormal_frame(coordinates='int')217sage: E1 = EE[1]; E2 = EE[2]218sage: CC = eparaboloid.frame_structure_functions(E1,E2)219sage: CC[1,2,1].simplify_full()2204*sqrt(4*u^2 + 4*v^2 + 1)*v/((16*u^4 + 4*(4*u^2 + 1)*v^2 + 8*u^2 + 1)*sqrt(4*u^2 + 1))221222We compute the Gaussian and mean curvatures of the sphere::223224sage: sphere = surfaces.Sphere(); sphere225Parametrized surface ('Sphere') with equation (cos(u)*cos(v), cos(v)*sin(u), sin(v))226sage: K = sphere.gauss_curvature(); K # Not tested -- see trac 127372271228sage: H = sphere.mean_curvature(); H # Not tested -- see trac 12737229-1230231We can easily generate a color plot of the Gaussian curvature of a surface.232Here we deal with the ellipsoid::233234sage: u1, u2 = var('u1,u2', domain='real');235sage: u = [u1,u2]236sage: ellipsoid_equation(u1,u2) = [2*cos(u1)*cos(u2),1.5*cos(u1)*sin(u2),sin(u1)]237sage: ellipsoid = ParametrizedSurface3D(ellipsoid_equation(u1,u2), [u1, u2],'ellipsoid')238sage: # set intervals for variables and the number of division points239sage: u1min, u1max = -1.5, 1.5240sage: u2min, u2max = 0, 6.28241sage: u1num, u2num = 10, 20242sage: # make the arguments array243sage: from numpy import linspace244sage: u1_array = linspace(u1min, u1max, u1num)245sage: u2_array = linspace(u2min, u2max, u2num)246sage: u_array = [ (uu1,uu2) for uu1 in u1_array for uu2 in u2_array]247sage: # Find the gaussian curvature248sage: K(u1,u2) = ellipsoid.gauss_curvature()249sage: # Make array of K values250sage: K_array = [K(uu[0],uu[1]) for uu in u_array]251sage: # Find minimum and max of the gauss curvature252sage: K_max = max(K_array)253sage: K_min = min(K_array)254sage: # Make the array of color coefficients255sage: cc_array = [ (ccc - K_min)/(K_max - K_min) for ccc in K_array ]256sage: points_array = [ellipsoid_equation(u_array[counter][0],u_array[counter][1]) for counter in range(0,len(u_array)) ]257sage: curvature_ellipsoid_plot = sum( point([xx for xx in points_array[counter]],color=hue(cc_array[counter]/2)) for counter in range(0,len(u_array)) )258sage: curvature_ellipsoid_plot.show(aspect_ratio=1)259260We can find the principal curvatures and principal directions of the261elliptic paraboloid::262263sage: u, v = var('u, v', domain='real')264sage: eparaboloid = ParametrizedSurface3D([u, v, u^2+v^2], [u, v], 'elliptic paraboloid')265sage: pd = eparaboloid.principal_directions(); pd266[(2*sqrt(4*u^2 + 4*v^2 + 1)/(16*u^4 + 16*v^4 + 8*(4*u^2 + 1)*v^2 + 8*u^2 + 1), [(1, v/u)], 1), (2/sqrt(4*u^2 + 4*v^2 + 1), [(1, -u/v)], 1)]267268We extract the principal curvatures::269270sage: k1 = pd[0][0].simplify_full()271sage: k12722*sqrt(4*u^2 + 4*v^2 + 1)/(16*u^4 + 16*v^4 + 8*(4*u^2 + 1)*v^2 + 8*u^2 + 1)273sage: k2 = pd[1][0].simplify_full()274sage: k22752/sqrt(4*u^2 + 4*v^2 + 1)276277and check them by comparison with the Gaussian and mean curvature278expressed in terms of the principal curvatures::279280sage: K = eparaboloid.gauss_curvature().simplify_full()281sage: K2824/(16*u^4 + 16*v^4 + 8*(4*u^2 + 1)*v^2 + 8*u^2 + 1)283sage: H = eparaboloid.mean_curvature().simplify_full()284sage: H2852*(2*u^2 + 2*v^2 + 1)/(4*u^2 + 4*v^2 + 1)^(3/2)286sage: (K - k1*k2).simplify_full()2870288sage: (2*H - k1 - k2).simplify_full()2890290291We can find the intrinsic (local coordinates) of the principal directions::292293sage: pd[0][1]294[(1, v/u)]295sage: pd[1][1]296[(1, -u/v)]297298The ParametrizedSurface3D class also contains functionality to299compute the coefficients of the second fundamental form, the shape300operator, the rotation on the surface at a given angle, the301connection coefficients. One can also calculate numerically the302geodesics and the parallel translation along a curve.303304Here we compute a number of geodesics on the sphere emanating305from the point ``(1, 0, 0)``, in various directions. The geodesics306intersect again in the antipodal point ``(-1, 0, 0)``, indicating307that these points are conjugate::308309sage: S = surfaces.Sphere()310sage: g1 = [c[-1] for c in S.geodesics_numerical((0,0),(1,0),(0,2*pi,100))]311sage: g2 = [c[-1] for c in S.geodesics_numerical((0,0),(cos(pi/3),sin(pi/3)),(0,2*pi,100))]312sage: g3 = [c[-1] for c in S.geodesics_numerical((0,0),(cos(2*pi/3),sin(2*pi/3)),(0,2*pi,100))]313sage: (S.plot(opacity=0.3) + line3d(g1,color='red') + line3d(g2,color='red') + line3d(g3,color='red')).show()314315"""316317318def __init__(self, equation, variables, name=None):319r"""320See ``ParametrizedSurface3D`` for full documentation.321322.. note::323324The orientation of the surface is determined by the325parametrization, that is, the natural frame with positive326orientation is given by `\partial_1 \vec r`, `\partial_2 \vec327r`.328329330EXAMPLES::331332sage: u, v = var('u,v', domain='real')333sage: eq = (3*u + 3*u*v^2 - u^3, 3*v + 3*u^2*v - v^3, 3*(u^2-v^2))334sage: enneper = ParametrizedSurface3D(eq, (u, v),'Enneper Surface'); enneper335Parametrized surface ('Enneper Surface') with equation (-u^3 + 3*u*v^2 + 3*u, 3*u^2*v - v^3 + 3*v, 3*u^2 - 3*v^2)336337"""338self.equation = tuple(equation)339340if len(variables[0]) > 0:341self.variables_range = (variables[0][1:3], variables[1][1:3])342self.variables_list = (variables[0][0], variables[1][0])343else:344self.variables_range = None345self.variables_list = variables346347self.variables = {1:self.variables_list[0],2:self.variables_list[1]}348self.name = name349350351def _latex_(self):352r"""353Returns the LaTeX representation of this parametrized surface.354355EXAMPLES::356357sage: u, v = var('u, v')358sage: sphere = ParametrizedSurface3D((cos(u)*cos(v), sin(u)*cos(v), sin(v)), (u, v),'sphere')359sage: latex(sphere)360\left(\cos\left(u\right) \cos\left(v\right), \cos\left(v\right) \sin\left(u\right), \sin\left(v\right)\right)361sage: sphere._latex_()362\left(\cos\left(u\right) \cos\left(v\right), \cos\left(v\right) \sin\left(u\right), \sin\left(v\right)\right)363364"""365from sage.misc.latex import latex366return latex(self.equation)367368369def _repr_(self):370r"""371Returns the string representation of this parametrized surface.372373EXAMPLES::374375sage: u, v = var('u, v', domain='real')376sage: eq = (3*u + 3*u*v^2 - u^3, 3*v + 3*u^2*v - v^3, 3*(u^2-v^2))377sage: enneper = ParametrizedSurface3D(eq,[u,v],'enneper_surface')378sage: print enneper379Parametrized surface ('enneper_surface') with equation (-u^3 + 3*u*v^2 + 3*u, 3*u^2*v - v^3 + 3*v, 3*u^2 - 3*v^2)380sage: enneper._repr_()381"Parametrized surface ('enneper_surface') with equation (-u^3 + 3*u*v^2 + 3*u, 3*u^2*v - v^3 + 3*v, 3*u^2 - 3*v^2)"382383"""384name = 'Parametrized surface'385if self.name is not None:386name += " ('%s')" % self.name387s ='%(designation)s with equation %(eq)s' % \388{'designation': name, 'eq': str(self.equation)}389return s390391392def point(self, coords):393r"""394Returns a point on the surface given its intrinsic coordinates.395396INPUT:397398- ``coords`` - 2-tuple specifying the intrinsic coordinates ``(u, v)`` of the point.399400OUTPUT:401402- 3-vector specifying the coordinates in `\RR^3` of the point.403404EXAMPLES::405406sage: u, v = var('u, v', domain='real')407sage: torus = ParametrizedSurface3D(((2 + cos(u))*cos(v),(2 + cos(u))*sin(v), sin(u)),[u,v],'torus')408sage: torus.point((0, pi/2))409(0, 3, 0)410sage: torus.point((pi/2, pi))411(-2, 0, 1)412sage: torus.point((pi, pi/2))413(0, 1, 0)414415"""416417d = dict(zip(self.variables_list, coords))418return vector([f.subs(d) for f in self.equation])419420421def tangent_vector(self, coords, components):422r"""423Returns the components of a tangent vector given the intrinsic424coordinates of the base point and the components of the vector425in the intrinsic frame.426427INPUT:428429- ``coords`` - 2-tuple specifying the intrinsic coordinates ``(u, v)`` of the point.430431- ``components`` - 2-tuple specifying the components of the tangent vector in the intrinsic coordinate frame.432433OUTPUT:434435- 3-vector specifying the components in `\RR^3` of the vector.436437EXAMPLES:438439We compute two tangent vectors to Enneper's surface along the440coordinate lines and check that their cross product gives the441normal vector::442443sage: u, v = var('u,v', domain='real')444sage: eq = (3*u + 3*u*v^2 - u^3, 3*v + 3*u^2*v - v^3, 3*(u^2-v^2))445sage: e = ParametrizedSurface3D(eq, (u, v),'Enneper Surface')446447sage: w1 = e.tangent_vector((1, 2), (1, 0)); w1448(12, 12, 6)449sage: w2 = e.tangent_vector((1, 2), (0, 1)); w2450(12, -6, -12)451sage: w1.cross_product(w2)452(-108, 216, -216)453454sage: n = e.normal_vector().subs({u: 1, v: 2}); n455(-108, 216, -216)456sage: n == w1.cross_product(w2)457True458459"""460461components = vector(components)462d = dict(zip(self.variables_list, coords))463jacobian = matrix([[f.diff(u).subs(d) for u in self.variables_list]464for f in self.equation])465return jacobian * components466467468def plot(self, urange=None, vrange=None, **kwds):469r"""470Enable easy plotting directly from the surface class.471472The optional keywords ``urange`` and ``vrange`` specify the range for473the surface parameters `u` and `v`. If either of these parameters474is ``None``, the method checks whether a parameter range was475specified when the surface was created. If not, the default of476$(0, 2 \pi)$ is used.477478INPUT:479480- ``urange`` - 2-tuple specifying the parameter range for `u`.481- ``vrange`` - 2-tuple specifying the parameter range for `v`.482483EXAMPLES::484485sage: u, v = var('u, v', domain='real')486sage: eq = (3*u + 3*u*v^2 - u^3, 3*v + 3*u^2*v - v^3, 3*(u^2-v^2))487sage: enneper = ParametrizedSurface3D(eq, (u, v), 'Enneper Surface')488sage: enneper.plot((-5, 5), (-5, 5))489490"""491492from sage.plot.plot3d.parametric_plot3d import parametric_plot3d493494if self.variables_range is None:495if urange is None:496urange = (0, 2*pi)497if vrange is None:498vrange = (0, 2*pi)499else:500if urange is None:501urange = self.variables_range[0]502if vrange is None:503vrange = self.variables_range[1]504505urange3 = (self.variables[1],) + tuple(urange)506vrange3 = (self.variables[2],) + tuple(vrange)507P = parametric_plot3d(self.equation, urange3, vrange3, **kwds)508509return P510511512@cached_method513def natural_frame(self):514"""515Returns the natural tangent frame on the parametrized surface.516The vectors of this frame are tangent to the coordinate lines517on the surface.518519OUTPUT:520521- The natural frame as a dictionary.522523EXAMPLES::524525sage: u, v = var('u, v', domain='real')526sage: eparaboloid = ParametrizedSurface3D((u, v, u^2+v^2), (u, v), 'elliptic paraboloid')527sage: eparaboloid.natural_frame()528{1: (1, 0, 2*u), 2: (0, 1, 2*v)}529"""530531dr1 = \532vector([_simplify_full_rad( diff(f,self.variables[1]) )533for f in self.equation])534dr2 = \535vector([_simplify_full_rad( diff(f,self.variables[2]) )536for f in self.equation])537538return {1:dr1, 2:dr2}539540541@cached_method542def normal_vector(self, normalized=False):543"""544Returns the normal vector field of the parametrized surface.545546INPUT:547548- ``normalized`` - default ``False`` - specifies whether the normal vector should be normalized.549550OUTPUT:551552- Normal vector field.553554EXAMPLES::555556sage: u, v = var('u, v', domain='real')557sage: eparaboloid = ParametrizedSurface3D((u, v, u^2 + v^2), (u, v), 'elliptic paraboloid')558sage: eparaboloid.normal_vector(normalized=False)559(-2*u, -2*v, 1)560sage: eparaboloid.normal_vector(normalized=True)561(-2*u/sqrt(4*u^2 + 4*v^2 + 1), -2*v/sqrt(4*u^2 + 4*v^2 + 1), 1/sqrt(4*u^2 + 4*v^2 + 1))562563"""564565dr = self.natural_frame()566normal = dr[1].cross_product(dr[2])567568if normalized:569normal /= normal.norm()570return _simplify_full_rad(normal)571572573@cached_method574def _compute_first_fundamental_form_coefficient(self, index):575"""576Helper function to compute coefficients of the first fundamental form.577578Do not call this method directly; instead use579``first_fundamental_form_coefficient``.580This method is cached, and expects its argument to be a list.581582EXAMPLES::583584sage: u, v = var('u, v', domain='real')585sage: eparaboloid = ParametrizedSurface3D((u, v, u^2+v^2), (u, v))586sage: eparaboloid._compute_first_fundamental_form_coefficient((1,2))5874*u*v588589"""590dr = self.natural_frame()591return _simplify_full_rad(dr[index[0]]*dr[index[1]])592593594def first_fundamental_form_coefficient(self, index):595r"""596Compute a single component $g_{ij}$ of the first fundamental form. If597the parametric representation of the surface is given by the vector598function $\vec r(u^i)$, where $u^i$, $i = 1, 2$ are curvilinear599coordinates, then $g_{ij} = \frac{\partial \vec r}{\partial u^i} \cdot \frac{\partial \vec r}{\partial u^j}$.600601INPUT:602603- ``index`` - tuple ``(i, j)`` specifying the index of the component $g_{ij}$.604605OUTPUT:606607- Component $g_{ij}$ of the first fundamental form608609EXAMPLES::610611sage: u, v = var('u, v', domain='real')612sage: eparaboloid = ParametrizedSurface3D((u, v, u^2+v^2), (u, v))613sage: eparaboloid.first_fundamental_form_coefficient((1,2))6144*u*v615616When the index is invalid, an error is raised::617618sage: u, v = var('u, v', domain='real')619sage: eparaboloid = ParametrizedSurface3D((u, v, u^2+v^2), (u, v))620sage: eparaboloid.first_fundamental_form_coefficient((1,5))621Traceback (most recent call last):622...623ValueError: Index (1, 5) out of bounds.624625"""626index = tuple(sorted(index))627if len(index) == 2 and all(i == 1 or i == 2 for i in index):628return self._compute_first_fundamental_form_coefficient(index)629else:630raise ValueError("Index %s out of bounds." % str(index))631632def first_fundamental_form_coefficients(self):633r"""634Returns the coefficients of the first fundamental form as a dictionary.635The keys are tuples $(i, j)$, where $i$ and $j$ range over $1, 2$,636while the values are the corresponding coefficients $g_{ij}$.637638OUTPUT:639640- Dictionary of first fundamental form coefficients.641642EXAMPLES::643644sage: u, v = var('u,v', domain='real')645sage: sphere = ParametrizedSurface3D((cos(u)*cos(v), sin(u)*cos(v), sin(v)), (u, v), 'sphere')646sage: sphere.first_fundamental_form_coefficients()647{(1, 2): 0, (1, 1): cos(v)^2, (2, 1): 0, (2, 2): 1}648649"""650coefficients = {}651for index in product((1, 2), repeat=2):652sorted_index = list(sorted(index))653coefficients[index] = \654self._compute_first_fundamental_form_coefficient(index)655return coefficients656657658def first_fundamental_form(self, vector1, vector2):659r"""660Evaluate the first fundamental form on two vectors expressed with661respect to the natural coordinate frame on the surface. In other words,662if the vectors are $v = (v^1, v^2)$ and $w = (w^1, w^2)$, calculate663$g_{11} v^1 w^1 + g_{12}(v^1 w^2 + v^2 w^1) + g_{22} v^2 w^2$, with664$g_{ij}$ the coefficients of the first fundamental form.665666INPUT:667668- ``vector1``, ``vector2`` - vectors on the surface.669670OUTPUT:671672- First fundamental form evaluated on the input vectors.673674EXAMPLES::675676sage: u, v = var('u, v', domain='real')677sage: v1, v2, w1, w2 = var('v1, v2, w1, w2', domain='real')678sage: sphere = ParametrizedSurface3D((cos(u)*cos(v), sin(u)*cos(v), sin(v)), (u, v),'sphere')679sage: sphere.first_fundamental_form(vector([v1,v2]),vector([w1,w2]))680v1*w1*cos(v)^2 + v2*w2681682sage: vv = vector([1,2])683sage: sphere.first_fundamental_form(vv,vv)684cos(v)^2 + 4685686sage: sphere.first_fundamental_form([1,1],[2,1])6872*cos(v)^2 + 1688"""689gamma = self.first_fundamental_form_coefficients()690return sum(gamma[(i,j)] * vector1[i - 1] * vector2[j - 1]691for i, j in product((1, 2), repeat=2))692693694def area_form_squared(self):695"""696Returns the square of the coefficient of the area form on the surface.697In terms of the coefficients $g_{ij}$ (where $i, j = 1, 2$) of the698first fundamental form, this invariant is given by699$A^2 = g_{11}g_{22} - g_{12}^2$.700701See also :meth:`.area_form`.702703OUTPUT:704705- Square of the area form706707EXAMPLES::708709sage: u, v = var('u, v', domain='real')710sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')711sage: sphere.area_form_squared()712cos(v)^2713714"""715gamma = self.first_fundamental_form_coefficients()716sq = gamma[(1,1)] * gamma[(2,2)] - gamma[(1,2)]**2717return _simplify_full_rad(sq)718719720def area_form(self):721r"""722Returns the coefficient of the area form on the surface. In terms of723the coefficients $g_{ij}$ (where $i, j = 1, 2$) of the first724fundamental form, the coefficient of the area form is given by725$A = \sqrt{g_{11}g_{22} - g_{12}^2}$.726727See also :meth:`.area_form_squared`.728729OUTPUT:730731- Coefficient of the area form732733EXAMPLES::734735sage: u, v = var('u,v', domain='real')736sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')737sage: sphere.area_form()738cos(v)739740"""741f = abs(sqrt(self.area_form_squared()))742return _simplify_full_rad(f)743744745def first_fundamental_form_inverse_coefficients(self):746r"""747Returns the coefficients $g^{ij}$ of the inverse of the fundamental748form, as a dictionary. The inverse coefficients are defined by749$g^{ij} g_{jk} = \delta^i_k$ with $\delta^i_k$ the Kronecker750delta.751752OUTPUT:753754- Dictionary of the inverse coefficients.755756EXAMPLES::757758sage: u, v = var('u, v', domain='real')759sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')760sage: sphere.first_fundamental_form_inverse_coefficients()761{(1, 2): 0, (1, 1): cos(v)^(-2), (2, 1): 0, (2, 2): 1}762763"""764765g = self.first_fundamental_form_coefficients()766D = g[(1,1)] * g[(2,2)] - g[(1,2)]**2767768gi11 = _simplify_full_rad(g[(2,2)]/D)769gi12 = _simplify_full_rad(-g[(1,2)]/D)770gi21 = gi12771gi22 = _simplify_full_rad(g[(1,1)]/D)772773return {(1,1): gi11, (1,2): gi12, (2,1): gi21, (2,2): gi22}774775776def first_fundamental_form_inverse_coefficient(self, index):777r"""778Returns a specific component $g^{ij}$ of the inverse of the fundamental779form.780781INPUT:782783- ``index`` - tuple ``(i, j)`` specifying the index of the component $g^{ij}$.784785OUTPUT:786787- Component of the inverse of the fundamental form.788789EXAMPLES::790791sage: u, v = var('u, v', domain='real')792sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')793sage: sphere.first_fundamental_form_inverse_coefficient((1, 2))7940795sage: sphere.first_fundamental_form_inverse_coefficient((1, 1))796cos(v)^(-2)797798"""799800index = tuple(sorted(index))801if len(index) == 2 and all(i == 1 or i == 2 for i in index):802return self.first_fundamental_form_inverse_coefficients()[index]803else:804raise ValueError("Index %s out of bounds." % str(index))805806807808@cached_method809def rotation(self,theta):810r"""811Gives the matrix of the rotation operator over a given angle $\theta$812with respect to the natural frame.813814INPUT:815816- ``theta`` - rotation angle817818OUTPUT:819820- Rotation matrix with respect to the natural frame.821822ALGORITHM:823824The operator of rotation over $\pi/2$ is $J^i_j = g^{ik}\omega_{jk}$,825where $\omega$ is the area form. The operator of rotation over an826angle $\theta$ is $\cos(\theta) I + sin(\theta) J$.827828EXAMPLES::829830sage: u, v = var('u, v', domain='real')831sage: assume(cos(v)>0)832sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')833834We first compute the matrix of rotation over $\pi/3$::835836sage: rotation = sphere.rotation(pi/3); rotation837[ 1/2 -1/2*sqrt(3)/cos(v)]838[ 1/2*sqrt(3)*cos(v) 1/2]839840We verify that three succesive rotations over $\pi/3$ yield minus the identity::841842sage: rotation^3843[-1 0]844[ 0 -1]845846"""847848from sage.functions.trig import sin, cos849850gi = self.first_fundamental_form_inverse_coefficients()851w12 = self.area_form()852R11 = (cos(theta) + sin(theta)*gi[1,2]*w12).simplify_full()853R12 = (- sin(theta)*gi[1,1]*w12).simplify_full()854R21 = (sin(theta)*gi[2,2]*w12).simplify_full()855R22 = (cos(theta) - sin(theta)*gi[2,1]*w12).simplify_full()856return matrix([[R11,R12],[R21,R22]])857858859@cached_method860def orthonormal_frame(self, coordinates='ext'):861r"""862Returns the orthonormal frame field on the surface, expressed either863in exterior coordinates (i.e. expressed as vector fields in the864ambient space $\mathbb{R}^3$, the default) or interior coordinates865(with respect to the natural frame)866867INPUT:868869- ``coordinates`` - either ``ext`` (default) or ``int``.870871OUTPUT:872873- Orthogonal frame field as a dictionary.874875ALGORITHM:876877We normalize the first vector $\vec e_1$ of the natural frame and then878get the second frame vector as $\vec e_2 = [\vec n, \vec e_1]$, where879$\vec n$ is the unit normal to the surface.880881EXAMPLES::882883sage: u, v = var('u,v', domain='real')884sage: assume(cos(v)>0)885sage: sphere = ParametrizedSurface3D([cos(u)*cos(v), sin(u)*cos(v), sin(v)], [u, v],'sphere')886sage: frame = sphere.orthonormal_frame(); frame887{1: (-sin(u), cos(u), 0), 2: (-cos(u)*sin(v), -sin(u)*sin(v), cos(v))}888sage: (frame[1]*frame[1]).simplify_full()8891890sage: (frame[1]*frame[2]).simplify_full()8910892sage: frame[1] == sphere.orthonormal_frame_vector(1)893True894895We compute the orthonormal frame with respect to the natural frame on896the surface::897898sage: frame_int = sphere.orthonormal_frame(coordinates='int'); frame_int899{1: (1/cos(v), 0), 2: (0, 1)}900sage: sphere.first_fundamental_form(frame_int[1], frame_int[1])9011902sage: sphere.first_fundamental_form(frame_int[1], frame_int[2])9030904sage: sphere.first_fundamental_form(frame_int[2], frame_int[2])9051906907"""908909910from sage.symbolic.constants import pi911912if coordinates not in ['ext', 'int']:913raise ValueError("Coordinate system must be exterior ('ext') "914"or interior ('int').")915916c = self.first_fundamental_form_coefficient([1,1])917if coordinates == 'ext':918f1 = self.natural_frame()[1]919920E1 = _simplify_full_rad(f1/sqrt(c))921E2 = _simplify_full_rad(922self.normal_vector(normalized=True).cross_product(E1))923else:924E1 = vector([_simplify_full_rad(1/sqrt(c)), 0])925E2 = (self.rotation(pi/2)*E1).simplify_full()926return {1:E1, 2:E2}927928929def orthonormal_frame_vector(self, index, coordinates='ext'):930r"""931Returns a specific basis vector field of the orthonormal frame field on932the surface, expressed in exterior or interior coordinates. See933:meth:`orthogonal_frame` for more details.934935INPUT:936937- ``index`` - index of the basis vector;938- ``coordinates`` - either ``ext`` (default) or ``int``.939940OUTPUT:941942- Orthonormal frame vector field.943944EXAMPLES::945946sage: u, v = var('u, v', domain='real')947sage: assume(cos(v)>0)948sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')949sage: V1 = sphere.orthonormal_frame_vector(1); V1950(-sin(u), cos(u), 0)951sage: V2 = sphere.orthonormal_frame_vector(2); V2952(-cos(u)*sin(v), -sin(u)*sin(v), cos(v))953sage: (V1*V1).simplify_full()9541955sage: (V1*V2).simplify_full()9560957958sage: n = sphere.normal_vector(normalized=True)959sage: (V1.cross_product(V2) - n).simplify_full()960(0, 0, 0)961"""962963return self.orthonormal_frame(coordinates)[index]964965966def lie_bracket(self, v, w):967r"""968Returns the Lie bracket of two vector fields that are tangent969to the surface. The vector fields should be given in intrinsic970coordinates, i.e. with respect to the natural frame.971972INPUT:973974- ``v`` and ``w`` - vector fields on the surface, expressed975as pairs of functions or as vectors of length 2.976977OUTPUT:978979- The Lie bracket $[v, w]$.980981982EXAMPLES::983984sage: u, v = var('u, v', domain='real')985sage: assume(cos(v)>0)986sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')987sage: sphere.lie_bracket([u,v],[-v,u])988(0, 0)989990sage: EE_int = sphere.orthonormal_frame(coordinates='int')991sage: sphere.lie_bracket(EE_int[1],EE_int[2])992(sin(v)/cos(v)^2, 0)993"""994v = vector(SR, v)995w = vector(SR, w)996997variables = self.variables_list998Dv = matrix([[_simplify_full_rad(diff(component, u))999for u in variables] for component in v])1000Dw = matrix([[_simplify_full_rad(diff(component, u))1001for u in variables] for component in w])1002return vector(Dv*w - Dw*v).simplify_full()100310041005def frame_structure_functions(self, e1, e2):1006r"""1007Returns the structure functions $c^k_{ij}$ for a frame field1008$e_1, e_2$, i.e. a pair of vector fields on the surface which are1009linearly independent at each point. The structure functions are1010defined using the Lie bracket by $[e_i,e_j] = c^k_{ij}e_k$.10111012INPUT:10131014- ``e1``, ``e2`` - vector fields in intrinsic coordinates on1015the surface, expressed as pairs of functions, or as vectors of1016length 2.10171018OUTPUT:10191020- Dictionary of structure functions, where the key ``(i, j, k)`` refers to1021the structure function $c_{i,j}^k$.102210231024EXAMPLES::10251026sage: u, v = var('u, v', domain='real')1027sage: assume(cos(v) > 0)1028sage: sphere = ParametrizedSurface3D([cos(u)*cos(v), sin(u)*cos(v), sin(v)], [u, v], 'sphere')1029sage: sphere.frame_structure_functions([u, v], [-v, u])1030{(1, 2, 1): 0, (2, 1, 2): 0, (2, 2, 2): 0, (1, 2, 2): 0, (1, 1, 1): 0, (2, 1, 1): 0, (2, 2, 1): 0, (1, 1, 2): 0}10311032We construct the structure functions of the orthonormal frame on the1033surface::10341035sage: EE_int = sphere.orthonormal_frame(coordinates='int')1036sage: CC = sphere.frame_structure_functions(EE_int[1],EE_int[2]); CC1037{(1, 2, 1): sin(v)/cos(v), (2, 1, 2): 0, (2, 2, 2): 0, (1, 2, 2): 0, (1, 1, 1): 0, (2, 1, 1): -sin(v)/cos(v), (2, 2, 1): 0, (1, 1, 2): 0}1038sage: sphere.lie_bracket(EE_int[1],EE_int[2]) - CC[(1,2,1)]*EE_int[1] - CC[(1,2,2)]*EE_int[2]1039(0, 0)1040"""1041e1 = vector(SR, e1)1042e2 = vector(SR, e2)10431044lie_bracket = self.lie_bracket(e1, e2).simplify_full()1045transformation = matrix(SR, [e1, e2]).transpose()10461047w = (transformation.inverse()*lie_bracket).simplify_full()10481049return {(1,1,1): 0, (1,1,2): 0, (1,2,1): w[0], (1,2,2): w[1],1050(2,1,1): -w[0], (2,1,2): -w[1], (2,2,1): 0, (2,2,2): 0}105110521053@cached_method1054def _compute_second_order_frame_element(self, index):1055"""1056Compute an element of the second order frame of the surface. See1057:meth:`second_order_natural_frame` for more details.10581059This method expects its arguments in tuple form for caching.1060As it does no input checking, it should not be called directly.10611062EXAMPLES::10631064sage: u, v = var('u, v', domain='real')1065sage: paraboloid = ParametrizedSurface3D([u, v, u^2 + v^2], [u,v], 'paraboloid')1066sage: paraboloid._compute_second_order_frame_element((1, 2))1067(0, 0, 0)1068sage: paraboloid._compute_second_order_frame_element((2, 2))1069(0, 0, 2)10701071"""1072variables = [self.variables[i] for i in index]1073ddr_element = vector([_simplify_full_rad(diff(f, variables))1074for f in self.equation])10751076return ddr_element107710781079def second_order_natural_frame(self):1080r"""1081Returns the second-order frame of the surface, i.e. computes the1082second-order derivatives (with respect to the parameters on the1083surface) of the parametric expression $\vec r = \vec r(u^1,u^2)$1084of the surface.10851086OUTPUT:10871088- Dictionary where the keys are 2-tuples ``(i, j)`` and the values are the corresponding derivatives $r_{ij}$.10891090EXAMPLES:10911092We compute the second-order natural frame of the sphere::10931094sage: u, v = var('u, v', domain='real')1095sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')1096sage: sphere.second_order_natural_frame()1097{(1, 2): (sin(u)*sin(v), -cos(u)*sin(v), 0), (1, 1): (-cos(u)*cos(v), -cos(v)*sin(u), 0), (2, 1): (sin(u)*sin(v), -cos(u)*sin(v), 0), (2, 2): (-cos(u)*cos(v), -cos(v)*sin(u), -sin(v))}10981099"""11001101vectors = {}1102for index in product((1, 2), repeat=2):1103sorted_index = tuple(sorted(index))1104vectors[index] = \1105self._compute_second_order_frame_element(sorted_index)1106return vectors110711081109def second_order_natural_frame_element(self, index):1110r"""1111Returns a vector in the second-order frame of the surface, i.e.1112computes the second-order derivatives of the parametric expression1113$\vec{r}$ of the surface with respect to the parameters listed in the1114argument.11151116INPUT:11171118- ``index`` - a 2-tuple ``(i, j)`` specifying the element of the second-order frame.11191120OUTPUT:11211122- The second-order derivative $r_{ij}$.11231124EXAMPLES::11251126sage: u, v = var('u, v', domain='real')1127sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')1128sage: sphere.second_order_natural_frame_element((1, 2))1129(sin(u)*sin(v), -cos(u)*sin(v), 0)11301131"""11321133index = tuple(sorted(index))1134if len(index) == 2 and all(i == 1 or i == 2 for i in index):1135return self._compute_second_order_frame_element(index)1136else:1137raise ValueError("Index %s out of bounds." % str(index))11381139@cached_method1140def _compute_second_fundamental_form_coefficient(self, index):1141"""1142Compute a coefficient of the second fundamental form of the surface.1143See ``second_fundamental_form_coefficient`` for more details.11441145This method expects its arguments in tuple form for caching. As it1146does no input checking, it should not be called directly.11471148EXAMPLES::11491150sage: u, v = var('u,v', domain='real')1151sage: paraboloid = ParametrizedSurface3D([u, v, u^2+v^2], [u, v], 'paraboloid')1152sage: paraboloid._compute_second_fundamental_form_coefficient((1,1))11532/sqrt(4*u^2 + 4*v^2 + 1)11541155"""1156N = self.normal_vector(normalized=True)1157v = self.second_order_natural_frame_element(index)1158return _simplify_full_rad(v*N)115911601161def second_fundamental_form_coefficient(self, index):1162r"""1163Returns the coefficient $h_{ij}$ of the second fundamental form1164corresponding to the index $(i, j)$. If the equation of the surface1165is $\vec{r}(u^1, u^2)$, then $h_{ij} = \vec{r}_{u^i u^j} \cdot \vec{n}$,1166where $\vec{n}$ is the unit normal.11671168INPUT:11691170- ``index`` - a 2-tuple ``(i, j)``11711172OUTPUT:11731174- Component $h_{ij}$ of the second fundamental form.11751176EXAMPLES::11771178sage: u, v = var('u,v', domain='real')1179sage: assume(cos(v)>0)1180sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')1181sage: sphere.second_fundamental_form_coefficient((1, 1))1182-cos(v)^21183sage: sphere.second_fundamental_form_coefficient((2, 1))1184011851186"""1187index = tuple(index)1188if len(index) == 2 and all(i == 1 or i == 2 for i in index):1189return self._compute_second_fundamental_form_coefficient(index)1190else:1191raise ValueError("Index %s out of bounds." % str(index))119211931194def second_fundamental_form_coefficients(self):1195"""1196Returns the coefficients $h_{ij}$ of the second fundamental form as1197a dictionary, where the keys are the indices $(i, j)$ and the values1198are the corresponding components $h_{ij}$.11991200When only one component is needed, consider instead the function1201:meth:`second_fundamental_form_coefficient`.12021203OUTPUT:12041205Dictionary of second fundamental form coefficients.12061207EXAMPLES::12081209sage: u, v = var('u, v', domain='real')1210sage: assume(cos(v)>0)1211sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')1212sage: sphere.second_fundamental_form_coefficients()1213{(1, 2): 0, (1, 1): -cos(v)^2, (2, 1): 0, (2, 2): -1}12141215"""12161217coefficients = {}1218for index in product((1, 2), repeat=2):1219coefficients[index] = \1220self._compute_second_fundamental_form_coefficient(index)1221return coefficients122212231224def second_fundamental_form(self,vector1,vector2):1225r"""1226Evaluates the second fundamental form on two vectors on the surface.1227If the vectors are given by $v=(v^1,v^2)$ and $w=(w^1,w^2)$, the1228result of this function is $h_{11} v^1 w^1 + h_{12}(v^1 w^2 + v^2 w^1) + h_{22} v^2 w^2$.12291230INPUT:12311232- ``vector1``, ``vector2`` - 2-tuples representing the input vectors.12331234OUTPUT:12351236- Value of the second fundamental form evaluated on the given vectors.12371238EXAMPLES:12391240We evaluate the second fundamental form on two symbolic vectors::12411242sage: u, v = var('u, v', domain='real')1243sage: v1, v2, w1, w2 = var('v1, v2, w1, w2', domain='real')1244sage: assume(cos(v) > 0)1245sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')1246sage: sphere.second_fundamental_form(vector([v1, v2]), vector([w1, w2]))1247-v1*w1*cos(v)^2 - v2*w212481249We evaluate the second fundamental form on vectors with numerical1250components::12511252sage: vect = vector([1,2])1253sage: sphere.second_fundamental_form(vect, vect)1254-cos(v)^2 - 41255sage: sphere.second_fundamental_form([1,1], [2,1])1256-2*cos(v)^2 - 112571258"""1259hh = self.second_fundamental_form_coefficients()1260return sum(hh[(i, j)] * vector1[i - 1] * vector2[j - 1]1261for (i, j) in product((1, 2), repeat=2))126212631264def gauss_curvature(self):1265r"""1266Finds the gaussian curvature of the surface, given by1267$K = \frac{h_{11}h_{22} - h_{12}^2}{g_{11}g_{22} - g_{12}^2}$,1268where $g_{ij}$ and $h_{ij}$ are the coefficients of the first1269and second fundamental form, respectively.12701271OUTPUT:12721273- Gaussian curvature of the surface.12741275EXAMPLES::12761277sage: R = var('R')1278sage: assume(R>0)1279sage: u, v = var('u,v', domain='real')1280sage: assume(cos(v)>0)1281sage: sphere = ParametrizedSurface3D([R*cos(u)*cos(v),R*sin(u)*cos(v),R*sin(v)],[u,v],'sphere')1282sage: sphere.gauss_curvature()1283R^(-2)12841285"""1286hh = self.second_fundamental_form_coefficients()1287return _simplify_full_rad(1288(hh[(1,1)] * hh[(2,2)] - hh[(1,2)]**2)/self.area_form_squared())128912901291def mean_curvature(self):1292r"""1293Finds the mean curvature of the surface, given by1294$H = \frac{1}{2}\frac{g_{22}h_{11} - 2g_{12}h_{12} + g_{11}h_{22}}{g_{11}g_{22} - g_{12}^2}$,1295where $g_{ij}$ and $h_{ij}$ are the components of the first and second1296fundamental forms, respectively.12971298OUTPUT:12991300- Mean curvature of the surface13011302EXAMPLES::13031304sage: R = var('R')1305sage: assume(R>0)1306sage: u, v = var('u,v', domain='real')1307sage: assume(cos(v)>0)1308sage: sphere = ParametrizedSurface3D([R*cos(u)*cos(v),R*sin(u)*cos(v),R*sin(v)],[u,v],'sphere')1309sage: sphere.mean_curvature()1310-1/R13111312"""1313gg = self.first_fundamental_form_coefficients()1314hh = self.second_fundamental_form_coefficients()1315denom = 2*self.area_form_squared()1316numer = (gg[(2,2)]*hh[(1,1)] - 2*gg[(1,2)]*hh[(1,2)] +1317gg[(1,1)]*hh[(2,2)]).simplify_full()1318return _simplify_full_rad(numer/denom)131913201321@cached_method1322def shape_operator_coefficients(self):1323r"""1324Returns the components of the shape operator of the surface as a1325dictionary. See ``shape_operator`` for more information.13261327OUTPUT:13281329- Dictionary where the keys are two-tuples ``(i, j)``, with values the1330corresponding component of the shape operator.13311332EXAMPLES::13331334sage: R = var('R')1335sage: u, v = var('u,v', domain='real')1336sage: assume(cos(v)>0)1337sage: sphere = ParametrizedSurface3D([R*cos(u)*cos(v),R*sin(u)*cos(v),R*sin(v)],[u,v],'sphere')1338sage: sphere.shape_operator_coefficients()1339{(1, 2): 0, (1, 1): -1/R, (2, 1): 0, (2, 2): -1/R}13401341"""13421343gi = self.first_fundamental_form_inverse_coefficients()1344hh = self.second_fundamental_form_coefficients()13451346sh_op11 = _simplify_full_rad(gi[(1,1)]*hh[(1,1)] + gi[(1,2)]*hh[(1,2)])1347sh_op12 = _simplify_full_rad(gi[(1,1)]*hh[(2,1)] + gi[(1,2)]*hh[(2,2)])1348sh_op21 = _simplify_full_rad(gi[(2,1)]*hh[(1,1)] + gi[(2,2)]*hh[(1,2)])1349sh_op22 = _simplify_full_rad(gi[(2,1)]*hh[(2,1)] + gi[(2,2)]*hh[(2,2)])13501351return {(1,1): sh_op11, (1,2): sh_op12, (2,1): sh_op21, (2,2): sh_op22}135213531354def shape_operator(self):1355r"""1356Returns the shape operator of the surface as a matrix. The shape1357operator is defined as the derivative of the Gauss map, and is1358computed here in terms of the first and second fundamental form by1359means of the Weingarten equations.13601361OUTPUT:13621363- Matrix of the shape operator13641365EXAMPLES::13661367sage: R = var('R')1368sage: assume(R>0)1369sage: u, v = var('u,v', domain='real')1370sage: assume(cos(v)>0)1371sage: sphere = ParametrizedSurface3D([R*cos(u)*cos(v),R*sin(u)*cos(v),R*sin(v)],[u,v],'sphere')1372sage: S = sphere.shape_operator(); S1373[-1/R 0]1374[ 0 -1/R]13751376The eigenvalues of the shape operator are the principal curvatures of1377the surface::13781379sage: u, v = var('u,v', domain='real')1380sage: paraboloid = ParametrizedSurface3D([u, v, u^2+v^2], [u, v], 'paraboloid')1381sage: S = paraboloid.shape_operator(); S1382[2*(4*v^2 + 1)/(4*u^2 + 4*v^2 + 1)^(3/2) -8*u*v/(4*u^2 + 4*v^2 + 1)^(3/2)]1383[ -8*u*v/(4*u^2 + 4*v^2 + 1)^(3/2) 2*(4*u^2 + 1)/(4*u^2 + 4*v^2 + 1)^(3/2)]1384sage: S.eigenvalues()1385[2*sqrt(4*u^2 + 4*v^2 + 1)/(16*u^4 + 16*v^4 + 8*(4*u^2 + 1)*v^2 + 8*u^2 + 1), 2/sqrt(4*u^2 + 4*v^2 + 1)]13861387"""13881389shop = self.shape_operator_coefficients()1390shop_matrix=matrix([[shop[(1,1)],shop[(1,2)]],1391[shop[(2,1)],shop[(2,2)]]])1392return shop_matrix139313941395def principal_directions(self):1396r"""1397Finds the principal curvatures and principal directions of the surface13981399OUTPUT:14001401For each principal curvature, returns a list of the form1402$(\rho, V, n)$, where $\rho$ is the principal curvature,1403$V$ is the corresponding principal direction, and $n$ is1404the multiplicity.14051406EXAMPLES::14071408sage: u, v = var('u, v', domain='real')1409sage: R, r = var('R,r', domain='real')1410sage: assume(R>r,r>0)1411sage: torus = ParametrizedSurface3D([(R+r*cos(v))*cos(u),(R+r*cos(v))*sin(u),r*sin(v)],[u,v],'torus')1412sage: torus.principal_directions()1413[(-cos(v)/(r*cos(v) + R), [(1, 0)], 1), (-1/r, [(0, 1)], 1)]14141415"""1416return self.shape_operator().eigenvectors_left()141714181419@cached_method1420def connection_coefficients(self):1421r"""1422Computes the connection coefficients or Christoffel symbols1423$\Gamma^k_{ij}$ of the surface. If the coefficients of the first1424fundamental form are given by $g_{ij}$ (where $i, j = 1, 2$), then1425$\Gamma^k_{ij} = \frac{1}{2} g^{kl} \left( \frac{\partial g_{li}}{\partial x^j}1426- \frac{\partial g_{ij}}{\partial x^l}1427+ \frac{\partial g_{lj}}{\partial x^i} \right)$.1428Here, $(g^{kl})$ is the inverse of the matrix $(g_{ij})$, with1429$i, j = 1, 2$.14301431OUTPUT:14321433Dictionary of connection coefficients, where the keys are 3-tuples1434$(i,j,k)$ and the values are the corresponding coefficients1435$\Gamma^k_{ij}$.14361437EXAMPLES::14381439sage: r = var('r')1440sage: assume(r > 0)1441sage: u, v = var('u,v', domain='real')1442sage: assume(cos(v)>0)1443sage: sphere = ParametrizedSurface3D([r*cos(u)*cos(v),r*sin(u)*cos(v),r*sin(v)],[u,v],'sphere')1444sage: sphere.connection_coefficients()1445{(1, 2, 1): -sin(v)/cos(v), (2, 2, 2): 0, (1, 2, 2): 0, (2, 1, 1): -sin(v)/cos(v), (1, 1, 2): cos(v)*sin(v), (2, 2, 1): 0, (2, 1, 2): 0, (1, 1, 1): 0}14461447"""1448x = self.variables1449gg = self.first_fundamental_form_coefficients()1450gi = self.first_fundamental_form_inverse_coefficients()14511452dg = {}1453for i,j,k in product((1, 2), repeat=3):1454dg[(i,j,k)] = _simplify_full_rad(gg[(j,k)].differentiate(x[i]))14551456structfun={}1457for i,j,k in product((1, 2), repeat=3):1458structfun[(i,j,k)] = sum(gi[(k,s)]*(dg[(i,j,s)] + dg[(j,i,s)]1459-dg[(s,i,j)])/21460for s in (1,2))1461structfun[(i,j,k)] = _simplify_full_rad(structfun[(i,j,k)])1462return structfun146314641465@cached_method1466def _create_geodesic_ode_system(self):1467r"""1468Helper method to create a fast floating-point version of the1469geodesic equations, used by :meth:`geodesics_numerical`.14701471EXAMPLES::1472sage: p, q = var('p,q', domain='real')1473sage: sphere = ParametrizedSurface3D([cos(q)*cos(p),sin(q)*cos(p),sin(p)],[p,q],'sphere')1474sage: ode = sphere._create_geodesic_ode_system()1475sage: ode.function(0.0, (1.0, 0.0, 1.0, 1.0))1476[1.00000000000000, 1.00000000000000, -0.4546487134128409, 3.114815449309804]14771478"""1479from sage.ext.fast_eval import fast_float1480from sage.calculus.var import var1481from sage.gsl.ode import ode_solver14821483u1 = self.variables[1]1484u2 = self.variables[2]1485v1, v2 = var('v1, v2', domain='real')14861487C = self.connection_coefficients()14881489dv1 = - C[(1,1,1)]*v1**2 - 2*C[(1,2,1)]*v1*v2 - C[(2,2,1)]*v2**21490dv2 = - C[(1,1,2)]*v1**2 - 2*C[(1,2,2)]*v1*v2 - C[(2,2,2)]*v2**21491fun1 = fast_float(dv1, str(u1), str(u2), str(v1), str(v2))1492fun2 = fast_float(dv2, str(u1), str(u2), str(v1), str(v2))14931494geodesic_ode = ode_solver()1495geodesic_ode.function = \1496lambda t, (u1, u2, v1, v2) : \1497[v1, v2, fun1(u1, u2, v1, v2), fun2(u1, u2, v1, v2)]1498return geodesic_ode149915001501def geodesics_numerical(self, p0, v0, tinterval):1502r"""1503Numerical integration of the geodesic equations. Explicitly, the1504geodesic equations are given by1505$\frac{d^2 u^i}{dt^2} + \Gamma^i_{jk} \frac{d u^j}{dt} \frac{d u^k}{dt} = 0$.15061507Solving these equations gives the coordinates $(u^1, u^2)$ of1508the geodesic on the surface. The coordinates in space can1509then be found by substituting $(u^1, u^2)$ into the vector1510$\vec{r}(u^1, u^2)$ representing the surface.15111512ALGORITHM:15131514The geodesic equations are integrated forward in time using1515the ode solvers from ``sage.gsl.ode``. See the member1516function ``_create_geodesic_ode_system`` for more details.15171518INPUT:15191520- ``p0`` - 2-tuple with coordinates of the initial point.15211522- ``v0`` - 2-tuple with components of the initial tangent vector to the geodesic.15231524- ``tinterval`` - List ``[a, b, M]``, where ``(a,b)`` is the domain of the geodesic and ``M`` is the number of subdivision points used when returning the solution.15251526OUTPUT:15271528List of lists ``[t, [u1(t), u2(t)], [v1(t), v2(t)], [x1(t), x2(t), x3(t)]]``, where15291530- ``t`` is a subdivision point;15311532- ``[u1(t), u2(t)]`` are the intrinsic coordinates of the geodesic point;15331534- ``[v1(t), v2(t)]`` are the intrinsic coordinates of the tangent vector to the geodesic;15351536- ``[x1(t), x2(t), x3(t)]`` are the coordinates of the geodesic point in the three-dimensional space.15371538EXAMPLES::15391540sage: p, q = var('p,q', domain='real')1541sage: assume(cos(q)>0)1542sage: sphere = ParametrizedSurface3D([cos(q)*cos(p),sin(q)*cos(p),sin(p)],[p,q],'sphere')1543sage: geodesic = sphere.geodesics_numerical([0.0,0.0],[1.0,1.0],[0,2*pi,5])1544sage: times, points, tangent_vectors, ext_points = zip(*geodesic)15451546sage: round4 = lambda vec: [N(x, digits=4) for x in vec] # helper function to round to 4 digits1547sage: round4(times)1548[0.0000, 1.257, 2.513, 3.770, 5.027, 6.283]1549sage: [round4(p) for p in points]1550[[0.0000, 0.0000], [0.7644, 1.859], [-0.2876, 3.442], [-0.6137, 5.502], [0.5464, 6.937], [0.3714, 9.025]]1551sage: [round4(p) for p in ext_points]1552[[1.000, 0.0000, 0.0000], [-0.2049, 0.6921, 0.6921], [-0.9160, -0.2836, -0.2836], [0.5803, -0.5759, -0.5759], [0.6782, 0.5196, 0.5196], [-0.8582, 0.3629, 0.3629]]1553"""15541555u1 = self.variables[1]1556u2 = self.variables[2]15571558solver = self._create_geodesic_ode_system()15591560t_interval, n = tinterval[0:2], tinterval[2]1561solver.y_0 = [p0[0], p0[1], v0[0], v0[1]]1562solver.ode_solve(t_span=t_interval, num_points=n)15631564parsed_solution = \1565[[vec[0], vec[1][0:2], vec[1][2:], self.point(vec[1])]1566for vec in solver.solution]15671568return parsed_solution156915701571@cached_method1572def _create_pt_ode_system(self, curve, t):1573"""1574Helper method to create a fast floating-point version of the parallel1575transport equations, used by ``parallel_translation_numerical``.15761577INPUT:15781579- ``curve`` - curve in intrinsic coordinates along which to do parallel transport.1580- ``t`` - curve parameter15811582EXAMPLES::1583sage: p, q = var('p,q', domain='real')1584sage: sphere = ParametrizedSurface3D([cos(q)*cos(p),sin(q)*cos(p),sin(p)],[p,q],'sphere')1585sage: s = var('s')1586sage: ode = sphere._create_pt_ode_system((s, s), s)1587sage: ode.function(0.0, (1.0, 1.0))1588[-0.0, 0.0]15891590"""15911592from sage.ext.fast_eval import fast_float1593from sage.calculus.var import var1594from sage.gsl.ode import ode_solver15951596u1 = self.variables[1]1597u2 = self.variables[2]1598v1, v2 = var('v1, v2', domain='real')15991600du1 = diff(curve[0], t)1601du2 = diff(curve[1], t)16021603C = self.connection_coefficients()1604for coef in C:1605C[coef] = C[coef].subs({u1: curve[0], u2: curve[1]})16061607dv1 = - C[(1,1,1)]*v1*du1 - C[(1,2,1)]*(du1*v2 + du2*v1) - \1608C[(2,2,1)]*du2*v21609dv2 = - C[(1,1,2)]*v1*du1 - C[(1,2,2)]*(du1*v2 + du2*v1) - \1610C[(2,2,2)]*du2*v21611fun1 = fast_float(dv1, str(t), str(v1), str(v2))1612fun2 = fast_float(dv2, str(t), str(v1), str(v2))16131614pt_ode = ode_solver()1615pt_ode.function = lambda t, (v1, v2): [fun1(t, v1, v2), fun2(t, v1, v2)]1616return pt_ode161716181619def parallel_translation_numerical(self,curve,t,v0,tinterval):1620r"""1621Numerically solves the equations for parallel translation of a vector1622along a curve on the surface. Explicitly, the equations for parallel1623translation are given by1624$\frac{d u^i}{dt} + u^j \frac{d c^k}{dt} \Gamma^i_{jk} = 0$,1625where $\Gamma^i_{jk}$ are the connection coefficients of the surface,1626the vector to be transported has components $u^j$ and the curve along1627which to transport has components $c^k$.16281629ALGORITHM:16301631The parallel transport equations are integrated forward in time using1632the ode solvers from ``sage.gsl.ode``. See :meth:`_create_pt_ode_system`1633for more details.16341635INPUT:16361637- ``curve`` - 2-tuple of functions which determine the curve with respect to1638the local coordinate system;16391640- ``t`` - symbolic variable denoting the curve parameter;16411642- ``v0`` - 2-tuple representing the initial vector;16431644- ``tinterval`` - list ``[a, b, N]``, where ``(a, b)`` is the domain of the curve1645and ``N`` is the number of subdivision points.16461647OUTPUT:16481649The list consisting of lists ``[t, [v1(t), v2(t)]]``, where16501651- ``t`` is a subdivision point;16521653- ``[v1(t), v2(t)]`` is the list of coordinates of the vector parallel translated1654along the curve.16551656EXAMPLES::16571658sage: p, q = var('p,q', domain='real')1659sage: v = [p,q]1660sage: assume(cos(q)>0)1661sage: sphere = ParametrizedSurface3D([cos(q)*cos(p),sin(q)*cos(p),sin(p)],v,'sphere')1662sage: s = var('s')1663sage: vector_field = sphere.parallel_translation_numerical([s,s],s,[1.0,1.0],[0.0, pi/4, 5])1664sage: times, components = zip(*vector_field)16651666sage: round4 = lambda vec: [N(x, digits=4) for x in vec] # helper function to round to 4 digits1667sage: round4(times)1668[0.0000, 0.1571, 0.3142, 0.4712, 0.6283, 0.7854]1669sage: [round4(v) for v in components]1670[[1.000, 1.000], [0.9876, 1.025], [0.9499, 1.102], [0.8853, 1.238], [0.7920, 1.448], [0.6687, 1.762]]16711672"""16731674u1 = self.variables[1]1675u2 = self.variables[2]16761677solver = self._create_pt_ode_system(tuple(curve), t)16781679t_interval, n = tinterval[0:2], tinterval[2]1680solver.y_0 = v01681solver.ode_solve(t_span=t_interval, num_points=n)16821683return solver.solution168416851686