passagemath-symbolics 10.8.1a1__cp314-cp314t-musllinux_1_2_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_symbolics/__init__.py +3 -0
- passagemath_symbolics-10.8.1a1.dist-info/METADATA +186 -0
- passagemath_symbolics-10.8.1a1.dist-info/RECORD +181 -0
- passagemath_symbolics-10.8.1a1.dist-info/WHEEL +5 -0
- passagemath_symbolics-10.8.1a1.dist-info/top_level.txt +3 -0
- sage/all__sagemath_symbolics.py +17 -0
- sage/calculus/all.py +14 -0
- sage/calculus/calculus.py +2838 -0
- sage/calculus/desolvers.py +1864 -0
- sage/calculus/predefined.py +51 -0
- sage/calculus/tests.py +225 -0
- sage/calculus/var.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/calculus/var.pyx +401 -0
- sage/dynamics/all__sagemath_symbolics.py +6 -0
- sage/dynamics/complex_dynamics/all.py +5 -0
- sage/dynamics/complex_dynamics/mandel_julia.py +765 -0
- sage/dynamics/complex_dynamics/mandel_julia_helper.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/dynamics/complex_dynamics/mandel_julia_helper.pyx +1034 -0
- sage/ext/all__sagemath_symbolics.py +1 -0
- sage/ext_data/kenzo/CP2.txt +45 -0
- sage/ext_data/kenzo/CP3.txt +349 -0
- sage/ext_data/kenzo/CP4.txt +4774 -0
- sage/ext_data/kenzo/README.txt +49 -0
- sage/ext_data/kenzo/S4.txt +20 -0
- sage/ext_data/magma/latex/latex.m +1021 -0
- sage/ext_data/magma/latex/latex.spec +1 -0
- sage/ext_data/magma/sage/basic.m +356 -0
- sage/ext_data/magma/sage/sage.spec +1 -0
- sage/ext_data/magma/spec +9 -0
- sage/geometry/all__sagemath_symbolics.py +8 -0
- sage/geometry/hyperbolic_space/all.py +5 -0
- sage/geometry/hyperbolic_space/hyperbolic_coercion.py +755 -0
- sage/geometry/hyperbolic_space/hyperbolic_constants.py +5 -0
- sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +2419 -0
- sage/geometry/hyperbolic_space/hyperbolic_interface.py +206 -0
- sage/geometry/hyperbolic_space/hyperbolic_isometry.py +1083 -0
- sage/geometry/hyperbolic_space/hyperbolic_model.py +1502 -0
- sage/geometry/hyperbolic_space/hyperbolic_point.py +621 -0
- sage/geometry/riemannian_manifolds/all.py +7 -0
- sage/geometry/riemannian_manifolds/parametrized_surface3d.py +1632 -0
- sage/geometry/riemannian_manifolds/surface3d_generators.py +461 -0
- sage/interfaces/all__sagemath_symbolics.py +1 -0
- sage/interfaces/magma.py +2991 -0
- sage/interfaces/magma_free.py +90 -0
- sage/interfaces/maple.py +1402 -0
- sage/interfaces/mathematica.py +1345 -0
- sage/interfaces/mathics.py +1312 -0
- sage/interfaces/sympy.py +1398 -0
- sage/interfaces/sympy_wrapper.py +197 -0
- sage/interfaces/tides.py +938 -0
- sage/libs/all__sagemath_symbolics.py +6 -0
- sage/manifolds/all.py +7 -0
- sage/manifolds/calculus_method.py +553 -0
- sage/manifolds/catalog.py +437 -0
- sage/manifolds/chart.py +4010 -0
- sage/manifolds/chart_func.py +3416 -0
- sage/manifolds/continuous_map.py +2183 -0
- sage/manifolds/continuous_map_image.py +155 -0
- sage/manifolds/differentiable/affine_connection.py +2475 -0
- sage/manifolds/differentiable/all.py +1 -0
- sage/manifolds/differentiable/automorphismfield.py +1383 -0
- sage/manifolds/differentiable/automorphismfield_group.py +604 -0
- sage/manifolds/differentiable/bundle_connection.py +1445 -0
- sage/manifolds/differentiable/characteristic_cohomology_class.py +1840 -0
- sage/manifolds/differentiable/chart.py +1241 -0
- sage/manifolds/differentiable/curve.py +1028 -0
- sage/manifolds/differentiable/de_rham_cohomology.py +541 -0
- sage/manifolds/differentiable/degenerate.py +559 -0
- sage/manifolds/differentiable/degenerate_submanifold.py +1668 -0
- sage/manifolds/differentiable/diff_form.py +1660 -0
- sage/manifolds/differentiable/diff_form_module.py +1062 -0
- sage/manifolds/differentiable/diff_map.py +1315 -0
- sage/manifolds/differentiable/differentiable_submanifold.py +291 -0
- sage/manifolds/differentiable/examples/all.py +1 -0
- sage/manifolds/differentiable/examples/euclidean.py +2517 -0
- sage/manifolds/differentiable/examples/real_line.py +897 -0
- sage/manifolds/differentiable/examples/sphere.py +1186 -0
- sage/manifolds/differentiable/examples/symplectic_space.py +187 -0
- sage/manifolds/differentiable/examples/symplectic_space_test.py +40 -0
- sage/manifolds/differentiable/integrated_curve.py +4035 -0
- sage/manifolds/differentiable/levi_civita_connection.py +841 -0
- sage/manifolds/differentiable/manifold.py +4254 -0
- sage/manifolds/differentiable/manifold_homset.py +1826 -0
- sage/manifolds/differentiable/metric.py +3032 -0
- sage/manifolds/differentiable/mixed_form.py +1507 -0
- sage/manifolds/differentiable/mixed_form_algebra.py +559 -0
- sage/manifolds/differentiable/multivector_module.py +800 -0
- sage/manifolds/differentiable/multivectorfield.py +1522 -0
- sage/manifolds/differentiable/poisson_tensor.py +268 -0
- sage/manifolds/differentiable/pseudo_riemannian.py +755 -0
- sage/manifolds/differentiable/pseudo_riemannian_submanifold.py +1839 -0
- sage/manifolds/differentiable/scalarfield.py +1343 -0
- sage/manifolds/differentiable/scalarfield_algebra.py +472 -0
- sage/manifolds/differentiable/symplectic_form.py +912 -0
- sage/manifolds/differentiable/symplectic_form_test.py +220 -0
- sage/manifolds/differentiable/tangent_space.py +412 -0
- sage/manifolds/differentiable/tangent_vector.py +616 -0
- sage/manifolds/differentiable/tensorfield.py +4665 -0
- sage/manifolds/differentiable/tensorfield_module.py +963 -0
- sage/manifolds/differentiable/tensorfield_paral.py +2450 -0
- sage/manifolds/differentiable/tensorfield_paral_test.py +16 -0
- sage/manifolds/differentiable/vector_bundle.py +1725 -0
- sage/manifolds/differentiable/vectorfield.py +1717 -0
- sage/manifolds/differentiable/vectorfield_module.py +2445 -0
- sage/manifolds/differentiable/vectorframe.py +1832 -0
- sage/manifolds/family.py +270 -0
- sage/manifolds/local_frame.py +1490 -0
- sage/manifolds/manifold.py +3090 -0
- sage/manifolds/manifold_homset.py +452 -0
- sage/manifolds/operators.py +359 -0
- sage/manifolds/point.py +994 -0
- sage/manifolds/scalarfield.py +3718 -0
- sage/manifolds/scalarfield_algebra.py +629 -0
- sage/manifolds/section.py +3111 -0
- sage/manifolds/section_module.py +831 -0
- sage/manifolds/structure.py +229 -0
- sage/manifolds/subset.py +2721 -0
- sage/manifolds/subsets/all.py +1 -0
- sage/manifolds/subsets/closure.py +131 -0
- sage/manifolds/subsets/pullback.py +883 -0
- sage/manifolds/topological_submanifold.py +891 -0
- sage/manifolds/trivialization.py +733 -0
- sage/manifolds/utilities.py +1348 -0
- sage/manifolds/vector_bundle.py +1347 -0
- sage/manifolds/vector_bundle_fiber.py +332 -0
- sage/manifolds/vector_bundle_fiber_element.py +111 -0
- sage/matrix/all__sagemath_symbolics.py +1 -0
- sage/matrix/matrix_symbolic_dense.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/matrix/matrix_symbolic_dense.pxd +6 -0
- sage/matrix/matrix_symbolic_dense.pyx +1030 -0
- sage/matrix/matrix_symbolic_sparse.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/matrix/matrix_symbolic_sparse.pxd +6 -0
- sage/matrix/matrix_symbolic_sparse.pyx +1038 -0
- sage/modules/all__sagemath_symbolics.py +1 -0
- sage/modules/vector_callable_symbolic_dense.py +105 -0
- sage/modules/vector_symbolic_dense.py +116 -0
- sage/modules/vector_symbolic_sparse.py +118 -0
- sage/rings/all__sagemath_symbolics.py +4 -0
- sage/rings/asymptotic/all.py +6 -0
- sage/rings/asymptotic/asymptotic_expansion_generators.py +1485 -0
- sage/rings/asymptotic/asymptotic_ring.py +4858 -0
- sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +4106 -0
- sage/rings/asymptotic/growth_group.py +5373 -0
- sage/rings/asymptotic/growth_group_cartesian.py +1400 -0
- sage/rings/asymptotic/term_monoid.py +5205 -0
- sage/rings/function_field/all__sagemath_symbolics.py +2 -0
- sage/rings/polynomial/all__sagemath_symbolics.py +1 -0
- sage/symbolic/all.py +15 -0
- sage/symbolic/assumptions.py +987 -0
- sage/symbolic/benchmark.py +93 -0
- sage/symbolic/callable.py +456 -0
- sage/symbolic/callable.pyi +66 -0
- sage/symbolic/comparison_impl.pyi +38 -0
- sage/symbolic/complexity_measures.py +35 -0
- sage/symbolic/constants.py +1286 -0
- sage/symbolic/constants_c_impl.pyi +10 -0
- sage/symbolic/expression_conversion_algebraic.py +310 -0
- sage/symbolic/expression_conversion_sympy.py +317 -0
- sage/symbolic/expression_conversions.py +1727 -0
- sage/symbolic/function_factory.py +355 -0
- sage/symbolic/function_factory.pyi +41 -0
- sage/symbolic/getitem_impl.pyi +24 -0
- sage/symbolic/integration/all.py +1 -0
- sage/symbolic/integration/external.py +271 -0
- sage/symbolic/integration/integral.py +1075 -0
- sage/symbolic/maxima_wrapper.py +162 -0
- sage/symbolic/operators.py +267 -0
- sage/symbolic/operators.pyi +61 -0
- sage/symbolic/pynac_constant_impl.pyi +13 -0
- sage/symbolic/pynac_function_impl.pyi +8 -0
- sage/symbolic/random_tests.py +461 -0
- sage/symbolic/relation.py +2062 -0
- sage/symbolic/ring.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/symbolic/ring.pxd +5 -0
- sage/symbolic/ring.pyi +110 -0
- sage/symbolic/ring.pyx +1393 -0
- sage/symbolic/series_impl.pyi +10 -0
- sage/symbolic/subring.py +1025 -0
- sage/symbolic/symengine.py +19 -0
- sage/symbolic/tests.py +40 -0
- sage/symbolic/units.py +1468 -0
|
@@ -0,0 +1,1632 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-symbolics
|
|
2
|
+
"""
|
|
3
|
+
Differential Geometry of Parametrized Surfaces
|
|
4
|
+
|
|
5
|
+
AUTHORS:
|
|
6
|
+
|
|
7
|
+
- Mikhail Malakhaltsev (2010-09-25): initial version
|
|
8
|
+
- Joris Vankerschaver (2010-10-25): implementation, doctests
|
|
9
|
+
"""
|
|
10
|
+
# ****************************************************************************
|
|
11
|
+
# Copyright (C) 2010 Mikhail Malakhaltsev <mikarm@gmail.com>
|
|
12
|
+
# Copyright (C) 2010 Joris Vankerschaver <joris.vankerschaver@gmail.com>
|
|
13
|
+
#
|
|
14
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
15
|
+
# https://www.gnu.org/licenses/
|
|
16
|
+
# ****************************************************************************
|
|
17
|
+
|
|
18
|
+
from itertools import product
|
|
19
|
+
|
|
20
|
+
from sage.structure.sage_object import SageObject
|
|
21
|
+
from sage.modules.free_module_element import vector
|
|
22
|
+
from sage.matrix.constructor import matrix
|
|
23
|
+
from sage.calculus.functional import diff
|
|
24
|
+
from sage.misc.functional import sqrt
|
|
25
|
+
from sage.misc.cachefunc import cached_method
|
|
26
|
+
from sage.symbolic.ring import SR
|
|
27
|
+
from sage.symbolic.constants import pi
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _simplify_full_rad(f):
|
|
31
|
+
"""
|
|
32
|
+
Helper function to conveniently call :meth:`simplify_full` and
|
|
33
|
+
:meth:`canonicalize_radical` in succession.
|
|
34
|
+
|
|
35
|
+
INPUT:
|
|
36
|
+
|
|
37
|
+
- ``f`` -- a symbolic expression
|
|
38
|
+
|
|
39
|
+
EXAMPLES::
|
|
40
|
+
|
|
41
|
+
sage: from sage.geometry.riemannian_manifolds.parametrized_surface3d import _simplify_full_rad
|
|
42
|
+
sage: _simplify_full_rad(sqrt(x^2)/x)
|
|
43
|
+
1
|
|
44
|
+
"""
|
|
45
|
+
return f.simplify_full().canonicalize_radical()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ParametrizedSurface3D(SageObject):
|
|
49
|
+
r"""
|
|
50
|
+
Class representing a parametrized two-dimensional surface in
|
|
51
|
+
Euclidian three-space. Provides methods for calculating the main
|
|
52
|
+
geometrical objects related to such a surface, such as the first
|
|
53
|
+
and the second fundamental form, the total (Gaussian) and the mean
|
|
54
|
+
curvature, the geodesic curves, parallel transport, etc.
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
INPUT:
|
|
58
|
+
|
|
59
|
+
- ``surface_equation`` -- a 3-tuple of functions specifying a parametric
|
|
60
|
+
representation of the surface
|
|
61
|
+
|
|
62
|
+
- ``variables`` -- a 2-tuple of intrinsic coordinates `(u, v)` on the
|
|
63
|
+
surface, with `u` and `v` symbolic variables, or a 2-tuple of triples
|
|
64
|
+
`(u, u_{min}, u_{max})`, `(v, v_{min}, v_{max})` when the parameter range
|
|
65
|
+
for the coordinates is known
|
|
66
|
+
|
|
67
|
+
- ``name`` -- name of the surface (optional)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
.. NOTE::
|
|
71
|
+
|
|
72
|
+
Throughout the documentation, we use the Einstein summation
|
|
73
|
+
convention: whenever an index appears twice, once as a
|
|
74
|
+
subscript, and once as a superscript, summation over that index
|
|
75
|
+
is implied. For instance, `g_{ij} g^{jk}` stands for `\sum_j
|
|
76
|
+
g_{ij}g^{jk}`.
|
|
77
|
+
|
|
78
|
+
EXAMPLES:
|
|
79
|
+
|
|
80
|
+
We give several examples of standard surfaces in differential
|
|
81
|
+
geometry. First, let's construct an elliptic paraboloid by
|
|
82
|
+
explicitly specifying its parametric equation::
|
|
83
|
+
|
|
84
|
+
sage: u, v = var('u,v', domain='real')
|
|
85
|
+
sage: eparaboloid = ParametrizedSurface3D((u, v, u^2 + v^2), (u, v),
|
|
86
|
+
....: 'elliptic paraboloid'); eparaboloid
|
|
87
|
+
Parametrized surface ('elliptic paraboloid') with equation (u, v, u^2 + v^2)
|
|
88
|
+
|
|
89
|
+
When the ranges for the intrinsic coordinates are known, they can be
|
|
90
|
+
specified explicitly. This is mainly useful for plotting. Here we
|
|
91
|
+
construct half of an ellipsoid::
|
|
92
|
+
|
|
93
|
+
sage: u1, u2 = var ('u1, u2', domain='real')
|
|
94
|
+
sage: coords = ((u1, -pi/2, pi/2), (u2, 0, pi))
|
|
95
|
+
sage: ellipsoid_eq = (cos(u1)*cos(u2), 2*sin(u1)*cos(u2), 3*sin(u2))
|
|
96
|
+
sage: ellipsoid = ParametrizedSurface3D(ellipsoid_eq, coords, 'ellipsoid'); ellipsoid
|
|
97
|
+
Parametrized surface ('ellipsoid') with equation
|
|
98
|
+
(cos(u1)*cos(u2), 2*cos(u2)*sin(u1), 3*sin(u2))
|
|
99
|
+
sage: ellipsoid.plot() # needs sage.plot
|
|
100
|
+
Graphics3d Object
|
|
101
|
+
|
|
102
|
+
Standard surfaces can be constructed using the ``surfaces`` generator::
|
|
103
|
+
|
|
104
|
+
sage: klein = surfaces.Klein(); klein
|
|
105
|
+
Parametrized surface ('Klein bottle') with equation
|
|
106
|
+
(-(sin(1/2*u)*sin(2*v) - cos(1/2*u)*sin(v) - 1)*cos(u),
|
|
107
|
+
-(sin(1/2*u)*sin(2*v) - cos(1/2*u)*sin(v) - 1)*sin(u),
|
|
108
|
+
cos(1/2*u)*sin(2*v) + sin(1/2*u)*sin(v))
|
|
109
|
+
|
|
110
|
+
Latex representation of the surfaces::
|
|
111
|
+
|
|
112
|
+
sage: u, v = var('u, v', domain='real')
|
|
113
|
+
sage: sphere = ParametrizedSurface3D((cos(u)*cos(v), sin(u)*cos(v), sin(v)), (u, v),
|
|
114
|
+
....: 'sphere')
|
|
115
|
+
sage: print(latex(sphere))
|
|
116
|
+
\left(\cos\left(u\right) \cos\left(v\right), \cos\left(v\right) \sin\left(u\right), \sin\left(v\right)\right)
|
|
117
|
+
sage: print(sphere._latex_())
|
|
118
|
+
\left(\cos\left(u\right) \cos\left(v\right), \cos\left(v\right) \sin\left(u\right), \sin\left(v\right)\right)
|
|
119
|
+
sage: print(sphere)
|
|
120
|
+
Parametrized surface ('sphere') with equation (cos(u)*cos(v), cos(v)*sin(u), sin(v))
|
|
121
|
+
|
|
122
|
+
To plot a parametric surface, use the :meth:`plot` member function::
|
|
123
|
+
|
|
124
|
+
sage: enneper = surfaces.Enneper(); enneper
|
|
125
|
+
Parametrized surface ('Enneper's surface') with equation
|
|
126
|
+
(-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)
|
|
127
|
+
sage: enneper.plot(aspect_ratio='automatic') # needs sage.plot
|
|
128
|
+
Graphics3d Object
|
|
129
|
+
|
|
130
|
+
We construct an ellipsoid whose axes are given by symbolic variables `a`,
|
|
131
|
+
`b` and `c`, and find the natural frame of tangent vectors,
|
|
132
|
+
expressed in intrinsic coordinates. Note that the result is a
|
|
133
|
+
dictionary of vector fields::
|
|
134
|
+
|
|
135
|
+
sage: a, b, c = var('a, b, c', domain='real')
|
|
136
|
+
sage: u1, u2 = var('u1, u2', domain='real')
|
|
137
|
+
sage: ellipsoid_eq = (a*cos(u1)*cos(u2), b*sin(u1)*cos(u2), c*sin(u2))
|
|
138
|
+
sage: ellipsoid = ParametrizedSurface3D(ellipsoid_eq, (u1, u2),
|
|
139
|
+
....: 'Symbolic ellipsoid'); ellipsoid
|
|
140
|
+
Parametrized surface ('Symbolic ellipsoid') with equation
|
|
141
|
+
(a*cos(u1)*cos(u2), b*cos(u2)*sin(u1), c*sin(u2))
|
|
142
|
+
|
|
143
|
+
sage: ellipsoid.natural_frame()
|
|
144
|
+
{1: (-a*cos(u2)*sin(u1), b*cos(u1)*cos(u2), 0),
|
|
145
|
+
2: (-a*cos(u1)*sin(u2), -b*sin(u1)*sin(u2), c*cos(u2))}
|
|
146
|
+
|
|
147
|
+
We find the normal vector field to the surface. The normal vector
|
|
148
|
+
field is the vector product of the vectors of the natural frame,
|
|
149
|
+
and is given by::
|
|
150
|
+
|
|
151
|
+
sage: ellipsoid.normal_vector()
|
|
152
|
+
(b*c*cos(u1)*cos(u2)^2, a*c*cos(u2)^2*sin(u1), a*b*cos(u2)*sin(u2))
|
|
153
|
+
|
|
154
|
+
By default, the normal vector field is not normalized. To obtain
|
|
155
|
+
the unit normal vector field of the elliptic paraboloid, we put::
|
|
156
|
+
|
|
157
|
+
sage: u, v = var('u,v', domain='real')
|
|
158
|
+
sage: eparaboloid = ParametrizedSurface3D([u, v, u^2 + v^2], [u,v],
|
|
159
|
+
....: 'elliptic paraboloid')
|
|
160
|
+
sage: eparaboloid.normal_vector(normalized=True)
|
|
161
|
+
(-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))
|
|
162
|
+
|
|
163
|
+
Now let us compute the coefficients of the first fundamental form of the torus::
|
|
164
|
+
|
|
165
|
+
sage: u, v = var('u, v', domain='real')
|
|
166
|
+
sage: a, b = var('a, b', domain='real')
|
|
167
|
+
sage: torus = ParametrizedSurface3D(((a + b*cos(u))*cos(v),
|
|
168
|
+
....: (a + b*cos(u))*sin(v),
|
|
169
|
+
....: b*sin(u)), [u,v], 'torus')
|
|
170
|
+
sage: torus.first_fundamental_form_coefficients()
|
|
171
|
+
{(1, 1): b^2, (1, 2): 0, (2, 1): 0, (2, 2): b^2*cos(u)^2 + 2*a*b*cos(u) + a^2}
|
|
172
|
+
|
|
173
|
+
The first fundamental form can be used to compute the length of a
|
|
174
|
+
curve on the surface. For example, let us find the length of the
|
|
175
|
+
curve `u^1 = t`, `u^2 = t`, `t \in [0,2\pi]`, on the ellipsoid
|
|
176
|
+
with axes `a=1`, `b=1.5` and `c=1`. So we take the curve::
|
|
177
|
+
|
|
178
|
+
sage: t = var('t', domain='real')
|
|
179
|
+
sage: u1 = t
|
|
180
|
+
sage: u2 = t
|
|
181
|
+
|
|
182
|
+
Then find the tangent vector::
|
|
183
|
+
|
|
184
|
+
sage: du1 = diff(u1,t)
|
|
185
|
+
sage: du2 = diff(u2,t)
|
|
186
|
+
sage: du = vector([du1, du2]); du
|
|
187
|
+
(1, 1)
|
|
188
|
+
|
|
189
|
+
Once we specify numerical values for the axes of the ellipsoid, we can
|
|
190
|
+
determine the numerical value of the length integral::
|
|
191
|
+
|
|
192
|
+
sage: L = sqrt(ellipsoid.first_fundamental_form(du, du).substitute(u1=u1, u2=u2))
|
|
193
|
+
sage: numerical_integral(L.substitute(a=2, b=1.5, c=1),0,1)[0] # rel tol 1e-11
|
|
194
|
+
2.00127905972
|
|
195
|
+
|
|
196
|
+
We find the area of the sphere of radius `R`::
|
|
197
|
+
|
|
198
|
+
sage: R = var('R', domain='real')
|
|
199
|
+
sage: u, v = var('u,v', domain='real')
|
|
200
|
+
sage: assume(R>0)
|
|
201
|
+
sage: assume(cos(v)>0)
|
|
202
|
+
sage: sphere = ParametrizedSurface3D([R*cos(u)*cos(v), R*sin(u)*cos(v), R*sin(v)],
|
|
203
|
+
....: [u,v], 'sphere')
|
|
204
|
+
sage: integral(integral(sphere.area_form(), u, 0, 2*pi), v, -pi/2, pi/2)
|
|
205
|
+
4*pi*R^2
|
|
206
|
+
|
|
207
|
+
We can find an orthonormal frame field `\{e_1, e_2\}` of a surface
|
|
208
|
+
and calculate its structure functions. Let us first determine the
|
|
209
|
+
orthonormal frame field for the elliptic paraboloid::
|
|
210
|
+
|
|
211
|
+
sage: u, v = var('u,v', domain='real')
|
|
212
|
+
sage: eparaboloid = ParametrizedSurface3D([u, v, u^2 + v^2], [u,v],
|
|
213
|
+
....: 'elliptic paraboloid')
|
|
214
|
+
sage: eparaboloid.orthonormal_frame()
|
|
215
|
+
{1: (1/sqrt(4*u^2 + 1), 0, 2*u/sqrt(4*u^2 + 1)),
|
|
216
|
+
2: (-4*u*v/(sqrt(4*u^2 + 4*v^2 + 1)*sqrt(4*u^2 + 1)),
|
|
217
|
+
sqrt(4*u^2 + 1)/sqrt(4*u^2 + 4*v^2 + 1),
|
|
218
|
+
2*v/(sqrt(4*u^2 + 4*v^2 + 1)*sqrt(4*u^2 + 1)))}
|
|
219
|
+
|
|
220
|
+
We can express the orthogonal frame field both in exterior
|
|
221
|
+
coordinates (i.e. expressed as vector field fields in the ambient
|
|
222
|
+
space `\RR^3`, the default) or in intrinsic coordinates
|
|
223
|
+
(with respect to the natural frame). Here we use intrinsic
|
|
224
|
+
coordinates::
|
|
225
|
+
|
|
226
|
+
sage: eparaboloid.orthonormal_frame(coordinates='int')
|
|
227
|
+
{1: (1/sqrt(4*u^2 + 1), 0),
|
|
228
|
+
2: (-4*u*v/(sqrt(4*u^2 + 4*v^2 + 1)*sqrt(4*u^2 + 1)),
|
|
229
|
+
sqrt(4*u^2 + 1)/sqrt(4*u^2 + 4*v^2 + 1))}
|
|
230
|
+
|
|
231
|
+
Using the orthonormal frame in interior coordinates, we can calculate
|
|
232
|
+
the structure functions `c^k_{ij}` of the surface, defined by
|
|
233
|
+
`[e_i,e_j] = c^k_{ij} e_k`, where `[e_i, e_j]` represents the Lie
|
|
234
|
+
bracket of two frame vector fields `e_i, e_j`. For the
|
|
235
|
+
elliptic paraboloid, we get::
|
|
236
|
+
|
|
237
|
+
sage: EE = eparaboloid.orthonormal_frame(coordinates='int')
|
|
238
|
+
sage: E1 = EE[1]; E2 = EE[2]
|
|
239
|
+
sage: CC = eparaboloid.frame_structure_functions(E1,E2)
|
|
240
|
+
sage: CC[1,2,1].simplify_full()
|
|
241
|
+
4*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))
|
|
242
|
+
|
|
243
|
+
We compute the Gaussian and mean curvatures of the sphere::
|
|
244
|
+
|
|
245
|
+
sage: sphere = surfaces.Sphere(); sphere
|
|
246
|
+
Parametrized surface ('Sphere') with equation (cos(u)*cos(v), cos(v)*sin(u), sin(v))
|
|
247
|
+
sage: K = sphere.gauss_curvature(); K # Not tested -- see trac 12737
|
|
248
|
+
1
|
|
249
|
+
sage: H = sphere.mean_curvature(); H # Not tested -- see trac 12737
|
|
250
|
+
-1
|
|
251
|
+
|
|
252
|
+
We can easily generate a color plot of the Gaussian curvature of a surface.
|
|
253
|
+
Here we deal with the ellipsoid::
|
|
254
|
+
|
|
255
|
+
sage: # needs numpy
|
|
256
|
+
sage: u1, u2 = var('u1,u2', domain='real')
|
|
257
|
+
sage: u = [u1,u2]
|
|
258
|
+
sage: ellipsoid_equation(u1,u2) = [2*cos(u1)*cos(u2),1.5*cos(u1)*sin(u2),sin(u1)]
|
|
259
|
+
sage: ellipsoid = ParametrizedSurface3D(ellipsoid_equation(u1,u2), [u1, u2], 'ellipsoid')
|
|
260
|
+
sage: # set intervals for variables and the number of division points
|
|
261
|
+
sage: u1min, u1max = -1.5, 1.5
|
|
262
|
+
sage: u2min, u2max = 0, 6.28
|
|
263
|
+
sage: u1num, u2num = 10, 20
|
|
264
|
+
sage: # make the arguments array
|
|
265
|
+
sage: from numpy import linspace
|
|
266
|
+
sage: u1_array = linspace(u1min, u1max, u1num)
|
|
267
|
+
sage: u2_array = linspace(u2min, u2max, u2num)
|
|
268
|
+
sage: u_array = [(uu1,uu2) for uu1 in u1_array for uu2 in u2_array]
|
|
269
|
+
sage: # Find the gaussian curvature
|
|
270
|
+
sage: K(u1,u2) = ellipsoid.gauss_curvature()
|
|
271
|
+
sage: # Make array of K values
|
|
272
|
+
sage: K_array = [K(uu[0],uu[1]) for uu in u_array]
|
|
273
|
+
sage: # Find minimum and max of the Gauss curvature
|
|
274
|
+
sage: K_max = max(K_array)
|
|
275
|
+
sage: K_min = min(K_array)
|
|
276
|
+
sage: # Make the array of color coefficients
|
|
277
|
+
sage: cc_array = [(ccc - K_min)/(K_max - K_min) for ccc in K_array]
|
|
278
|
+
sage: points_array = [ellipsoid_equation(u_array[counter][0],
|
|
279
|
+
....: u_array[counter][1])
|
|
280
|
+
....: for counter in range(len(u_array))]
|
|
281
|
+
sage: curvature_ellipsoid_plot = sum(point([xx # needs sage.plot
|
|
282
|
+
....: for xx in points_array[counter]],
|
|
283
|
+
....: color=hue(cc_array[counter]/2))
|
|
284
|
+
....: for counter in range(len(u_array)))
|
|
285
|
+
sage: curvature_ellipsoid_plot.show(aspect_ratio=1) # needs sage.plot
|
|
286
|
+
|
|
287
|
+
We can find the principal curvatures and principal directions of the
|
|
288
|
+
elliptic paraboloid::
|
|
289
|
+
|
|
290
|
+
sage: u, v = var('u, v', domain='real')
|
|
291
|
+
sage: eparaboloid = ParametrizedSurface3D([u, v, u^2+v^2], [u, v],
|
|
292
|
+
....: 'elliptic paraboloid')
|
|
293
|
+
sage: pd = eparaboloid.principal_directions(); pd
|
|
294
|
+
[(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),
|
|
295
|
+
(2/sqrt(4*u^2 + 4*v^2 + 1), [(1, -u/v)], 1)]
|
|
296
|
+
|
|
297
|
+
We extract the principal curvatures::
|
|
298
|
+
|
|
299
|
+
sage: k1 = pd[0][0].simplify_full(); k1
|
|
300
|
+
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)
|
|
301
|
+
sage: k2 = pd[1][0].simplify_full(); k2
|
|
302
|
+
2/sqrt(4*u^2 + 4*v^2 + 1)
|
|
303
|
+
|
|
304
|
+
and check them by comparison with the Gaussian and mean curvature
|
|
305
|
+
expressed in terms of the principal curvatures::
|
|
306
|
+
|
|
307
|
+
sage: K = eparaboloid.gauss_curvature().simplify_full(); K
|
|
308
|
+
4/(16*u^4 + 16*v^4 + 8*(4*u^2 + 1)*v^2 + 8*u^2 + 1)
|
|
309
|
+
sage: H = eparaboloid.mean_curvature().simplify_full(); H
|
|
310
|
+
2*(2*u^2 + 2*v^2 + 1)/(4*u^2 + 4*v^2 + 1)^(3/2)
|
|
311
|
+
sage: (K - k1*k2).simplify_full()
|
|
312
|
+
0
|
|
313
|
+
sage: (2*H - k1 - k2).simplify_full()
|
|
314
|
+
0
|
|
315
|
+
|
|
316
|
+
We can find the intrinsic (local coordinates) of the principal directions::
|
|
317
|
+
|
|
318
|
+
sage: pd[0][1]
|
|
319
|
+
[(1, v/u)]
|
|
320
|
+
sage: pd[1][1]
|
|
321
|
+
[(1, -u/v)]
|
|
322
|
+
|
|
323
|
+
The ParametrizedSurface3D class also contains functionality to
|
|
324
|
+
compute the coefficients of the second fundamental form, the shape
|
|
325
|
+
operator, the rotation on the surface at a given angle, the
|
|
326
|
+
connection coefficients. One can also calculate numerically the
|
|
327
|
+
geodesics and the parallel translation along a curve.
|
|
328
|
+
|
|
329
|
+
Here we compute a number of geodesics on the sphere emanating
|
|
330
|
+
from the point ``(1, 0, 0)``, in various directions. The geodesics
|
|
331
|
+
intersect again in the antipodal point ``(-1, 0, 0)``, indicating
|
|
332
|
+
that these points are conjugate::
|
|
333
|
+
|
|
334
|
+
sage: S = surfaces.Sphere()
|
|
335
|
+
sage: g1 = [c[-1] for c in S.geodesics_numerical((0,0), (1,0), (0,2*pi,100))]
|
|
336
|
+
sage: g2 = [c[-1] for c in S.geodesics_numerical((0,0),
|
|
337
|
+
....: (cos(pi/3),sin(pi/3)),
|
|
338
|
+
....: (0,2*pi,100))]
|
|
339
|
+
sage: g3 = [c[-1] for c in S.geodesics_numerical((0,0),
|
|
340
|
+
....: (cos(2*pi/3),sin(2*pi/3)),
|
|
341
|
+
....: (0,2*pi,100))]
|
|
342
|
+
sage: (S.plot(opacity=0.3) + line3d(g1, color='red') # needs sage.plot
|
|
343
|
+
....: + line3d(g2, color='red') + line3d(g3, color='red')).show()
|
|
344
|
+
"""
|
|
345
|
+
|
|
346
|
+
def __init__(self, equation, variables, name=None):
|
|
347
|
+
r"""
|
|
348
|
+
See ``ParametrizedSurface3D`` for full documentation.
|
|
349
|
+
|
|
350
|
+
.. NOTE::
|
|
351
|
+
|
|
352
|
+
The orientation of the surface is determined by the
|
|
353
|
+
parametrization, that is, the natural frame with positive
|
|
354
|
+
orientation is given by `\partial_1 \vec r`, `\partial_2 \vec
|
|
355
|
+
r`.
|
|
356
|
+
|
|
357
|
+
EXAMPLES::
|
|
358
|
+
|
|
359
|
+
sage: u, v = var('u,v', domain='real')
|
|
360
|
+
sage: eq = (3*u + 3*u*v^2 - u^3, 3*v + 3*u^2*v - v^3, 3*(u^2-v^2))
|
|
361
|
+
sage: enneper = ParametrizedSurface3D(eq, (u, v),'Enneper Surface'); enneper
|
|
362
|
+
Parametrized surface ('Enneper Surface') with equation
|
|
363
|
+
(-u^3 + 3*u*v^2 + 3*u, 3*u^2*v - v^3 + 3*v, 3*u^2 - 3*v^2)
|
|
364
|
+
"""
|
|
365
|
+
self.equation = tuple(equation)
|
|
366
|
+
|
|
367
|
+
if isinstance(variables[0], (list, tuple)):
|
|
368
|
+
self.variables_range = (variables[0][1:3], variables[1][1:3])
|
|
369
|
+
self.variables_list = (variables[0][0], variables[1][0])
|
|
370
|
+
else:
|
|
371
|
+
self.variables_range = None
|
|
372
|
+
self.variables_list = variables
|
|
373
|
+
|
|
374
|
+
self.variables = {1:self.variables_list[0], 2:self.variables_list[1]}
|
|
375
|
+
self.name = name
|
|
376
|
+
|
|
377
|
+
def _latex_(self):
|
|
378
|
+
r"""
|
|
379
|
+
Return the LaTeX representation of this parametrized surface.
|
|
380
|
+
|
|
381
|
+
EXAMPLES::
|
|
382
|
+
|
|
383
|
+
sage: u, v = var('u, v')
|
|
384
|
+
sage: sphere = ParametrizedSurface3D((cos(u)*cos(v), sin(u)*cos(v), sin(v)), (u, v),'sphere')
|
|
385
|
+
sage: latex(sphere)
|
|
386
|
+
\left(\cos\left(u\right) \cos\left(v\right), \cos\left(v\right) \sin\left(u\right), \sin\left(v\right)\right)
|
|
387
|
+
sage: sphere._latex_()
|
|
388
|
+
\left(\cos\left(u\right) \cos\left(v\right), \cos\left(v\right) \sin\left(u\right), \sin\left(v\right)\right)
|
|
389
|
+
"""
|
|
390
|
+
from sage.misc.latex import latex
|
|
391
|
+
return latex(self.equation)
|
|
392
|
+
|
|
393
|
+
def _repr_(self):
|
|
394
|
+
r"""
|
|
395
|
+
Return the string representation of this parametrized surface.
|
|
396
|
+
|
|
397
|
+
EXAMPLES::
|
|
398
|
+
|
|
399
|
+
sage: u, v = var('u, v', domain='real')
|
|
400
|
+
sage: eq = (3*u + 3*u*v^2 - u^3, 3*v + 3*u^2*v - v^3, 3*(u^2-v^2))
|
|
401
|
+
sage: enneper = ParametrizedSurface3D(eq,[u,v],'enneper_surface')
|
|
402
|
+
sage: print(enneper)
|
|
403
|
+
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)
|
|
404
|
+
sage: enneper._repr_()
|
|
405
|
+
"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)"
|
|
406
|
+
"""
|
|
407
|
+
name = 'Parametrized surface'
|
|
408
|
+
if self.name is not None:
|
|
409
|
+
name += " ('%s')" % self.name
|
|
410
|
+
s = '%(designation)s with equation %(eq)s' % \
|
|
411
|
+
{'designation': name, 'eq': str(self.equation)}
|
|
412
|
+
return s
|
|
413
|
+
|
|
414
|
+
def point(self, coords):
|
|
415
|
+
r"""
|
|
416
|
+
Return a point on the surface given its intrinsic coordinates.
|
|
417
|
+
|
|
418
|
+
INPUT:
|
|
419
|
+
|
|
420
|
+
- ``coords`` -- 2-tuple specifying the intrinsic coordinates ``(u, v)`` of the point
|
|
421
|
+
|
|
422
|
+
OUTPUT: 3-vector specifying the coordinates in `\RR^3` of the point
|
|
423
|
+
|
|
424
|
+
EXAMPLES::
|
|
425
|
+
|
|
426
|
+
sage: u, v = var('u, v', domain='real')
|
|
427
|
+
sage: torus = ParametrizedSurface3D(((2 + cos(u))*cos(v),
|
|
428
|
+
....: (2 + cos(u))*sin(v),
|
|
429
|
+
....: sin(u)), [u,v], 'torus')
|
|
430
|
+
sage: torus.point((0, pi/2))
|
|
431
|
+
(0, 3, 0)
|
|
432
|
+
sage: torus.point((pi/2, pi))
|
|
433
|
+
(-2, 0, 1)
|
|
434
|
+
sage: torus.point((pi, pi/2))
|
|
435
|
+
(0, 1, 0)
|
|
436
|
+
"""
|
|
437
|
+
|
|
438
|
+
d = dict(zip(self.variables_list, coords))
|
|
439
|
+
return vector([f.subs(d) for f in self.equation])
|
|
440
|
+
|
|
441
|
+
def tangent_vector(self, coords, components):
|
|
442
|
+
r"""
|
|
443
|
+
Return the components of a tangent vector given the intrinsic
|
|
444
|
+
coordinates of the base point and the components of the vector
|
|
445
|
+
in the intrinsic frame.
|
|
446
|
+
|
|
447
|
+
INPUT:
|
|
448
|
+
|
|
449
|
+
- ``coords`` -- 2-tuple specifying the intrinsic coordinates ``(u, v)`` of the point
|
|
450
|
+
|
|
451
|
+
- ``components`` -- 2-tuple specifying the components of the tangent
|
|
452
|
+
vector in the intrinsic coordinate frame
|
|
453
|
+
|
|
454
|
+
OUTPUT: 3-vector specifying the components in `\RR^3` of the vector
|
|
455
|
+
|
|
456
|
+
EXAMPLES:
|
|
457
|
+
|
|
458
|
+
We compute two tangent vectors to Enneper's surface along the
|
|
459
|
+
coordinate lines and check that their cross product gives the
|
|
460
|
+
normal vector::
|
|
461
|
+
|
|
462
|
+
sage: u, v = var('u,v', domain='real')
|
|
463
|
+
sage: eq = (3*u + 3*u*v^2 - u^3, 3*v + 3*u^2*v - v^3, 3*(u^2-v^2))
|
|
464
|
+
sage: e = ParametrizedSurface3D(eq, (u, v), 'Enneper Surface')
|
|
465
|
+
|
|
466
|
+
sage: w1 = e.tangent_vector((1, 2), (1, 0)); w1
|
|
467
|
+
(12, 12, 6)
|
|
468
|
+
sage: w2 = e.tangent_vector((1, 2), (0, 1)); w2
|
|
469
|
+
(12, -6, -12)
|
|
470
|
+
sage: w1.cross_product(w2)
|
|
471
|
+
(-108, 216, -216)
|
|
472
|
+
|
|
473
|
+
sage: n = e.normal_vector().subs({u: 1, v: 2}); n
|
|
474
|
+
(-108, 216, -216)
|
|
475
|
+
sage: n == w1.cross_product(w2)
|
|
476
|
+
True
|
|
477
|
+
"""
|
|
478
|
+
|
|
479
|
+
components = vector(components)
|
|
480
|
+
d = dict(zip(self.variables_list, coords))
|
|
481
|
+
jacobian = matrix([[f.diff(u).subs(d) for u in self.variables_list]
|
|
482
|
+
for f in self.equation])
|
|
483
|
+
return jacobian * components
|
|
484
|
+
|
|
485
|
+
def plot(self, urange=None, vrange=None, **kwds):
|
|
486
|
+
r"""
|
|
487
|
+
Enable easy plotting directly from the surface class.
|
|
488
|
+
|
|
489
|
+
The optional keywords ``urange`` and ``vrange`` specify the range for
|
|
490
|
+
the surface parameters `u` and `v`. If either of these parameters
|
|
491
|
+
is ``None``, the method checks whether a parameter range was
|
|
492
|
+
specified when the surface was created. If not, the default of
|
|
493
|
+
`(0, 2 \pi)` is used.
|
|
494
|
+
|
|
495
|
+
INPUT:
|
|
496
|
+
|
|
497
|
+
- ``urange`` -- 2-tuple specifying the parameter range for `u`
|
|
498
|
+
- ``vrange`` -- 2-tuple specifying the parameter range for `v`
|
|
499
|
+
|
|
500
|
+
EXAMPLES::
|
|
501
|
+
|
|
502
|
+
sage: u, v = var('u, v', domain='real')
|
|
503
|
+
sage: eq = (3*u + 3*u*v^2 - u^3, 3*v + 3*u^2*v - v^3, 3*(u^2-v^2))
|
|
504
|
+
sage: enneper = ParametrizedSurface3D(eq, (u, v), 'Enneper Surface')
|
|
505
|
+
sage: enneper.plot((-5, 5), (-5, 5)) # needs sage.plot
|
|
506
|
+
Graphics3d Object
|
|
507
|
+
"""
|
|
508
|
+
|
|
509
|
+
from sage.plot.plot3d.parametric_plot3d import parametric_plot3d
|
|
510
|
+
|
|
511
|
+
if self.variables_range is None:
|
|
512
|
+
if urange is None:
|
|
513
|
+
urange = (0, 2*pi)
|
|
514
|
+
if vrange is None:
|
|
515
|
+
vrange = (0, 2*pi)
|
|
516
|
+
else:
|
|
517
|
+
if urange is None:
|
|
518
|
+
urange = self.variables_range[0]
|
|
519
|
+
if vrange is None:
|
|
520
|
+
vrange = self.variables_range[1]
|
|
521
|
+
|
|
522
|
+
urange3 = (self.variables[1],) + tuple(urange)
|
|
523
|
+
vrange3 = (self.variables[2],) + tuple(vrange)
|
|
524
|
+
P = parametric_plot3d(self.equation, urange3, vrange3, **kwds)
|
|
525
|
+
|
|
526
|
+
return P
|
|
527
|
+
|
|
528
|
+
@cached_method
|
|
529
|
+
def natural_frame(self):
|
|
530
|
+
"""
|
|
531
|
+
Return the natural tangent frame on the parametrized surface.
|
|
532
|
+
The vectors of this frame are tangent to the coordinate lines
|
|
533
|
+
on the surface.
|
|
534
|
+
|
|
535
|
+
OUTPUT: the natural frame as a dictionary
|
|
536
|
+
|
|
537
|
+
EXAMPLES::
|
|
538
|
+
|
|
539
|
+
sage: u, v = var('u, v', domain='real')
|
|
540
|
+
sage: eparaboloid = ParametrizedSurface3D((u, v, u^2+v^2), (u, v),
|
|
541
|
+
....: 'elliptic paraboloid')
|
|
542
|
+
sage: eparaboloid.natural_frame()
|
|
543
|
+
{1: (1, 0, 2*u), 2: (0, 1, 2*v)}
|
|
544
|
+
"""
|
|
545
|
+
|
|
546
|
+
dr1 = \
|
|
547
|
+
vector([_simplify_full_rad( diff(f,self.variables[1]) )
|
|
548
|
+
for f in self.equation])
|
|
549
|
+
dr2 = \
|
|
550
|
+
vector([_simplify_full_rad( diff(f,self.variables[2]) )
|
|
551
|
+
for f in self.equation])
|
|
552
|
+
|
|
553
|
+
return {1:dr1, 2:dr2}
|
|
554
|
+
|
|
555
|
+
@cached_method
|
|
556
|
+
def normal_vector(self, normalized=False):
|
|
557
|
+
"""
|
|
558
|
+
Return the normal vector field of the parametrized surface.
|
|
559
|
+
|
|
560
|
+
INPUT:
|
|
561
|
+
|
|
562
|
+
- ``normalized`` -- boolean (default: ``False``); specifies whether
|
|
563
|
+
the normal vector should be normalized
|
|
564
|
+
|
|
565
|
+
OUTPUT: normal vector field
|
|
566
|
+
|
|
567
|
+
EXAMPLES::
|
|
568
|
+
|
|
569
|
+
sage: u, v = var('u, v', domain='real')
|
|
570
|
+
sage: eparaboloid = ParametrizedSurface3D((u, v, u^2 + v^2), (u, v),
|
|
571
|
+
....: 'elliptic paraboloid')
|
|
572
|
+
sage: eparaboloid.normal_vector(normalized=False)
|
|
573
|
+
(-2*u, -2*v, 1)
|
|
574
|
+
sage: eparaboloid.normal_vector(normalized=True)
|
|
575
|
+
(-2*u/sqrt(4*u^2 + 4*v^2 + 1),
|
|
576
|
+
-2*v/sqrt(4*u^2 + 4*v^2 + 1),
|
|
577
|
+
1/sqrt(4*u^2 + 4*v^2 + 1))
|
|
578
|
+
"""
|
|
579
|
+
|
|
580
|
+
dr = self.natural_frame()
|
|
581
|
+
normal = dr[1].cross_product(dr[2])
|
|
582
|
+
|
|
583
|
+
if normalized:
|
|
584
|
+
normal /= normal.norm()
|
|
585
|
+
return _simplify_full_rad(normal)
|
|
586
|
+
|
|
587
|
+
@cached_method
|
|
588
|
+
def _compute_first_fundamental_form_coefficient(self, index):
|
|
589
|
+
"""
|
|
590
|
+
Helper function to compute coefficients of the first fundamental form.
|
|
591
|
+
|
|
592
|
+
Do not call this method directly; instead use
|
|
593
|
+
``first_fundamental_form_coefficient``.
|
|
594
|
+
This method is cached, and expects its argument to be a list.
|
|
595
|
+
|
|
596
|
+
EXAMPLES::
|
|
597
|
+
|
|
598
|
+
sage: u, v = var('u, v', domain='real')
|
|
599
|
+
sage: eparaboloid = ParametrizedSurface3D((u, v, u^2+v^2), (u, v))
|
|
600
|
+
sage: eparaboloid._compute_first_fundamental_form_coefficient((1,2))
|
|
601
|
+
4*u*v
|
|
602
|
+
"""
|
|
603
|
+
dr = self.natural_frame()
|
|
604
|
+
return _simplify_full_rad(dr[index[0]]*dr[index[1]])
|
|
605
|
+
|
|
606
|
+
def first_fundamental_form_coefficient(self, index):
|
|
607
|
+
r"""
|
|
608
|
+
Compute a single component `g_{ij}` of the first fundamental form. If
|
|
609
|
+
the parametric representation of the surface is given by the vector
|
|
610
|
+
function `\vec r(u^i)`, where `u^i`, `i = 1, 2` are curvilinear
|
|
611
|
+
coordinates, then `g_{ij} = \frac{\partial \vec r}{\partial u^i} \cdot \frac{\partial \vec r}{\partial u^j}`.
|
|
612
|
+
|
|
613
|
+
INPUT:
|
|
614
|
+
|
|
615
|
+
- ``index`` -- tuple ``(i, j)`` specifying the index of the component `g_{ij}`
|
|
616
|
+
|
|
617
|
+
OUTPUT: component `g_{ij}` of the first fundamental form
|
|
618
|
+
|
|
619
|
+
EXAMPLES::
|
|
620
|
+
|
|
621
|
+
sage: u, v = var('u, v', domain='real')
|
|
622
|
+
sage: eparaboloid = ParametrizedSurface3D((u, v, u^2+v^2), (u, v))
|
|
623
|
+
sage: eparaboloid.first_fundamental_form_coefficient((1,2))
|
|
624
|
+
4*u*v
|
|
625
|
+
|
|
626
|
+
When the index is invalid, an error is raised::
|
|
627
|
+
|
|
628
|
+
sage: u, v = var('u, v', domain='real')
|
|
629
|
+
sage: eparaboloid = ParametrizedSurface3D((u, v, u^2+v^2), (u, v))
|
|
630
|
+
sage: eparaboloid.first_fundamental_form_coefficient((1,5))
|
|
631
|
+
Traceback (most recent call last):
|
|
632
|
+
...
|
|
633
|
+
ValueError: Index (1, 5) out of bounds.
|
|
634
|
+
"""
|
|
635
|
+
index = tuple(sorted(index))
|
|
636
|
+
if len(index) == 2 and all(i == 1 or i == 2 for i in index):
|
|
637
|
+
return self._compute_first_fundamental_form_coefficient(index)
|
|
638
|
+
else:
|
|
639
|
+
raise ValueError("Index %s out of bounds." % str(index))
|
|
640
|
+
|
|
641
|
+
def first_fundamental_form_coefficients(self):
|
|
642
|
+
r"""
|
|
643
|
+
Return the coefficients of the first fundamental form as a dictionary.
|
|
644
|
+
The keys are tuples `(i, j)`, where `i` and `j` range over `1, 2`,
|
|
645
|
+
while the values are the corresponding coefficients `g_{ij}`.
|
|
646
|
+
|
|
647
|
+
OUTPUT: dictionary of first fundamental form coefficients
|
|
648
|
+
|
|
649
|
+
EXAMPLES::
|
|
650
|
+
|
|
651
|
+
sage: u, v = var('u,v', domain='real')
|
|
652
|
+
sage: sphere = ParametrizedSurface3D((cos(u)*cos(v), sin(u)*cos(v), sin(v)), (u, v), 'sphere')
|
|
653
|
+
sage: sphere.first_fundamental_form_coefficients()
|
|
654
|
+
{(1, 1): cos(v)^2, (1, 2): 0, (2, 1): 0, (2, 2): 1}
|
|
655
|
+
"""
|
|
656
|
+
coefficients = {}
|
|
657
|
+
for index in product((1, 2), repeat=2):
|
|
658
|
+
coefficients[index] = \
|
|
659
|
+
self._compute_first_fundamental_form_coefficient(index)
|
|
660
|
+
return coefficients
|
|
661
|
+
|
|
662
|
+
def first_fundamental_form(self, vector1, vector2):
|
|
663
|
+
r"""
|
|
664
|
+
Evaluate the first fundamental form on two vectors expressed with
|
|
665
|
+
respect to the natural coordinate frame on the surface. In other words,
|
|
666
|
+
if the vectors are `v = (v^1, v^2)` and `w = (w^1, w^2)`, calculate
|
|
667
|
+
`g_{11} v^1 w^1 + g_{12}(v^1 w^2 + v^2 w^1) + g_{22} v^2 w^2`, with
|
|
668
|
+
`g_{ij}` the coefficients of the first fundamental form.
|
|
669
|
+
|
|
670
|
+
INPUT:
|
|
671
|
+
|
|
672
|
+
- ``vector1``, ``vector2`` -- vectors on the surface
|
|
673
|
+
|
|
674
|
+
OUTPUT: first fundamental form evaluated on the input vectors
|
|
675
|
+
|
|
676
|
+
EXAMPLES::
|
|
677
|
+
|
|
678
|
+
sage: u, v = var('u, v', domain='real')
|
|
679
|
+
sage: v1, v2, w1, w2 = var('v1, v2, w1, w2', domain='real')
|
|
680
|
+
sage: sphere = ParametrizedSurface3D((cos(u)*cos(v), sin(u)*cos(v), sin(v)), (u, v),'sphere')
|
|
681
|
+
sage: sphere.first_fundamental_form(vector([v1,v2]),vector([w1,w2]))
|
|
682
|
+
v1*w1*cos(v)^2 + v2*w2
|
|
683
|
+
|
|
684
|
+
sage: vv = vector([1,2])
|
|
685
|
+
sage: sphere.first_fundamental_form(vv,vv)
|
|
686
|
+
cos(v)^2 + 4
|
|
687
|
+
|
|
688
|
+
sage: sphere.first_fundamental_form([1,1],[2,1])
|
|
689
|
+
2*cos(v)^2 + 1
|
|
690
|
+
"""
|
|
691
|
+
gamma = self.first_fundamental_form_coefficients()
|
|
692
|
+
return sum(gamma[(i,j)] * vector1[i - 1] * vector2[j - 1]
|
|
693
|
+
for i, j in product((1, 2), repeat=2))
|
|
694
|
+
|
|
695
|
+
def area_form_squared(self):
|
|
696
|
+
"""
|
|
697
|
+
Return the square of the coefficient of the area form on the surface.
|
|
698
|
+
In terms of the coefficients `g_{ij}` (where `i, j = 1, 2`) of the
|
|
699
|
+
first fundamental form, this invariant is given by
|
|
700
|
+
`A^2 = g_{11}g_{22} - g_{12}^2`.
|
|
701
|
+
|
|
702
|
+
See also :meth:`.area_form`.
|
|
703
|
+
|
|
704
|
+
OUTPUT: square of the area form
|
|
705
|
+
|
|
706
|
+
EXAMPLES::
|
|
707
|
+
|
|
708
|
+
sage: u, v = var('u, v', domain='real')
|
|
709
|
+
sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
|
|
710
|
+
sage: sphere.area_form_squared()
|
|
711
|
+
cos(v)^2
|
|
712
|
+
"""
|
|
713
|
+
gamma = self.first_fundamental_form_coefficients()
|
|
714
|
+
sq = gamma[(1,1)] * gamma[(2,2)] - gamma[(1,2)]**2
|
|
715
|
+
return _simplify_full_rad(sq)
|
|
716
|
+
|
|
717
|
+
def area_form(self):
|
|
718
|
+
r"""
|
|
719
|
+
Return the coefficient of the area form on the surface. In terms of
|
|
720
|
+
the coefficients `g_{ij}` (where `i, j = 1, 2`) of the first
|
|
721
|
+
fundamental form, the coefficient of the area form is given by
|
|
722
|
+
`A = \sqrt{g_{11}g_{22} - g_{12}^2}`.
|
|
723
|
+
|
|
724
|
+
See also :meth:`.area_form_squared`.
|
|
725
|
+
|
|
726
|
+
OUTPUT: coefficient of the area form
|
|
727
|
+
|
|
728
|
+
EXAMPLES::
|
|
729
|
+
|
|
730
|
+
sage: u, v = var('u,v', domain='real')
|
|
731
|
+
sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
|
|
732
|
+
sage: sphere.area_form()
|
|
733
|
+
cos(v)
|
|
734
|
+
"""
|
|
735
|
+
f = abs(sqrt(self.area_form_squared()))
|
|
736
|
+
return _simplify_full_rad(f)
|
|
737
|
+
|
|
738
|
+
def first_fundamental_form_inverse_coefficients(self):
|
|
739
|
+
r"""
|
|
740
|
+
Return the coefficients `g^{ij}` of the inverse of the fundamental
|
|
741
|
+
form, as a dictionary. The inverse coefficients are defined by
|
|
742
|
+
`g^{ij} g_{jk} = \delta^i_k` with `\delta^i_k` the Kronecker
|
|
743
|
+
delta.
|
|
744
|
+
|
|
745
|
+
OUTPUT: dictionary of the inverse coefficients
|
|
746
|
+
|
|
747
|
+
EXAMPLES::
|
|
748
|
+
|
|
749
|
+
sage: u, v = var('u, v', domain='real')
|
|
750
|
+
sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
|
|
751
|
+
sage: sphere.first_fundamental_form_inverse_coefficients()
|
|
752
|
+
{(1, 1): cos(v)^(-2), (1, 2): 0, (2, 1): 0, (2, 2): 1}
|
|
753
|
+
"""
|
|
754
|
+
|
|
755
|
+
g = self.first_fundamental_form_coefficients()
|
|
756
|
+
D = g[(1,1)] * g[(2,2)] - g[(1,2)]**2
|
|
757
|
+
|
|
758
|
+
gi11 = _simplify_full_rad(g[(2,2)]/D)
|
|
759
|
+
gi12 = _simplify_full_rad(-g[(1,2)]/D)
|
|
760
|
+
gi21 = gi12
|
|
761
|
+
gi22 = _simplify_full_rad(g[(1,1)]/D)
|
|
762
|
+
|
|
763
|
+
return {(1,1): gi11, (1,2): gi12, (2,1): gi21, (2,2): gi22}
|
|
764
|
+
|
|
765
|
+
def first_fundamental_form_inverse_coefficient(self, index):
|
|
766
|
+
r"""
|
|
767
|
+
Return a specific component `g^{ij}` of the inverse of the fundamental
|
|
768
|
+
form.
|
|
769
|
+
|
|
770
|
+
INPUT:
|
|
771
|
+
|
|
772
|
+
- ``index`` -- tuple ``(i, j)`` specifying the index of the component `g^{ij}`
|
|
773
|
+
|
|
774
|
+
OUTPUT: component of the inverse of the fundamental form
|
|
775
|
+
|
|
776
|
+
EXAMPLES::
|
|
777
|
+
|
|
778
|
+
sage: u, v = var('u, v', domain='real')
|
|
779
|
+
sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
|
|
780
|
+
sage: sphere.first_fundamental_form_inverse_coefficient((1, 2))
|
|
781
|
+
0
|
|
782
|
+
sage: sphere.first_fundamental_form_inverse_coefficient((1, 1))
|
|
783
|
+
cos(v)^(-2)
|
|
784
|
+
"""
|
|
785
|
+
|
|
786
|
+
index = tuple(sorted(index))
|
|
787
|
+
if len(index) == 2 and all(i == 1 or i == 2 for i in index):
|
|
788
|
+
return self.first_fundamental_form_inverse_coefficients()[index]
|
|
789
|
+
else:
|
|
790
|
+
raise ValueError("Index %s out of bounds." % str(index))
|
|
791
|
+
|
|
792
|
+
@cached_method
|
|
793
|
+
def rotation(self, theta):
|
|
794
|
+
r"""
|
|
795
|
+
Give the matrix of the rotation operator over a given angle `\theta`
|
|
796
|
+
with respect to the natural frame.
|
|
797
|
+
|
|
798
|
+
INPUT:
|
|
799
|
+
|
|
800
|
+
- ``theta`` -- rotation angle
|
|
801
|
+
|
|
802
|
+
OUTPUT: rotation matrix with respect to the natural frame
|
|
803
|
+
|
|
804
|
+
ALGORITHM:
|
|
805
|
+
|
|
806
|
+
The operator of rotation over `\pi/2` is `J^i_j = g^{ik}\omega_{jk}`,
|
|
807
|
+
where `\omega` is the area form. The operator of rotation over an
|
|
808
|
+
angle `\theta` is `\cos(\theta) I + \sin(\theta) J`.
|
|
809
|
+
|
|
810
|
+
EXAMPLES::
|
|
811
|
+
|
|
812
|
+
sage: u, v = var('u, v', domain='real')
|
|
813
|
+
sage: assume(cos(v)>0)
|
|
814
|
+
sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
|
|
815
|
+
|
|
816
|
+
We first compute the matrix of rotation over `\pi/3`::
|
|
817
|
+
|
|
818
|
+
sage: rotation = sphere.rotation(pi/3); rotation
|
|
819
|
+
[ 1/2 -1/2*sqrt(3)/cos(v)]
|
|
820
|
+
[ 1/2*sqrt(3)*cos(v) 1/2]
|
|
821
|
+
|
|
822
|
+
We verify that three successive rotations over `\pi/3` yield minus the identity::
|
|
823
|
+
|
|
824
|
+
sage: rotation^3
|
|
825
|
+
[-1 0]
|
|
826
|
+
[ 0 -1]
|
|
827
|
+
"""
|
|
828
|
+
|
|
829
|
+
from sage.functions.trig import sin, cos
|
|
830
|
+
|
|
831
|
+
gi = self.first_fundamental_form_inverse_coefficients()
|
|
832
|
+
w12 = self.area_form()
|
|
833
|
+
R11 = (cos(theta) + sin(theta)*gi[1,2]*w12).simplify_full()
|
|
834
|
+
R12 = (- sin(theta)*gi[1,1]*w12).simplify_full()
|
|
835
|
+
R21 = (sin(theta)*gi[2,2]*w12).simplify_full()
|
|
836
|
+
R22 = (cos(theta) - sin(theta)*gi[2,1]*w12).simplify_full()
|
|
837
|
+
return matrix([[R11,R12],[R21,R22]])
|
|
838
|
+
|
|
839
|
+
@cached_method
|
|
840
|
+
def orthonormal_frame(self, coordinates='ext'):
|
|
841
|
+
r"""
|
|
842
|
+
Return the orthonormal frame field on the surface, expressed either
|
|
843
|
+
in exterior coordinates (i.e. expressed as vector fields in the
|
|
844
|
+
ambient space `\mathbb{R}^3`, the default) or interior coordinates
|
|
845
|
+
(with respect to the natural frame)
|
|
846
|
+
|
|
847
|
+
INPUT:
|
|
848
|
+
|
|
849
|
+
- ``coordinates`` -- either ``ext`` (default) or ``int``
|
|
850
|
+
|
|
851
|
+
OUTPUT: orthogonal frame field as a dictionary
|
|
852
|
+
|
|
853
|
+
ALGORITHM:
|
|
854
|
+
|
|
855
|
+
We normalize the first vector `\vec e_1` of the natural frame and then
|
|
856
|
+
get the second frame vector as `\vec e_2 = [\vec n, \vec e_1]`, where
|
|
857
|
+
`\vec n` is the unit normal to the surface.
|
|
858
|
+
|
|
859
|
+
EXAMPLES::
|
|
860
|
+
|
|
861
|
+
sage: u, v = var('u,v', domain='real')
|
|
862
|
+
sage: assume(cos(v)>0)
|
|
863
|
+
sage: sphere = ParametrizedSurface3D([cos(u)*cos(v), sin(u)*cos(v), sin(v)], [u, v],'sphere')
|
|
864
|
+
sage: frame = sphere.orthonormal_frame(); frame
|
|
865
|
+
{1: (-sin(u), cos(u), 0), 2: (-cos(u)*sin(v), -sin(u)*sin(v), cos(v))}
|
|
866
|
+
sage: (frame[1]*frame[1]).simplify_full()
|
|
867
|
+
1
|
|
868
|
+
sage: (frame[1]*frame[2]).simplify_full()
|
|
869
|
+
0
|
|
870
|
+
sage: frame[1] == sphere.orthonormal_frame_vector(1)
|
|
871
|
+
True
|
|
872
|
+
|
|
873
|
+
We compute the orthonormal frame with respect to the natural frame on
|
|
874
|
+
the surface::
|
|
875
|
+
|
|
876
|
+
sage: frame_int = sphere.orthonormal_frame(coordinates='int'); frame_int
|
|
877
|
+
{1: (1/cos(v), 0), 2: (0, 1)}
|
|
878
|
+
sage: sphere.first_fundamental_form(frame_int[1], frame_int[1])
|
|
879
|
+
1
|
|
880
|
+
sage: sphere.first_fundamental_form(frame_int[1], frame_int[2])
|
|
881
|
+
0
|
|
882
|
+
sage: sphere.first_fundamental_form(frame_int[2], frame_int[2])
|
|
883
|
+
1
|
|
884
|
+
"""
|
|
885
|
+
from sage.symbolic.constants import pi
|
|
886
|
+
|
|
887
|
+
if coordinates not in ['ext', 'int']:
|
|
888
|
+
raise ValueError("Coordinate system must be exterior ('ext') "
|
|
889
|
+
"or interior ('int').")
|
|
890
|
+
|
|
891
|
+
c = self.first_fundamental_form_coefficient([1, 1])
|
|
892
|
+
if coordinates == 'ext':
|
|
893
|
+
f1 = self.natural_frame()[1]
|
|
894
|
+
|
|
895
|
+
E1 = _simplify_full_rad(f1 / sqrt(c))
|
|
896
|
+
E2 = _simplify_full_rad(
|
|
897
|
+
self.normal_vector(normalized=True).cross_product(E1))
|
|
898
|
+
else:
|
|
899
|
+
E1 = vector([_simplify_full_rad(1 / sqrt(c)), 0])
|
|
900
|
+
E2 = (self.rotation(pi / 2) * E1).simplify_full()
|
|
901
|
+
return {1: E1, 2: E2}
|
|
902
|
+
|
|
903
|
+
def orthonormal_frame_vector(self, index, coordinates='ext'):
|
|
904
|
+
r"""
|
|
905
|
+
Return a specific basis vector field of the orthonormal frame field on
|
|
906
|
+
the surface, expressed in exterior or interior coordinates. See
|
|
907
|
+
:meth:`orthogonal_frame` for more details.
|
|
908
|
+
|
|
909
|
+
INPUT:
|
|
910
|
+
|
|
911
|
+
- ``index`` -- index of the basis vector
|
|
912
|
+
- ``coordinates`` -- either ``ext`` (default) or ``int``
|
|
913
|
+
|
|
914
|
+
OUTPUT: orthonormal frame vector field
|
|
915
|
+
|
|
916
|
+
EXAMPLES::
|
|
917
|
+
|
|
918
|
+
sage: u, v = var('u, v', domain='real')
|
|
919
|
+
sage: assume(cos(v)>0)
|
|
920
|
+
sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
|
|
921
|
+
sage: V1 = sphere.orthonormal_frame_vector(1); V1
|
|
922
|
+
(-sin(u), cos(u), 0)
|
|
923
|
+
sage: V2 = sphere.orthonormal_frame_vector(2); V2
|
|
924
|
+
(-cos(u)*sin(v), -sin(u)*sin(v), cos(v))
|
|
925
|
+
sage: (V1*V1).simplify_full()
|
|
926
|
+
1
|
|
927
|
+
sage: (V1*V2).simplify_full()
|
|
928
|
+
0
|
|
929
|
+
|
|
930
|
+
sage: n = sphere.normal_vector(normalized=True)
|
|
931
|
+
sage: (V1.cross_product(V2) - n).simplify_full()
|
|
932
|
+
(0, 0, 0)
|
|
933
|
+
"""
|
|
934
|
+
|
|
935
|
+
return self.orthonormal_frame(coordinates)[index]
|
|
936
|
+
|
|
937
|
+
def lie_bracket(self, v, w):
|
|
938
|
+
r"""
|
|
939
|
+
Return the Lie bracket of two vector fields that are tangent
|
|
940
|
+
to the surface. The vector fields should be given in intrinsic
|
|
941
|
+
coordinates, i.e. with respect to the natural frame.
|
|
942
|
+
|
|
943
|
+
INPUT:
|
|
944
|
+
|
|
945
|
+
- ``v``, ``w`` -- vector fields on the surface, expressed
|
|
946
|
+
as pairs of functions or as vectors of length 2
|
|
947
|
+
|
|
948
|
+
OUTPUT: the Lie bracket `[v, w]`
|
|
949
|
+
|
|
950
|
+
EXAMPLES::
|
|
951
|
+
|
|
952
|
+
sage: u, v = var('u, v', domain='real')
|
|
953
|
+
sage: assume(cos(v)>0)
|
|
954
|
+
sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
|
|
955
|
+
sage: sphere.lie_bracket([u,v],[-v,u])
|
|
956
|
+
(0, 0)
|
|
957
|
+
|
|
958
|
+
sage: EE_int = sphere.orthonormal_frame(coordinates='int')
|
|
959
|
+
sage: sphere.lie_bracket(EE_int[1],EE_int[2])
|
|
960
|
+
(sin(v)/cos(v)^2, 0)
|
|
961
|
+
"""
|
|
962
|
+
v = vector(SR, v)
|
|
963
|
+
w = vector(SR, w)
|
|
964
|
+
|
|
965
|
+
variables = self.variables_list
|
|
966
|
+
Dv = matrix([[_simplify_full_rad(diff(component, u))
|
|
967
|
+
for u in variables] for component in v])
|
|
968
|
+
Dw = matrix([[_simplify_full_rad(diff(component, u))
|
|
969
|
+
for u in variables] for component in w])
|
|
970
|
+
return vector(Dv*w - Dw*v).simplify_full()
|
|
971
|
+
|
|
972
|
+
def frame_structure_functions(self, e1, e2):
|
|
973
|
+
r"""
|
|
974
|
+
Return the structure functions `c^k_{ij}` for a frame field
|
|
975
|
+
`e_1, e_2`, i.e. a pair of vector fields on the surface which are
|
|
976
|
+
linearly independent at each point. The structure functions are
|
|
977
|
+
defined using the Lie bracket by `[e_i,e_j] = c^k_{ij}e_k`.
|
|
978
|
+
|
|
979
|
+
INPUT:
|
|
980
|
+
|
|
981
|
+
- ``e1``, ``e2`` -- vector fields in intrinsic coordinates on
|
|
982
|
+
the surface, expressed as pairs of functions, or as vectors of
|
|
983
|
+
length 2
|
|
984
|
+
|
|
985
|
+
OUTPUT:
|
|
986
|
+
|
|
987
|
+
Dictionary of structure functions, where the key ``(i, j, k)`` refers to
|
|
988
|
+
the structure function `c_{i,j}^k`.
|
|
989
|
+
|
|
990
|
+
EXAMPLES::
|
|
991
|
+
|
|
992
|
+
sage: u, v = var('u, v', domain='real')
|
|
993
|
+
sage: assume(cos(v) > 0)
|
|
994
|
+
sage: sphere = ParametrizedSurface3D([cos(u)*cos(v), sin(u)*cos(v), sin(v)], [u, v], 'sphere')
|
|
995
|
+
sage: sphere.frame_structure_functions([u, v], [-v, u])
|
|
996
|
+
{(1, 1, 1): 0,
|
|
997
|
+
(1, 1, 2): 0,
|
|
998
|
+
(1, 2, 1): 0,
|
|
999
|
+
(1, 2, 2): 0,
|
|
1000
|
+
(2, 1, 1): 0,
|
|
1001
|
+
(2, 1, 2): 0,
|
|
1002
|
+
(2, 2, 1): 0,
|
|
1003
|
+
(2, 2, 2): 0}
|
|
1004
|
+
|
|
1005
|
+
We construct the structure functions of the orthonormal frame on the
|
|
1006
|
+
surface::
|
|
1007
|
+
|
|
1008
|
+
sage: EE_int = sphere.orthonormal_frame(coordinates='int')
|
|
1009
|
+
sage: CC = sphere.frame_structure_functions(EE_int[1],EE_int[2]); CC
|
|
1010
|
+
{(1, 1, 1): 0,
|
|
1011
|
+
(1, 1, 2): 0,
|
|
1012
|
+
(1, 2, 1): sin(v)/cos(v),
|
|
1013
|
+
(1, 2, 2): 0,
|
|
1014
|
+
(2, 1, 1): -sin(v)/cos(v),
|
|
1015
|
+
(2, 1, 2): 0,
|
|
1016
|
+
(2, 2, 1): 0,
|
|
1017
|
+
(2, 2, 2): 0}
|
|
1018
|
+
sage: sphere.lie_bracket(EE_int[1],EE_int[2]) - CC[(1,2,1)]*EE_int[1] - CC[(1,2,2)]*EE_int[2]
|
|
1019
|
+
(0, 0)
|
|
1020
|
+
"""
|
|
1021
|
+
e1 = vector(SR, e1)
|
|
1022
|
+
e2 = vector(SR, e2)
|
|
1023
|
+
|
|
1024
|
+
lie_bracket = self.lie_bracket(e1, e2).simplify_full()
|
|
1025
|
+
transformation = matrix(SR, [e1, e2]).transpose()
|
|
1026
|
+
|
|
1027
|
+
w = (transformation.inverse()*lie_bracket).simplify_full()
|
|
1028
|
+
|
|
1029
|
+
return {(1,1,1): 0, (1,1,2): 0, (1,2,1): w[0], (1,2,2): w[1],
|
|
1030
|
+
(2,1,1): -w[0], (2,1,2): -w[1], (2,2,1): 0, (2,2,2): 0}
|
|
1031
|
+
|
|
1032
|
+
@cached_method
|
|
1033
|
+
def _compute_second_order_frame_element(self, index):
|
|
1034
|
+
"""
|
|
1035
|
+
Compute an element of the second order frame of the surface. See
|
|
1036
|
+
:meth:`second_order_natural_frame` for more details.
|
|
1037
|
+
|
|
1038
|
+
This method expects its arguments in tuple form for caching.
|
|
1039
|
+
As it does no input checking, it should not be called directly.
|
|
1040
|
+
|
|
1041
|
+
EXAMPLES::
|
|
1042
|
+
|
|
1043
|
+
sage: u, v = var('u, v', domain='real')
|
|
1044
|
+
sage: paraboloid = ParametrizedSurface3D([u, v, u^2 + v^2], [u,v], 'paraboloid')
|
|
1045
|
+
sage: paraboloid._compute_second_order_frame_element((1, 2))
|
|
1046
|
+
(0, 0, 0)
|
|
1047
|
+
sage: paraboloid._compute_second_order_frame_element((2, 2))
|
|
1048
|
+
(0, 0, 2)
|
|
1049
|
+
"""
|
|
1050
|
+
variables = [self.variables[i] for i in index]
|
|
1051
|
+
ddr_element = vector([_simplify_full_rad(diff(f, variables))
|
|
1052
|
+
for f in self.equation])
|
|
1053
|
+
|
|
1054
|
+
return ddr_element
|
|
1055
|
+
|
|
1056
|
+
def second_order_natural_frame(self):
|
|
1057
|
+
r"""
|
|
1058
|
+
Return the second-order frame of the surface, i.e. computes the
|
|
1059
|
+
second-order derivatives (with respect to the parameters on the
|
|
1060
|
+
surface) of the parametric expression `\vec r = \vec r(u^1,u^2)`
|
|
1061
|
+
of the surface.
|
|
1062
|
+
|
|
1063
|
+
OUTPUT:
|
|
1064
|
+
|
|
1065
|
+
- Dictionary where the keys are 2-tuples ``(i, j)`` and the values are the corresponding derivatives `r_{ij}`.
|
|
1066
|
+
|
|
1067
|
+
EXAMPLES:
|
|
1068
|
+
|
|
1069
|
+
We compute the second-order natural frame of the sphere::
|
|
1070
|
+
|
|
1071
|
+
sage: u, v = var('u, v', domain='real')
|
|
1072
|
+
sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
|
|
1073
|
+
sage: sphere.second_order_natural_frame()
|
|
1074
|
+
{(1, 1): (-cos(u)*cos(v), -cos(v)*sin(u), 0),
|
|
1075
|
+
(1, 2): (sin(u)*sin(v), -cos(u)*sin(v), 0),
|
|
1076
|
+
(2, 1): (sin(u)*sin(v), -cos(u)*sin(v), 0),
|
|
1077
|
+
(2, 2): (-cos(u)*cos(v), -cos(v)*sin(u), -sin(v))}
|
|
1078
|
+
"""
|
|
1079
|
+
|
|
1080
|
+
vectors = {}
|
|
1081
|
+
for index in product((1, 2), repeat=2):
|
|
1082
|
+
sorted_index = tuple(sorted(index))
|
|
1083
|
+
vectors[index] = \
|
|
1084
|
+
self._compute_second_order_frame_element(sorted_index)
|
|
1085
|
+
return vectors
|
|
1086
|
+
|
|
1087
|
+
def second_order_natural_frame_element(self, index):
|
|
1088
|
+
r"""
|
|
1089
|
+
Return a vector in the second-order frame of the surface, i.e.
|
|
1090
|
+
computes the second-order derivatives of the parametric expression
|
|
1091
|
+
`\vec{r}` of the surface with respect to the parameters listed in the
|
|
1092
|
+
argument.
|
|
1093
|
+
|
|
1094
|
+
INPUT:
|
|
1095
|
+
|
|
1096
|
+
- ``index`` -- a 2-tuple ``(i, j)`` specifying the element of the second-order frame
|
|
1097
|
+
|
|
1098
|
+
OUTPUT: the second-order derivative `r_{ij}`
|
|
1099
|
+
|
|
1100
|
+
EXAMPLES::
|
|
1101
|
+
|
|
1102
|
+
sage: u, v = var('u, v', domain='real')
|
|
1103
|
+
sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
|
|
1104
|
+
sage: sphere.second_order_natural_frame_element((1, 2))
|
|
1105
|
+
(sin(u)*sin(v), -cos(u)*sin(v), 0)
|
|
1106
|
+
"""
|
|
1107
|
+
|
|
1108
|
+
index = tuple(sorted(index))
|
|
1109
|
+
if len(index) == 2 and all(i == 1 or i == 2 for i in index):
|
|
1110
|
+
return self._compute_second_order_frame_element(index)
|
|
1111
|
+
else:
|
|
1112
|
+
raise ValueError("Index %s out of bounds." % str(index))
|
|
1113
|
+
|
|
1114
|
+
@cached_method
|
|
1115
|
+
def _compute_second_fundamental_form_coefficient(self, index):
|
|
1116
|
+
"""
|
|
1117
|
+
Compute a coefficient of the second fundamental form of the surface.
|
|
1118
|
+
See ``second_fundamental_form_coefficient`` for more details.
|
|
1119
|
+
|
|
1120
|
+
This method expects its arguments in tuple form for caching. As it
|
|
1121
|
+
does no input checking, it should not be called directly.
|
|
1122
|
+
|
|
1123
|
+
EXAMPLES::
|
|
1124
|
+
|
|
1125
|
+
sage: u, v = var('u,v', domain='real')
|
|
1126
|
+
sage: paraboloid = ParametrizedSurface3D([u, v, u^2+v^2], [u, v], 'paraboloid')
|
|
1127
|
+
sage: paraboloid._compute_second_fundamental_form_coefficient((1,1))
|
|
1128
|
+
2/sqrt(4*u^2 + 4*v^2 + 1)
|
|
1129
|
+
"""
|
|
1130
|
+
N = self.normal_vector(normalized=True)
|
|
1131
|
+
v = self.second_order_natural_frame_element(index)
|
|
1132
|
+
return _simplify_full_rad(v*N)
|
|
1133
|
+
|
|
1134
|
+
def second_fundamental_form_coefficient(self, index):
|
|
1135
|
+
r"""
|
|
1136
|
+
Return the coefficient `h_{ij}` of the second fundamental form
|
|
1137
|
+
corresponding to the index `(i, j)`. If the equation of the surface
|
|
1138
|
+
is `\vec{r}(u^1, u^2)`, then `h_{ij} = \vec{r}_{u^i u^j} \cdot \vec{n}`,
|
|
1139
|
+
where `\vec{n}` is the unit normal.
|
|
1140
|
+
|
|
1141
|
+
INPUT:
|
|
1142
|
+
|
|
1143
|
+
- ``index`` -- a 2-tuple ``(i, j)``
|
|
1144
|
+
|
|
1145
|
+
OUTPUT: component `h_{ij}` of the second fundamental form
|
|
1146
|
+
|
|
1147
|
+
EXAMPLES::
|
|
1148
|
+
|
|
1149
|
+
sage: u, v = var('u,v', domain='real')
|
|
1150
|
+
sage: assume(cos(v)>0)
|
|
1151
|
+
sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
|
|
1152
|
+
sage: sphere.second_fundamental_form_coefficient((1, 1))
|
|
1153
|
+
-cos(v)^2
|
|
1154
|
+
sage: sphere.second_fundamental_form_coefficient((2, 1))
|
|
1155
|
+
0
|
|
1156
|
+
"""
|
|
1157
|
+
index = tuple(index)
|
|
1158
|
+
if len(index) == 2 and all(i == 1 or i == 2 for i in index):
|
|
1159
|
+
return self._compute_second_fundamental_form_coefficient(index)
|
|
1160
|
+
else:
|
|
1161
|
+
raise ValueError("Index %s out of bounds." % str(index))
|
|
1162
|
+
|
|
1163
|
+
def second_fundamental_form_coefficients(self):
|
|
1164
|
+
"""
|
|
1165
|
+
Return the coefficients `h_{ij}` of the second fundamental form as
|
|
1166
|
+
a dictionary, where the keys are the indices `(i, j)` and the values
|
|
1167
|
+
are the corresponding components `h_{ij}`.
|
|
1168
|
+
|
|
1169
|
+
When only one component is needed, consider instead the function
|
|
1170
|
+
:meth:`second_fundamental_form_coefficient`.
|
|
1171
|
+
|
|
1172
|
+
OUTPUT: dictionary of second fundamental form coefficients
|
|
1173
|
+
|
|
1174
|
+
EXAMPLES::
|
|
1175
|
+
|
|
1176
|
+
sage: u, v = var('u, v', domain='real')
|
|
1177
|
+
sage: assume(cos(v)>0)
|
|
1178
|
+
sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
|
|
1179
|
+
sage: sphere.second_fundamental_form_coefficients()
|
|
1180
|
+
{(1, 1): -cos(v)^2, (1, 2): 0, (2, 1): 0, (2, 2): -1}
|
|
1181
|
+
"""
|
|
1182
|
+
|
|
1183
|
+
coefficients = {}
|
|
1184
|
+
for index in product((1, 2), repeat=2):
|
|
1185
|
+
coefficients[index] = \
|
|
1186
|
+
self._compute_second_fundamental_form_coefficient(index)
|
|
1187
|
+
return coefficients
|
|
1188
|
+
|
|
1189
|
+
def second_fundamental_form(self, vector1, vector2):
|
|
1190
|
+
r"""
|
|
1191
|
+
Evaluates the second fundamental form on two vectors on the surface.
|
|
1192
|
+
If the vectors are given by `v=(v^1,v^2)` and `w=(w^1,w^2)`, the
|
|
1193
|
+
result 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`.
|
|
1194
|
+
|
|
1195
|
+
INPUT:
|
|
1196
|
+
|
|
1197
|
+
- ``vector1``, ``vector2`` -- 2-tuples representing the input vectors
|
|
1198
|
+
|
|
1199
|
+
OUTPUT: value of the second fundamental form evaluated on the given vectors
|
|
1200
|
+
|
|
1201
|
+
EXAMPLES:
|
|
1202
|
+
|
|
1203
|
+
We evaluate the second fundamental form on two symbolic vectors::
|
|
1204
|
+
|
|
1205
|
+
sage: u, v = var('u, v', domain='real')
|
|
1206
|
+
sage: v1, v2, w1, w2 = var('v1, v2, w1, w2', domain='real')
|
|
1207
|
+
sage: assume(cos(v) > 0)
|
|
1208
|
+
sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
|
|
1209
|
+
sage: sphere.second_fundamental_form(vector([v1, v2]), vector([w1, w2]))
|
|
1210
|
+
-v1*w1*cos(v)^2 - v2*w2
|
|
1211
|
+
|
|
1212
|
+
We evaluate the second fundamental form on vectors with numerical
|
|
1213
|
+
components::
|
|
1214
|
+
|
|
1215
|
+
sage: vect = vector([1,2])
|
|
1216
|
+
sage: sphere.second_fundamental_form(vect, vect)
|
|
1217
|
+
-cos(v)^2 - 4
|
|
1218
|
+
sage: sphere.second_fundamental_form([1,1], [2,1])
|
|
1219
|
+
-2*cos(v)^2 - 1
|
|
1220
|
+
"""
|
|
1221
|
+
hh = self.second_fundamental_form_coefficients()
|
|
1222
|
+
return sum(hh[(i, j)] * vector1[i - 1] * vector2[j - 1]
|
|
1223
|
+
for (i, j) in product((1, 2), repeat=2))
|
|
1224
|
+
|
|
1225
|
+
def gauss_curvature(self):
|
|
1226
|
+
r"""
|
|
1227
|
+
Finds the gaussian curvature of the surface, given by
|
|
1228
|
+
`K = \frac{h_{11}h_{22} - h_{12}^2}{g_{11}g_{22} - g_{12}^2}`,
|
|
1229
|
+
where `g_{ij}` and `h_{ij}` are the coefficients of the first
|
|
1230
|
+
and second fundamental form, respectively.
|
|
1231
|
+
|
|
1232
|
+
OUTPUT: Gaussian curvature of the surface.
|
|
1233
|
+
|
|
1234
|
+
EXAMPLES::
|
|
1235
|
+
|
|
1236
|
+
sage: R = var('R')
|
|
1237
|
+
sage: assume(R>0)
|
|
1238
|
+
sage: u, v = var('u,v', domain='real')
|
|
1239
|
+
sage: assume(cos(v)>0)
|
|
1240
|
+
sage: sphere = ParametrizedSurface3D([R*cos(u)*cos(v),R*sin(u)*cos(v),R*sin(v)],[u,v],'sphere')
|
|
1241
|
+
sage: sphere.gauss_curvature()
|
|
1242
|
+
R^(-2)
|
|
1243
|
+
"""
|
|
1244
|
+
hh = self.second_fundamental_form_coefficients()
|
|
1245
|
+
return _simplify_full_rad(
|
|
1246
|
+
(hh[(1,1)] * hh[(2,2)] - hh[(1,2)]**2)/self.area_form_squared())
|
|
1247
|
+
|
|
1248
|
+
def mean_curvature(self):
|
|
1249
|
+
r"""
|
|
1250
|
+
Finds the mean curvature of the surface, given by
|
|
1251
|
+
`H = \frac{1}{2}\frac{g_{22}h_{11} - 2g_{12}h_{12} + g_{11}h_{22}}{g_{11}g_{22} - g_{12}^2}`,
|
|
1252
|
+
where `g_{ij}` and `h_{ij}` are the components of the first and second
|
|
1253
|
+
fundamental forms, respectively.
|
|
1254
|
+
|
|
1255
|
+
OUTPUT: mean curvature of the surface
|
|
1256
|
+
|
|
1257
|
+
EXAMPLES::
|
|
1258
|
+
|
|
1259
|
+
sage: R = var('R')
|
|
1260
|
+
sage: assume(R>0)
|
|
1261
|
+
sage: u, v = var('u,v', domain='real')
|
|
1262
|
+
sage: assume(cos(v)>0)
|
|
1263
|
+
sage: sphere = ParametrizedSurface3D([R*cos(u)*cos(v),R*sin(u)*cos(v),R*sin(v)],[u,v],'sphere')
|
|
1264
|
+
sage: sphere.mean_curvature()
|
|
1265
|
+
-1/R
|
|
1266
|
+
"""
|
|
1267
|
+
gg = self.first_fundamental_form_coefficients()
|
|
1268
|
+
hh = self.second_fundamental_form_coefficients()
|
|
1269
|
+
denom = 2*self.area_form_squared()
|
|
1270
|
+
numer = (gg[(2,2)]*hh[(1,1)] - 2*gg[(1,2)]*hh[(1,2)] +
|
|
1271
|
+
gg[(1,1)]*hh[(2,2)]).simplify_full()
|
|
1272
|
+
return _simplify_full_rad(numer/denom)
|
|
1273
|
+
|
|
1274
|
+
@cached_method
|
|
1275
|
+
def shape_operator_coefficients(self):
|
|
1276
|
+
r"""
|
|
1277
|
+
Return the components of the shape operator of the surface as a
|
|
1278
|
+
dictionary. See ``shape_operator`` for more information.
|
|
1279
|
+
|
|
1280
|
+
OUTPUT:
|
|
1281
|
+
|
|
1282
|
+
Dictionary where the keys are two-tuples ``(i, j)``, with values the
|
|
1283
|
+
corresponding component of the shape operator.
|
|
1284
|
+
|
|
1285
|
+
EXAMPLES::
|
|
1286
|
+
|
|
1287
|
+
sage: R = var('R')
|
|
1288
|
+
sage: u, v = var('u,v', domain='real')
|
|
1289
|
+
sage: assume(cos(v)>0)
|
|
1290
|
+
sage: sphere = ParametrizedSurface3D([R*cos(u)*cos(v),R*sin(u)*cos(v),R*sin(v)],[u,v],'sphere')
|
|
1291
|
+
sage: sphere.shape_operator_coefficients()
|
|
1292
|
+
{(1, 1): -1/R, (1, 2): 0, (2, 1): 0, (2, 2): -1/R}
|
|
1293
|
+
"""
|
|
1294
|
+
|
|
1295
|
+
gi = self.first_fundamental_form_inverse_coefficients()
|
|
1296
|
+
hh = self.second_fundamental_form_coefficients()
|
|
1297
|
+
|
|
1298
|
+
sh_op11 = _simplify_full_rad(gi[(1,1)]*hh[(1,1)] + gi[(1,2)]*hh[(1,2)])
|
|
1299
|
+
sh_op12 = _simplify_full_rad(gi[(1,1)]*hh[(2,1)] + gi[(1,2)]*hh[(2,2)])
|
|
1300
|
+
sh_op21 = _simplify_full_rad(gi[(2,1)]*hh[(1,1)] + gi[(2,2)]*hh[(1,2)])
|
|
1301
|
+
sh_op22 = _simplify_full_rad(gi[(2,1)]*hh[(2,1)] + gi[(2,2)]*hh[(2,2)])
|
|
1302
|
+
|
|
1303
|
+
return {(1,1): sh_op11, (1,2): sh_op12, (2,1): sh_op21, (2,2): sh_op22}
|
|
1304
|
+
|
|
1305
|
+
def shape_operator(self):
|
|
1306
|
+
r"""
|
|
1307
|
+
Return the shape operator of the surface as a matrix. The shape
|
|
1308
|
+
operator is defined as the derivative of the Gauss map, and is
|
|
1309
|
+
computed here in terms of the first and second fundamental form by
|
|
1310
|
+
means of the Weingarten equations.
|
|
1311
|
+
|
|
1312
|
+
OUTPUT: matrix of the shape operator
|
|
1313
|
+
|
|
1314
|
+
EXAMPLES::
|
|
1315
|
+
|
|
1316
|
+
sage: R = var('R')
|
|
1317
|
+
sage: assume(R>0)
|
|
1318
|
+
sage: u, v = var('u,v', domain='real')
|
|
1319
|
+
sage: assume(cos(v)>0)
|
|
1320
|
+
sage: sphere = ParametrizedSurface3D([R*cos(u)*cos(v),R*sin(u)*cos(v),R*sin(v)],[u,v],'sphere')
|
|
1321
|
+
sage: S = sphere.shape_operator(); S
|
|
1322
|
+
[-1/R 0]
|
|
1323
|
+
[ 0 -1/R]
|
|
1324
|
+
|
|
1325
|
+
The eigenvalues of the shape operator are the principal curvatures of
|
|
1326
|
+
the surface::
|
|
1327
|
+
|
|
1328
|
+
sage: u, v = var('u,v', domain='real')
|
|
1329
|
+
sage: paraboloid = ParametrizedSurface3D([u, v, u^2+v^2], [u, v], 'paraboloid')
|
|
1330
|
+
sage: S = paraboloid.shape_operator(); S
|
|
1331
|
+
[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)]
|
|
1332
|
+
[ -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)]
|
|
1333
|
+
sage: S.eigenvalues()
|
|
1334
|
+
[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)]
|
|
1335
|
+
"""
|
|
1336
|
+
|
|
1337
|
+
shop = self.shape_operator_coefficients()
|
|
1338
|
+
shop_matrix = matrix([[shop[(1,1)],shop[(1,2)]],
|
|
1339
|
+
[shop[(2,1)],shop[(2,2)]]])
|
|
1340
|
+
return shop_matrix
|
|
1341
|
+
|
|
1342
|
+
def principal_directions(self):
|
|
1343
|
+
r"""
|
|
1344
|
+
Finds the principal curvatures and principal directions of the surface.
|
|
1345
|
+
|
|
1346
|
+
OUTPUT:
|
|
1347
|
+
|
|
1348
|
+
For each principal curvature, returns a list of the form
|
|
1349
|
+
`(\rho, V, n)`, where `\rho` is the principal curvature,
|
|
1350
|
+
`V` is the corresponding principal direction, and `n` is
|
|
1351
|
+
the multiplicity.
|
|
1352
|
+
|
|
1353
|
+
EXAMPLES::
|
|
1354
|
+
|
|
1355
|
+
sage: u, v = var('u, v', domain='real')
|
|
1356
|
+
sage: R, r = var('R,r', domain='real')
|
|
1357
|
+
sage: assume(R>r,r>0)
|
|
1358
|
+
sage: torus = ParametrizedSurface3D([(R+r*cos(v))*cos(u),(R+r*cos(v))*sin(u),r*sin(v)],[u,v],'torus')
|
|
1359
|
+
sage: torus.principal_directions()
|
|
1360
|
+
[(-cos(v)/(r*cos(v) + R), [(1, 0)], 1), (-1/r, [(0, 1)], 1)]
|
|
1361
|
+
|
|
1362
|
+
::
|
|
1363
|
+
|
|
1364
|
+
sage: u, v = var('u, v', domain='real')
|
|
1365
|
+
sage: V = vector([u*cos(u+v), u*sin(u+v), u+v])
|
|
1366
|
+
sage: helicoid = ParametrizedSurface3D(V, (u, v))
|
|
1367
|
+
sage: helicoid.principal_directions()
|
|
1368
|
+
[(-1/(u^2 + 1), [(1, -(u^2 - sqrt(u^2 + 1) + 1)/(u^2 + 1))], 1),
|
|
1369
|
+
(1/(u^2 + 1), [(1, -(u^2 + sqrt(u^2 + 1) + 1)/(u^2 + 1))], 1)]
|
|
1370
|
+
"""
|
|
1371
|
+
return self.shape_operator().eigenvectors_right()
|
|
1372
|
+
|
|
1373
|
+
@cached_method
|
|
1374
|
+
def connection_coefficients(self):
|
|
1375
|
+
r"""
|
|
1376
|
+
Compute the connection coefficients or Christoffel symbols
|
|
1377
|
+
`\Gamma^k_{ij}` of the surface. If the coefficients of the first
|
|
1378
|
+
fundamental form are given by `g_{ij}` (where `i, j = 1, 2`), then
|
|
1379
|
+
`\Gamma^k_{ij} = \frac{1}{2} g^{kl} \left( \frac{\partial g_{li}}{\partial x^j}
|
|
1380
|
+
- \frac{\partial g_{ij}}{\partial x^l}
|
|
1381
|
+
+ \frac{\partial g_{lj}}{\partial x^i} \right)`.
|
|
1382
|
+
Here, `(g^{kl})` is the inverse of the matrix `(g_{ij})`, with
|
|
1383
|
+
`i, j = 1, 2`.
|
|
1384
|
+
|
|
1385
|
+
OUTPUT:
|
|
1386
|
+
|
|
1387
|
+
Dictionary of connection coefficients, where the keys are 3-tuples
|
|
1388
|
+
`(i,j,k)` and the values are the corresponding coefficients
|
|
1389
|
+
`\Gamma^k_{ij}`.
|
|
1390
|
+
|
|
1391
|
+
EXAMPLES::
|
|
1392
|
+
|
|
1393
|
+
sage: r = var('r')
|
|
1394
|
+
sage: assume(r > 0)
|
|
1395
|
+
sage: u, v = var('u,v', domain='real')
|
|
1396
|
+
sage: assume(cos(v)>0)
|
|
1397
|
+
sage: sphere = ParametrizedSurface3D([r*cos(u)*cos(v),r*sin(u)*cos(v),r*sin(v)],[u,v],'sphere')
|
|
1398
|
+
sage: sphere.connection_coefficients()
|
|
1399
|
+
{(1, 1, 1): 0,
|
|
1400
|
+
(1, 1, 2): cos(v)*sin(v),
|
|
1401
|
+
(1, 2, 1): -sin(v)/cos(v),
|
|
1402
|
+
(1, 2, 2): 0,
|
|
1403
|
+
(2, 1, 1): -sin(v)/cos(v),
|
|
1404
|
+
(2, 1, 2): 0,
|
|
1405
|
+
(2, 2, 1): 0,
|
|
1406
|
+
(2, 2, 2): 0}
|
|
1407
|
+
"""
|
|
1408
|
+
x = self.variables
|
|
1409
|
+
gg = self.first_fundamental_form_coefficients()
|
|
1410
|
+
gi = self.first_fundamental_form_inverse_coefficients()
|
|
1411
|
+
|
|
1412
|
+
dg = {}
|
|
1413
|
+
for i,j,k in product((1, 2), repeat=3):
|
|
1414
|
+
dg[(i,j,k)] = _simplify_full_rad(gg[(j,k)].differentiate(x[i]))
|
|
1415
|
+
|
|
1416
|
+
structfun = {}
|
|
1417
|
+
for i,j,k in product((1, 2), repeat=3):
|
|
1418
|
+
structfun[(i,j,k)] = sum(gi[(k,s)]*(dg[(i,j,s)] + dg[(j,i,s)]
|
|
1419
|
+
- dg[(s,i,j)])/2
|
|
1420
|
+
for s in (1,2))
|
|
1421
|
+
structfun[(i,j,k)] = _simplify_full_rad(structfun[(i,j,k)])
|
|
1422
|
+
return structfun
|
|
1423
|
+
|
|
1424
|
+
@cached_method
|
|
1425
|
+
def _create_geodesic_ode_system(self):
|
|
1426
|
+
r"""
|
|
1427
|
+
Helper method to create a fast floating-point version of the
|
|
1428
|
+
geodesic equations, used by :meth:`geodesics_numerical`.
|
|
1429
|
+
|
|
1430
|
+
EXAMPLES::
|
|
1431
|
+
|
|
1432
|
+
sage: p, q = var('p,q', domain='real')
|
|
1433
|
+
sage: sphere = ParametrizedSurface3D([cos(q)*cos(p),sin(q)*cos(p),sin(p)],[p,q],'sphere')
|
|
1434
|
+
sage: ode = sphere._create_geodesic_ode_system()
|
|
1435
|
+
sage: ode.function(0.0, (1.0, 0.0, 1.0, 1.0))
|
|
1436
|
+
[1.00000000000000, 1.00000000000000, -0.4546487134128409, 3.114815449309804]
|
|
1437
|
+
"""
|
|
1438
|
+
from sage.ext.fast_eval import fast_float
|
|
1439
|
+
from sage.calculus.ode import ode_solver
|
|
1440
|
+
|
|
1441
|
+
u1 = self.variables[1]
|
|
1442
|
+
u2 = self.variables[2]
|
|
1443
|
+
|
|
1444
|
+
C = self.connection_coefficients()
|
|
1445
|
+
|
|
1446
|
+
with SR.temp_var(domain='real') as v1:
|
|
1447
|
+
with SR.temp_var(domain='real') as v2:
|
|
1448
|
+
dv1 = - C[(1,1,1)]*v1**2 - 2*C[(1,2,1)]*v1*v2 - C[(2,2,1)]*v2**2
|
|
1449
|
+
dv2 = - C[(1,1,2)]*v1**2 - 2*C[(1,2,2)]*v1*v2 - C[(2,2,2)]*v2**2
|
|
1450
|
+
fun1 = fast_float(dv1, str(u1), str(u2), str(v1), str(v2))
|
|
1451
|
+
fun2 = fast_float(dv2, str(u1), str(u2), str(v1), str(v2))
|
|
1452
|
+
|
|
1453
|
+
geodesic_ode = ode_solver()
|
|
1454
|
+
geodesic_ode.function = (
|
|
1455
|
+
lambda t, u1_u2_v1_v2:
|
|
1456
|
+
[u1_u2_v1_v2[2], u1_u2_v1_v2[3], fun1(*u1_u2_v1_v2), fun2(*u1_u2_v1_v2)])
|
|
1457
|
+
return geodesic_ode
|
|
1458
|
+
|
|
1459
|
+
def geodesics_numerical(self, p0, v0, tinterval):
|
|
1460
|
+
r"""
|
|
1461
|
+
Numerical integration of the geodesic equations. Explicitly, the
|
|
1462
|
+
geodesic equations are given by
|
|
1463
|
+
`\frac{d^2 u^i}{dt^2} + \Gamma^i_{jk} \frac{d u^j}{dt} \frac{d u^k}{dt} = 0`.
|
|
1464
|
+
|
|
1465
|
+
Solving these equations gives the coordinates `(u^1, u^2)` of
|
|
1466
|
+
the geodesic on the surface. The coordinates in space can
|
|
1467
|
+
then be found by substituting `(u^1, u^2)` into the vector
|
|
1468
|
+
`\vec{r}(u^1, u^2)` representing the surface.
|
|
1469
|
+
|
|
1470
|
+
ALGORITHM:
|
|
1471
|
+
|
|
1472
|
+
The geodesic equations are integrated forward in time using
|
|
1473
|
+
the ode solvers from ``sage.calculus.ode``. See the member
|
|
1474
|
+
function ``_create_geodesic_ode_system`` for more details.
|
|
1475
|
+
|
|
1476
|
+
INPUT:
|
|
1477
|
+
|
|
1478
|
+
- ``p0`` -- 2-tuple with coordinates of the initial point
|
|
1479
|
+
|
|
1480
|
+
- ``v0`` -- 2-tuple with components of the initial tangent vector to the geodesic
|
|
1481
|
+
|
|
1482
|
+
- ``tinterval`` -- list ``[a, b, M]``, where ``(a,b)`` is the domain
|
|
1483
|
+
of the geodesic and ``M`` is the number of subdivision points used
|
|
1484
|
+
when returning the solution
|
|
1485
|
+
|
|
1486
|
+
OUTPUT:
|
|
1487
|
+
|
|
1488
|
+
List of lists ``[t, [u1(t), u2(t)], [v1(t), v2(t)], [x1(t), x2(t), x3(t)]]``, where
|
|
1489
|
+
|
|
1490
|
+
- ``t`` -- a subdivision point;
|
|
1491
|
+
|
|
1492
|
+
- ``[u1(t), u2(t)]`` are the intrinsic coordinates of the geodesic point;
|
|
1493
|
+
|
|
1494
|
+
- ``[v1(t), v2(t)]`` are the intrinsic coordinates of the tangent vector to the geodesic;
|
|
1495
|
+
|
|
1496
|
+
- ``[x1(t), x2(t), x3(t)]`` are the coordinates of the geodesic point in the three-dimensional space.
|
|
1497
|
+
|
|
1498
|
+
EXAMPLES::
|
|
1499
|
+
|
|
1500
|
+
sage: p, q = var('p,q', domain='real')
|
|
1501
|
+
sage: assume(cos(q)>0)
|
|
1502
|
+
sage: sphere = ParametrizedSurface3D([cos(q)*cos(p),sin(q)*cos(p),sin(p)],[p,q],'sphere')
|
|
1503
|
+
sage: geodesic = sphere.geodesics_numerical([0.0,0.0],[1.0,1.0],[0,2*pi,5])
|
|
1504
|
+
sage: times, points, tangent_vectors, ext_points = zip(*geodesic)
|
|
1505
|
+
|
|
1506
|
+
sage: round4 = lambda vec: [N(x, digits=4) for x in vec] # helper function to round to 4 digits
|
|
1507
|
+
sage: round4(times)
|
|
1508
|
+
[0.0000, 1.257, 2.513, 3.770, 5.027, 6.283]
|
|
1509
|
+
sage: [round4(p) for p in points]
|
|
1510
|
+
[[0.0000, 0.0000], [0.7644, 1.859], [-0.2876, 3.442], [-0.6137, 5.502], [0.5464, 6.937], [0.3714, 9.025]]
|
|
1511
|
+
sage: [round4(p) for p in ext_points]
|
|
1512
|
+
[[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]]
|
|
1513
|
+
"""
|
|
1514
|
+
solver = self._create_geodesic_ode_system()
|
|
1515
|
+
|
|
1516
|
+
t_interval, n = tinterval[0:2], tinterval[2]
|
|
1517
|
+
solver.y_0 = [p0[0], p0[1], v0[0], v0[1]]
|
|
1518
|
+
solver.ode_solve(t_span=t_interval, num_points=n)
|
|
1519
|
+
|
|
1520
|
+
parsed_solution = \
|
|
1521
|
+
[[vec[0], vec[1][0:2], vec[1][2:], self.point(vec[1])]
|
|
1522
|
+
for vec in solver.solution]
|
|
1523
|
+
|
|
1524
|
+
return parsed_solution
|
|
1525
|
+
|
|
1526
|
+
@cached_method
|
|
1527
|
+
def _create_pt_ode_system(self, curve, t):
|
|
1528
|
+
"""
|
|
1529
|
+
Helper method to create a fast floating-point version of the parallel
|
|
1530
|
+
transport equations, used by ``parallel_translation_numerical``.
|
|
1531
|
+
|
|
1532
|
+
INPUT:
|
|
1533
|
+
|
|
1534
|
+
- ``curve`` -- curve in intrinsic coordinates along which to do parallel transport
|
|
1535
|
+
- ``t`` -- curve parameter
|
|
1536
|
+
|
|
1537
|
+
EXAMPLES::
|
|
1538
|
+
|
|
1539
|
+
sage: p, q = var('p,q', domain='real')
|
|
1540
|
+
sage: sphere = ParametrizedSurface3D([cos(q)*cos(p),sin(q)*cos(p),sin(p)],[p,q],'sphere')
|
|
1541
|
+
sage: s = var('s')
|
|
1542
|
+
sage: ode = sphere._create_pt_ode_system((s, s), s)
|
|
1543
|
+
sage: ode.function(0.0, (1.0, 1.0))
|
|
1544
|
+
[-0.0, 0.0]
|
|
1545
|
+
"""
|
|
1546
|
+
|
|
1547
|
+
from sage.ext.fast_eval import fast_float
|
|
1548
|
+
from sage.calculus.ode import ode_solver
|
|
1549
|
+
|
|
1550
|
+
u1 = self.variables[1]
|
|
1551
|
+
u2 = self.variables[2]
|
|
1552
|
+
|
|
1553
|
+
du1 = diff(curve[0], t)
|
|
1554
|
+
du2 = diff(curve[1], t)
|
|
1555
|
+
|
|
1556
|
+
C = self.connection_coefficients()
|
|
1557
|
+
for coef in C:
|
|
1558
|
+
C[coef] = C[coef].subs({u1: curve[0], u2: curve[1]})
|
|
1559
|
+
|
|
1560
|
+
with SR.temp_var(domain='real') as v1:
|
|
1561
|
+
with SR.temp_var(domain='real') as v2:
|
|
1562
|
+
dv1 = - C[(1,1,1)]*v1*du1 - C[(1,2,1)]*(du1*v2 + du2*v1) - \
|
|
1563
|
+
C[(2,2,1)]*du2*v2
|
|
1564
|
+
dv2 = - C[(1,1,2)]*v1*du1 - C[(1,2,2)]*(du1*v2 + du2*v1) - \
|
|
1565
|
+
C[(2,2,2)]*du2*v2
|
|
1566
|
+
fun1 = fast_float(dv1, str(t), str(v1), str(v2))
|
|
1567
|
+
fun2 = fast_float(dv2, str(t), str(v1), str(v2))
|
|
1568
|
+
|
|
1569
|
+
pt_ode = ode_solver()
|
|
1570
|
+
pt_ode.function = lambda t, v1_v2: [fun1(t, v1_v2[0], v1_v2[1]), fun2(t, v1_v2[0], v1_v2[1])]
|
|
1571
|
+
return pt_ode
|
|
1572
|
+
|
|
1573
|
+
def parallel_translation_numerical(self, curve, t, v0, tinterval):
|
|
1574
|
+
r"""
|
|
1575
|
+
Numerically solve the equations for parallel translation of a vector
|
|
1576
|
+
along a curve on the surface. Explicitly, the equations for parallel
|
|
1577
|
+
translation are given by
|
|
1578
|
+
`\frac{d u^i}{dt} + u^j \frac{d c^k}{dt} \Gamma^i_{jk} = 0`,
|
|
1579
|
+
where `\Gamma^i_{jk}` are the connection coefficients of the surface,
|
|
1580
|
+
the vector to be transported has components `u^j` and the curve along
|
|
1581
|
+
which to transport has components `c^k`.
|
|
1582
|
+
|
|
1583
|
+
ALGORITHM:
|
|
1584
|
+
|
|
1585
|
+
The parallel transport equations are integrated forward in time using
|
|
1586
|
+
the ode solvers from ``sage.calculus.ode``. See :meth:`_create_pt_ode_system`
|
|
1587
|
+
for more details.
|
|
1588
|
+
|
|
1589
|
+
INPUT:
|
|
1590
|
+
|
|
1591
|
+
- ``curve`` -- 2-tuple of functions which determine the curve with respect to
|
|
1592
|
+
the local coordinate system
|
|
1593
|
+
|
|
1594
|
+
- ``t`` -- symbolic variable denoting the curve parameter
|
|
1595
|
+
|
|
1596
|
+
- ``v0`` -- 2-tuple representing the initial vector
|
|
1597
|
+
|
|
1598
|
+
- ``tinterval`` -- list ``[a, b, N]``, where ``(a, b)`` is the domain of the curve
|
|
1599
|
+
and ``N`` is the number of subdivision points
|
|
1600
|
+
|
|
1601
|
+
OUTPUT:
|
|
1602
|
+
|
|
1603
|
+
The list consisting of lists ``[t, [v1(t), v2(t)]]``, where
|
|
1604
|
+
|
|
1605
|
+
- ``t`` -- a subdivision point;
|
|
1606
|
+
|
|
1607
|
+
- ``[v1(t), v2(t)]`` is the list of coordinates of the vector parallel translated
|
|
1608
|
+
along the curve.
|
|
1609
|
+
|
|
1610
|
+
EXAMPLES::
|
|
1611
|
+
|
|
1612
|
+
sage: p, q = var('p,q', domain='real')
|
|
1613
|
+
sage: v = [p,q]
|
|
1614
|
+
sage: assume(cos(q)>0)
|
|
1615
|
+
sage: sphere = ParametrizedSurface3D([cos(q)*cos(p),sin(q)*cos(p),sin(p)],v,'sphere')
|
|
1616
|
+
sage: s = var('s')
|
|
1617
|
+
sage: vector_field = sphere.parallel_translation_numerical([s,s],s,[1.0,1.0],[0.0, pi/4, 5])
|
|
1618
|
+
sage: times, components = zip(*vector_field)
|
|
1619
|
+
|
|
1620
|
+
sage: round4 = lambda vec: [N(x, digits=4) for x in vec] # helper function to round to 4 digits
|
|
1621
|
+
sage: round4(times)
|
|
1622
|
+
[0.0000, 0.1571, 0.3142, 0.4712, 0.6283, 0.7854]
|
|
1623
|
+
sage: [round4(v) for v in components]
|
|
1624
|
+
[[1.000, 1.000], [0.9876, 1.025], [0.9499, 1.102], [0.8853, 1.238], [0.7920, 1.448], [0.6687, 1.762]]
|
|
1625
|
+
"""
|
|
1626
|
+
solver = self._create_pt_ode_system(tuple(curve), t)
|
|
1627
|
+
|
|
1628
|
+
t_interval, n = tinterval[0:2], tinterval[2]
|
|
1629
|
+
solver.y_0 = v0
|
|
1630
|
+
solver.ode_solve(t_span=t_interval, num_points=n)
|
|
1631
|
+
|
|
1632
|
+
return solver.solution
|