passagemath-symbolics 10.6.40__cp314-cp314t-macosx_13_0_x86_64.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.

Potentially problematic release.


This version of passagemath-symbolics might be problematic. Click here for more details.

Files changed (172) hide show
  1. passagemath_symbolics/.dylibs/libgmp.10.dylib +0 -0
  2. passagemath_symbolics/__init__.py +3 -0
  3. passagemath_symbolics-10.6.40.dist-info/METADATA +187 -0
  4. passagemath_symbolics-10.6.40.dist-info/RECORD +172 -0
  5. passagemath_symbolics-10.6.40.dist-info/WHEEL +6 -0
  6. passagemath_symbolics-10.6.40.dist-info/top_level.txt +3 -0
  7. sage/all__sagemath_symbolics.py +17 -0
  8. sage/calculus/all.py +14 -0
  9. sage/calculus/calculus.py +2826 -0
  10. sage/calculus/desolvers.py +1866 -0
  11. sage/calculus/predefined.py +51 -0
  12. sage/calculus/tests.py +225 -0
  13. sage/calculus/var.cpython-314t-darwin.so +0 -0
  14. sage/calculus/var.pyx +401 -0
  15. sage/dynamics/all__sagemath_symbolics.py +6 -0
  16. sage/dynamics/complex_dynamics/all.py +5 -0
  17. sage/dynamics/complex_dynamics/mandel_julia.py +765 -0
  18. sage/dynamics/complex_dynamics/mandel_julia_helper.cpython-314t-darwin.so +0 -0
  19. sage/dynamics/complex_dynamics/mandel_julia_helper.pyx +1035 -0
  20. sage/ext/all__sagemath_symbolics.py +1 -0
  21. sage/ext_data/kenzo/CP2.txt +45 -0
  22. sage/ext_data/kenzo/CP3.txt +349 -0
  23. sage/ext_data/kenzo/CP4.txt +4774 -0
  24. sage/ext_data/kenzo/README.txt +49 -0
  25. sage/ext_data/kenzo/S4.txt +20 -0
  26. sage/ext_data/magma/latex/latex.m +1021 -0
  27. sage/ext_data/magma/latex/latex.spec +1 -0
  28. sage/ext_data/magma/sage/basic.m +356 -0
  29. sage/ext_data/magma/sage/sage.spec +1 -0
  30. sage/ext_data/magma/spec +9 -0
  31. sage/geometry/all__sagemath_symbolics.py +8 -0
  32. sage/geometry/hyperbolic_space/all.py +5 -0
  33. sage/geometry/hyperbolic_space/hyperbolic_coercion.py +743 -0
  34. sage/geometry/hyperbolic_space/hyperbolic_constants.py +5 -0
  35. sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +2409 -0
  36. sage/geometry/hyperbolic_space/hyperbolic_interface.py +206 -0
  37. sage/geometry/hyperbolic_space/hyperbolic_isometry.py +1082 -0
  38. sage/geometry/hyperbolic_space/hyperbolic_model.py +1502 -0
  39. sage/geometry/hyperbolic_space/hyperbolic_point.py +621 -0
  40. sage/geometry/riemannian_manifolds/all.py +7 -0
  41. sage/geometry/riemannian_manifolds/parametrized_surface3d.py +1632 -0
  42. sage/geometry/riemannian_manifolds/surface3d_generators.py +461 -0
  43. sage/interfaces/all__sagemath_symbolics.py +1 -0
  44. sage/interfaces/magma.py +3017 -0
  45. sage/interfaces/magma_free.py +92 -0
  46. sage/interfaces/maple.py +1397 -0
  47. sage/interfaces/mathematica.py +1345 -0
  48. sage/interfaces/mathics.py +1312 -0
  49. sage/interfaces/sympy.py +1398 -0
  50. sage/interfaces/sympy_wrapper.py +197 -0
  51. sage/interfaces/tides.py +938 -0
  52. sage/libs/all__sagemath_symbolics.py +6 -0
  53. sage/manifolds/all.py +7 -0
  54. sage/manifolds/calculus_method.py +555 -0
  55. sage/manifolds/catalog.py +437 -0
  56. sage/manifolds/chart.py +4019 -0
  57. sage/manifolds/chart_func.py +3419 -0
  58. sage/manifolds/continuous_map.py +2183 -0
  59. sage/manifolds/continuous_map_image.py +155 -0
  60. sage/manifolds/differentiable/affine_connection.py +2475 -0
  61. sage/manifolds/differentiable/all.py +1 -0
  62. sage/manifolds/differentiable/automorphismfield.py +1383 -0
  63. sage/manifolds/differentiable/automorphismfield_group.py +604 -0
  64. sage/manifolds/differentiable/bundle_connection.py +1445 -0
  65. sage/manifolds/differentiable/characteristic_cohomology_class.py +1840 -0
  66. sage/manifolds/differentiable/chart.py +1241 -0
  67. sage/manifolds/differentiable/curve.py +1028 -0
  68. sage/manifolds/differentiable/de_rham_cohomology.py +541 -0
  69. sage/manifolds/differentiable/degenerate.py +559 -0
  70. sage/manifolds/differentiable/degenerate_submanifold.py +1671 -0
  71. sage/manifolds/differentiable/diff_form.py +1658 -0
  72. sage/manifolds/differentiable/diff_form_module.py +1062 -0
  73. sage/manifolds/differentiable/diff_map.py +1315 -0
  74. sage/manifolds/differentiable/differentiable_submanifold.py +291 -0
  75. sage/manifolds/differentiable/examples/all.py +1 -0
  76. sage/manifolds/differentiable/examples/euclidean.py +2517 -0
  77. sage/manifolds/differentiable/examples/real_line.py +897 -0
  78. sage/manifolds/differentiable/examples/sphere.py +1186 -0
  79. sage/manifolds/differentiable/examples/symplectic_space.py +187 -0
  80. sage/manifolds/differentiable/examples/symplectic_space_test.py +40 -0
  81. sage/manifolds/differentiable/integrated_curve.py +4035 -0
  82. sage/manifolds/differentiable/levi_civita_connection.py +841 -0
  83. sage/manifolds/differentiable/manifold.py +4254 -0
  84. sage/manifolds/differentiable/manifold_homset.py +1826 -0
  85. sage/manifolds/differentiable/metric.py +3032 -0
  86. sage/manifolds/differentiable/mixed_form.py +1507 -0
  87. sage/manifolds/differentiable/mixed_form_algebra.py +559 -0
  88. sage/manifolds/differentiable/multivector_module.py +800 -0
  89. sage/manifolds/differentiable/multivectorfield.py +1520 -0
  90. sage/manifolds/differentiable/poisson_tensor.py +268 -0
  91. sage/manifolds/differentiable/pseudo_riemannian.py +755 -0
  92. sage/manifolds/differentiable/pseudo_riemannian_submanifold.py +1839 -0
  93. sage/manifolds/differentiable/scalarfield.py +1343 -0
  94. sage/manifolds/differentiable/scalarfield_algebra.py +472 -0
  95. sage/manifolds/differentiable/symplectic_form.py +910 -0
  96. sage/manifolds/differentiable/symplectic_form_test.py +220 -0
  97. sage/manifolds/differentiable/tangent_space.py +412 -0
  98. sage/manifolds/differentiable/tangent_vector.py +616 -0
  99. sage/manifolds/differentiable/tensorfield.py +4665 -0
  100. sage/manifolds/differentiable/tensorfield_module.py +963 -0
  101. sage/manifolds/differentiable/tensorfield_paral.py +2450 -0
  102. sage/manifolds/differentiable/tensorfield_paral_test.py +16 -0
  103. sage/manifolds/differentiable/vector_bundle.py +1728 -0
  104. sage/manifolds/differentiable/vectorfield.py +1717 -0
  105. sage/manifolds/differentiable/vectorfield_module.py +2445 -0
  106. sage/manifolds/differentiable/vectorframe.py +1832 -0
  107. sage/manifolds/family.py +270 -0
  108. sage/manifolds/local_frame.py +1490 -0
  109. sage/manifolds/manifold.py +3090 -0
  110. sage/manifolds/manifold_homset.py +452 -0
  111. sage/manifolds/operators.py +359 -0
  112. sage/manifolds/point.py +994 -0
  113. sage/manifolds/scalarfield.py +3718 -0
  114. sage/manifolds/scalarfield_algebra.py +629 -0
  115. sage/manifolds/section.py +3111 -0
  116. sage/manifolds/section_module.py +831 -0
  117. sage/manifolds/structure.py +229 -0
  118. sage/manifolds/subset.py +2764 -0
  119. sage/manifolds/subsets/all.py +1 -0
  120. sage/manifolds/subsets/closure.py +131 -0
  121. sage/manifolds/subsets/pullback.py +885 -0
  122. sage/manifolds/topological_submanifold.py +891 -0
  123. sage/manifolds/trivialization.py +733 -0
  124. sage/manifolds/utilities.py +1348 -0
  125. sage/manifolds/vector_bundle.py +1342 -0
  126. sage/manifolds/vector_bundle_fiber.py +332 -0
  127. sage/manifolds/vector_bundle_fiber_element.py +111 -0
  128. sage/matrix/all__sagemath_symbolics.py +1 -0
  129. sage/matrix/matrix_symbolic_dense.cpython-314t-darwin.so +0 -0
  130. sage/matrix/matrix_symbolic_dense.pxd +6 -0
  131. sage/matrix/matrix_symbolic_dense.pyx +1022 -0
  132. sage/matrix/matrix_symbolic_sparse.cpython-314t-darwin.so +0 -0
  133. sage/matrix/matrix_symbolic_sparse.pxd +6 -0
  134. sage/matrix/matrix_symbolic_sparse.pyx +1029 -0
  135. sage/modules/all__sagemath_symbolics.py +1 -0
  136. sage/modules/vector_callable_symbolic_dense.py +105 -0
  137. sage/modules/vector_symbolic_dense.py +116 -0
  138. sage/modules/vector_symbolic_sparse.py +118 -0
  139. sage/rings/all__sagemath_symbolics.py +4 -0
  140. sage/rings/asymptotic/all.py +6 -0
  141. sage/rings/asymptotic/asymptotic_expansion_generators.py +1485 -0
  142. sage/rings/asymptotic/asymptotic_ring.py +4858 -0
  143. sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +4153 -0
  144. sage/rings/asymptotic/growth_group.py +5373 -0
  145. sage/rings/asymptotic/growth_group_cartesian.py +1400 -0
  146. sage/rings/asymptotic/term_monoid.py +5237 -0
  147. sage/rings/function_field/all__sagemath_symbolics.py +2 -0
  148. sage/rings/polynomial/all__sagemath_symbolics.py +1 -0
  149. sage/symbolic/all.py +15 -0
  150. sage/symbolic/assumptions.py +985 -0
  151. sage/symbolic/benchmark.py +93 -0
  152. sage/symbolic/callable.py +459 -0
  153. sage/symbolic/complexity_measures.py +35 -0
  154. sage/symbolic/constants.py +1287 -0
  155. sage/symbolic/expression_conversion_algebraic.py +310 -0
  156. sage/symbolic/expression_conversion_sympy.py +317 -0
  157. sage/symbolic/expression_conversions.py +1713 -0
  158. sage/symbolic/function_factory.py +355 -0
  159. sage/symbolic/integration/all.py +1 -0
  160. sage/symbolic/integration/external.py +270 -0
  161. sage/symbolic/integration/integral.py +1115 -0
  162. sage/symbolic/maxima_wrapper.py +162 -0
  163. sage/symbolic/operators.py +267 -0
  164. sage/symbolic/random_tests.py +462 -0
  165. sage/symbolic/relation.py +1907 -0
  166. sage/symbolic/ring.cpython-314t-darwin.so +0 -0
  167. sage/symbolic/ring.pxd +5 -0
  168. sage/symbolic/ring.pyx +1396 -0
  169. sage/symbolic/subring.py +1025 -0
  170. sage/symbolic/symengine.py +19 -0
  171. sage/symbolic/tests.py +40 -0
  172. sage/symbolic/units.py +1470 -0
@@ -0,0 +1,2517 @@
1
+ # sage_setup: distribution = sagemath-symbolics
2
+ r"""
3
+ Euclidean Spaces
4
+
5
+ A *Euclidean space of dimension* `n` is an affine space `E`, whose associated
6
+ vector space is a `n`-dimensional vector space over `\RR` and is equipped with
7
+ a positive definite symmetric bilinear form, called the *scalar product* or
8
+ *dot product* [Ber1987]_. A Euclidean space of dimension `n` can also be
9
+ viewed as a Riemannian manifold that is diffeomorphic to `\RR^n` and that
10
+ has a flat metric `g`. The Euclidean scalar product is then that defined
11
+ by the Riemannian metric `g`.
12
+
13
+ The current implementation of Euclidean spaces is based on the second point of
14
+ view. This allows for the introduction of various coordinate systems in
15
+ addition to the usual the Cartesian systems. Standard curvilinear systems
16
+ (planar, spherical and cylindrical coordinates) are predefined for
17
+ 2-dimensional and 3-dimensional Euclidean spaces, along with the corresponding
18
+ transition maps between them. Another benefit of such an implementation is
19
+ the direct use of methods for vector calculus already implemented at the
20
+ level of Riemannian manifolds (see, e.g., the methods
21
+ :meth:`~sage.manifolds.differentiable.vectorfield.VectorField.cross_product`
22
+ and
23
+ :meth:`~sage.manifolds.differentiable.vectorfield.VectorField.curl`,
24
+ as well as the module :mod:`~sage.manifolds.operators`).
25
+
26
+ Euclidean spaces are implemented via the following classes:
27
+
28
+ - :class:`EuclideanSpace` for generic values `n`,
29
+ - :class:`EuclideanPlane` for `n = 2`,
30
+ - :class:`Euclidean3dimSpace` for `n = 3`.
31
+
32
+ The user interface is provided by :class:`EuclideanSpace`.
33
+
34
+ .. _EuclideanSpace_example1:
35
+
36
+ .. RUBRIC:: Example 1: the Euclidean plane
37
+
38
+ We start by declaring the Euclidean plane ``E``, with ``(x, y)`` as
39
+ Cartesian coordinates::
40
+
41
+ sage: E.<x,y> = EuclideanSpace()
42
+ sage: E
43
+ Euclidean plane E^2
44
+ sage: dim(E)
45
+ 2
46
+
47
+ ``E`` is automatically endowed with the chart of Cartesian coordinates::
48
+
49
+ sage: E.atlas()
50
+ [Chart (E^2, (x, y))]
51
+ sage: cartesian = E.default_chart(); cartesian
52
+ Chart (E^2, (x, y))
53
+
54
+ Thanks to the use of ``<x,y>`` when declaring ``E``, the coordinates `(x,y)`
55
+ have been injected in the global namespace, i.e. the Python variables ``x``
56
+ and ``y`` have been created and are available to form symbolic expressions::
57
+
58
+ sage: y
59
+ y
60
+ sage: type(y)
61
+ <class 'sage.symbolic.expression.Expression'>
62
+ sage: assumptions()
63
+ [x is real, y is real]
64
+
65
+ The metric tensor of ``E`` is predefined::
66
+
67
+ sage: g = E.metric(); g
68
+ Riemannian metric g on the Euclidean plane E^2
69
+ sage: g.display()
70
+ g = dx⊗dx + dy⊗dy
71
+ sage: g[:]
72
+ [1 0]
73
+ [0 1]
74
+
75
+ It is a *flat* metric, i.e. it has a vanishing Riemann tensor::
76
+
77
+ sage: g.riemann()
78
+ Tensor field Riem(g) of type (1,3) on the Euclidean plane E^2
79
+ sage: g.riemann().display()
80
+ Riem(g) = 0
81
+
82
+ Polar coordinates `(r,\phi)` are introduced by::
83
+
84
+ sage: polar.<r,ph> = E.polar_coordinates()
85
+ sage: polar
86
+ Chart (E^2, (r, ph))
87
+
88
+ ``E`` is now endowed with two coordinate charts::
89
+
90
+ sage: E.atlas()
91
+ [Chart (E^2, (x, y)), Chart (E^2, (r, ph))]
92
+
93
+ The ranges of the coordinates introduced so far are::
94
+
95
+ sage: cartesian.coord_range()
96
+ x: (-oo, +oo); y: (-oo, +oo)
97
+ sage: polar.coord_range()
98
+ r: (0, +oo); ph: [0, 2*pi] (periodic)
99
+
100
+ The transition map from polar coordinates to Cartesian ones is::
101
+
102
+ sage: E.coord_change(polar, cartesian).display()
103
+ x = r*cos(ph)
104
+ y = r*sin(ph)
105
+
106
+ while the reverse one is::
107
+
108
+ sage: E.coord_change(cartesian, polar).display()
109
+ r = sqrt(x^2 + y^2)
110
+ ph = arctan2(y, x)
111
+
112
+ A point of ``E`` is constructed from its coordinates (by default in the
113
+ Cartesian chart)::
114
+
115
+ sage: p = E((-1,1), name='p'); p
116
+ Point p on the Euclidean plane E^2
117
+ sage: p.parent()
118
+ Euclidean plane E^2
119
+
120
+ The coordinates of a point are obtained by letting the corresponding chart
121
+ act on it::
122
+
123
+ sage: cartesian(p)
124
+ (-1, 1)
125
+ sage: polar(p)
126
+ (sqrt(2), 3/4*pi)
127
+
128
+ At this stage, ``E`` is endowed with three vector frames::
129
+
130
+ sage: E.frames()
131
+ [Coordinate frame (E^2, (e_x,e_y)),
132
+ Coordinate frame (E^2, (∂/∂r,∂/∂ph)),
133
+ Vector frame (E^2, (e_r,e_ph))]
134
+
135
+ The third one is the standard orthonormal frame associated with polar
136
+ coordinates, as we can check from the metric components in it::
137
+
138
+ sage: polar_frame = E.polar_frame(); polar_frame
139
+ Vector frame (E^2, (e_r,e_ph))
140
+ sage: g[polar_frame,:]
141
+ [1 0]
142
+ [0 1]
143
+
144
+ The expression of the metric tensor in terms of polar coordinates is::
145
+
146
+ sage: g.display(polar)
147
+ g = dr⊗dr + r^2 dph⊗dph
148
+
149
+ A vector field on ``E``::
150
+
151
+ sage: v = E.vector_field(-y, x, name='v'); v
152
+ Vector field v on the Euclidean plane E^2
153
+ sage: v.display()
154
+ v = -y e_x + x e_y
155
+ sage: v[:]
156
+ [-y, x]
157
+
158
+ By default, the components of ``v``, as returned by ``display`` or the bracket
159
+ operator, refer to the Cartesian frame on ``E``; to get the components with
160
+ respect to the orthonormal polar frame, one has to specify it explicitly,
161
+ generally along with the polar chart for the coordinate expression of the
162
+ components::
163
+
164
+ sage: v.display(polar_frame, polar)
165
+ v = r e_ph
166
+ sage: v[polar_frame,:,polar]
167
+ [0, r]
168
+
169
+ Note that the default frame for the display of vector fields can be changed
170
+ thanks to the method
171
+ :meth:`~sage.manifolds.differentiable.manifold.DifferentiableManifold.set_default_frame`;
172
+ in the same vein, the default coordinates can be changed via the method
173
+ :meth:`~sage.manifolds.manifold.TopologicalManifold.set_default_chart`::
174
+
175
+ sage: E.set_default_frame(polar_frame)
176
+ sage: E.set_default_chart(polar)
177
+ sage: v.display()
178
+ v = r e_ph
179
+ sage: v[:]
180
+ [0, r]
181
+ sage: E.set_default_frame(E.cartesian_frame()) # revert to Cartesian frame
182
+ sage: E.set_default_chart(cartesian) # and chart
183
+
184
+ When defining a vector field from components relative to a vector frame
185
+ different from the default one, the vector frame has to be specified
186
+ explicitly::
187
+
188
+ sage: v = E.vector_field(1, 0, frame=polar_frame)
189
+ sage: v.display(polar_frame)
190
+ e_r
191
+ sage: v.display()
192
+ x/sqrt(x^2 + y^2) e_x + y/sqrt(x^2 + y^2) e_y
193
+
194
+ The argument ``chart`` must be used to specify in which coordinate
195
+ chart the components are expressed::
196
+
197
+ sage: v = E.vector_field(0, r, frame=polar_frame, chart=polar)
198
+ sage: v.display(polar_frame, polar)
199
+ r e_ph
200
+ sage: v.display()
201
+ -y e_x + x e_y
202
+
203
+ It is also possible to pass the components as a dictionary, with
204
+ a pair (vector frame, chart) as a key::
205
+
206
+ sage: v = E.vector_field({(polar_frame, polar): (0, r)})
207
+ sage: v.display(polar_frame, polar)
208
+ r e_ph
209
+
210
+ The key can be reduced to the vector frame if the chart is the default
211
+ one::
212
+
213
+ sage: v = E.vector_field({polar_frame: (0, 1)})
214
+ sage: v.display(polar_frame)
215
+ e_ph
216
+
217
+ Finally, it is possible to construct the vector field without
218
+ initializing any component::
219
+
220
+ sage: v = E.vector_field(); v
221
+ Vector field on the Euclidean plane E^2
222
+
223
+ The components can then by set in a second stage, via the square
224
+ bracket operator, the unset components being assumed to be zero::
225
+
226
+ sage: v[1] = -y
227
+ sage: v.display() # v[2] is zero
228
+ -y e_x
229
+ sage: v[2] = x
230
+ sage: v.display()
231
+ -y e_x + x e_y
232
+
233
+ The above is equivalent to::
234
+
235
+ sage: v[:] = -y, x
236
+ sage: v.display()
237
+ -y e_x + x e_y
238
+
239
+ The square bracket operator can also be used to set components in a
240
+ vector frame that is not the default one::
241
+
242
+ sage: v = E.vector_field(name='v')
243
+ sage: v[polar_frame, 2, polar] = r
244
+ sage: v.display(polar_frame, polar)
245
+ v = r e_ph
246
+ sage: v.display()
247
+ v = -y e_x + x e_y
248
+
249
+ The value of the vector field ``v`` at point ``p``::
250
+
251
+ sage: vp = v.at(p); vp
252
+ Vector v at Point p on the Euclidean plane E^2
253
+ sage: vp.display()
254
+ v = -e_x - e_y
255
+ sage: vp.display(polar_frame.at(p))
256
+ v = sqrt(2) e_ph
257
+
258
+ A scalar field on ``E``::
259
+
260
+ sage: f = E.scalar_field(x*y, name='f'); f
261
+ Scalar field f on the Euclidean plane E^2
262
+ sage: f.display()
263
+ f: E^2 → ℝ
264
+ (x, y) ↦ x*y
265
+ (r, ph) ↦ r^2*cos(ph)*sin(ph)
266
+
267
+ The value of ``f`` at point ``p``::
268
+
269
+ sage: f(p)
270
+ -1
271
+
272
+ The gradient of ``f``::
273
+
274
+ sage: from sage.manifolds.operators import * # to get grad, div, etc.
275
+ sage: w = grad(f); w
276
+ Vector field grad(f) on the Euclidean plane E^2
277
+ sage: w.display()
278
+ grad(f) = y e_x + x e_y
279
+ sage: w.display(polar_frame, polar)
280
+ grad(f) = 2*r*cos(ph)*sin(ph) e_r + (2*cos(ph)^2 - 1)*r e_ph
281
+
282
+ The dot product of two vector fields::
283
+
284
+ sage: s = v.dot(w); s
285
+ Scalar field v.grad(f) on the Euclidean plane E^2
286
+ sage: s.display()
287
+ v.grad(f): E^2 → ℝ
288
+ (x, y) ↦ x^2 - y^2
289
+ (r, ph) ↦ (2*cos(ph)^2 - 1)*r^2
290
+ sage: s.expr()
291
+ x^2 - y^2
292
+
293
+ The norm is related to the dot product by the standard formula::
294
+
295
+ sage: norm(v)^2 == v.dot(v)
296
+ True
297
+
298
+ The divergence of the vector field ``v``::
299
+
300
+ sage: s = div(v); s
301
+ Scalar field div(v) on the Euclidean plane E^2
302
+ sage: s.display()
303
+ div(v): E^2 → ℝ
304
+ (x, y) ↦ 0
305
+ (r, ph) ↦ 0
306
+
307
+ .. _EuclideanSpace_example2:
308
+
309
+ .. RUBRIC:: Example 2: Vector calculus in the Euclidean 3-space
310
+
311
+ We start by declaring the 3-dimensional Euclidean space ``E``, with
312
+ ``(x,y,z)`` as Cartesian coordinates::
313
+
314
+ sage: E.<x,y,z> = EuclideanSpace()
315
+ sage: E
316
+ Euclidean space E^3
317
+
318
+ A simple vector field on ``E``::
319
+
320
+ sage: v = E.vector_field(-y, x, 0, name='v')
321
+ sage: v.display()
322
+ v = -y e_x + x e_y
323
+ sage: v[:]
324
+ [-y, x, 0]
325
+
326
+ The Euclidean norm of ``v``::
327
+
328
+ sage: s = norm(v); s
329
+ Scalar field |v| on the Euclidean space E^3
330
+ sage: s.display()
331
+ |v|: E^3 → ℝ
332
+ (x, y, z) ↦ sqrt(x^2 + y^2)
333
+ sage: s.expr()
334
+ sqrt(x^2 + y^2)
335
+
336
+ The divergence of ``v`` is zero::
337
+
338
+ sage: from sage.manifolds.operators import *
339
+ sage: div(v)
340
+ Scalar field div(v) on the Euclidean space E^3
341
+ sage: div(v).display()
342
+ div(v): E^3 → ℝ
343
+ (x, y, z) ↦ 0
344
+
345
+ while its curl is a constant vector field along `e_z`::
346
+
347
+ sage: w = curl(v); w
348
+ Vector field curl(v) on the Euclidean space E^3
349
+ sage: w.display()
350
+ curl(v) = 2 e_z
351
+
352
+ The gradient of a scalar field::
353
+
354
+ sage: f = E.scalar_field(sin(x*y*z), name='f')
355
+ sage: u = grad(f); u
356
+ Vector field grad(f) on the Euclidean space E^3
357
+ sage: u.display()
358
+ grad(f) = y*z*cos(x*y*z) e_x + x*z*cos(x*y*z) e_y + x*y*cos(x*y*z) e_z
359
+
360
+ The curl of a gradient is zero::
361
+
362
+ sage: curl(u).display()
363
+ curl(grad(f)) = 0
364
+
365
+ The dot product of two vector fields::
366
+
367
+ sage: s = u.dot(v); s
368
+ Scalar field grad(f).v on the Euclidean space E^3
369
+ sage: s.expr()
370
+ (x^2 - y^2)*z*cos(x*y*z)
371
+
372
+ The cross product of two vector fields::
373
+
374
+ sage: a = u.cross(v); a
375
+ Vector field grad(f) x v on the Euclidean space E^3
376
+ sage: a.display()
377
+ grad(f) x v = -x^2*y*cos(x*y*z) e_x - x*y^2*cos(x*y*z) e_y
378
+ + 2*x*y*z*cos(x*y*z) e_z
379
+
380
+ The scalar triple product of three vector fields::
381
+
382
+ sage: triple_product = E.scalar_triple_product()
383
+ sage: s = triple_product(u, v, w); s
384
+ Scalar field epsilon(grad(f),v,curl(v)) on the Euclidean space E^3
385
+ sage: s.expr()
386
+ 4*x*y*z*cos(x*y*z)
387
+
388
+ Let us check that the scalar triple product of `u`, `v` and `w` is
389
+ `u\cdot(v\times w)`::
390
+
391
+ sage: s == u.dot(v.cross(w))
392
+ True
393
+
394
+ AUTHORS:
395
+
396
+ - Eric Gourgoulhon (2018): initial version
397
+
398
+ REFERENCES:
399
+
400
+ - \M. Berger: *Geometry I* [Ber1987]_
401
+ """
402
+
403
+ #*****************************************************************************
404
+ # Copyright (C) 2018 Eric Gourgoulhon <eric.gourgoulhon@obspm.fr>
405
+ #
406
+ # Distributed under the terms of the GNU General Public License (GPL)
407
+ # as published by the Free Software Foundation; either version 2 of
408
+ # the License, or (at your option) any later version.
409
+ # https://www.gnu.org/licenses/
410
+ #*****************************************************************************
411
+
412
+ from sage.categories.manifolds import Manifolds
413
+ from sage.categories.metric_spaces import MetricSpaces
414
+ from sage.functions.trig import atan2, cos, sin
415
+ from sage.manifolds.differentiable.pseudo_riemannian import PseudoRiemannianManifold
416
+ from sage.misc.functional import sqrt
417
+ from sage.misc.latex import latex
418
+ from sage.rings.real_mpfr import RR
419
+
420
+ ###############################################################################
421
+
422
+
423
+ class EuclideanSpace(PseudoRiemannianManifold):
424
+ r"""
425
+ Euclidean space.
426
+
427
+ A *Euclidean space of dimension* `n` is an affine space `E`, whose
428
+ associated vector space is a `n`-dimensional vector space over `\RR` and
429
+ is equipped with a positive definite symmetric bilinear form, called
430
+ the *scalar product* or *dot product*.
431
+
432
+ Euclidean space of dimension `n` can be viewed as a Riemannian manifold
433
+ that is diffeomorphic to `\RR^n` and that has a flat metric `g`. The
434
+ Euclidean scalar product is the one defined by the Riemannian metric `g`.
435
+
436
+ INPUT:
437
+
438
+ - ``n`` -- positive integer; dimension of the space over the real field
439
+ - ``name`` -- (default: ``None``) string; name (symbol) given to the
440
+ Euclidean space; if ``None``, the name will be set to ``'E^n'``
441
+ - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to
442
+ denote the space; if ``None``, it is set to ``'\mathbb{E}^{n}'`` if
443
+ ``name`` is ``None`` and to ``name`` otherwise
444
+ - ``coordinates`` -- (default: ``'Cartesian'``) string describing the
445
+ type of coordinates to be initialized at the Euclidean space
446
+ creation; allowed values are
447
+
448
+ - ``'Cartesian'`` (canonical coordinates on `\RR^n`)
449
+ - ``'polar'`` for ``n=2`` only (see
450
+ :meth:`~sage.manifolds.differentiable.examples.euclidean.EuclideanPlane.polar_coordinates`)
451
+ - ``'spherical'`` for ``n=3`` only (see
452
+ :meth:`~sage.manifolds.differentiable.examples.euclidean.Euclidean3dimSpace.spherical_coordinates`)
453
+ - ``'cylindrical'`` for ``n=3`` only (see
454
+ :meth:`~sage.manifolds.differentiable.examples.euclidean.Euclidean3dimSpace.cylindrical_coordinates`)
455
+
456
+ - ``symbols`` -- (default: ``None``) string defining the coordinate
457
+ text symbols and LaTeX symbols, with the same conventions as the
458
+ argument ``coordinates`` in
459
+ :class:`~sage.manifolds.differentiable.chart.RealDiffChart`, namely
460
+ ``symbols`` is a string of coordinate fields separated by a blank
461
+ space, where each field contains the coordinate's text symbol and
462
+ possibly the coordinate's LaTeX symbol (when the latter is different
463
+ from the text symbol), both symbols being separated by a colon
464
+ (``:``); if ``None``, the symbols will be automatically generated
465
+ according to the value of ``coordinates``
466
+ - ``metric_name`` -- (default: ``'g'``) string; name (symbol) given to the
467
+ Euclidean metric tensor
468
+ - ``metric_latex_name`` -- (default: ``None``) string; LaTeX symbol to
469
+ denote the Euclidean metric tensor; if none is provided, it is set to
470
+ ``metric_name``
471
+ - ``start_index`` -- (default: 1) integer; lower value of the range of
472
+ indices used for "indexed objects" in the Euclidean space, e.g.
473
+ coordinates of a chart
474
+ - ``names`` -- (default: ``None``) unused argument, except if
475
+ ``symbols`` is not provided; it must then be a tuple containing
476
+ the coordinate symbols (this is guaranteed if the shortcut operator
477
+ ``<,>`` is used)
478
+
479
+ If ``names`` is specified, then ``n`` does not have to be specified.
480
+
481
+ EXAMPLES:
482
+
483
+ Constructing a 2-dimensional Euclidean space::
484
+
485
+ sage: E = EuclideanSpace(2); E
486
+ Euclidean plane E^2
487
+
488
+ Each call to :class:`EuclideanSpace` creates a different object::
489
+
490
+ sage: E1 = EuclideanSpace(2)
491
+ sage: E1 is E
492
+ False
493
+ sage: E1 == E
494
+ False
495
+
496
+ The LaTeX symbol of the Euclidean space is by default `\mathbb{E}^n`,
497
+ where `n` is the dimension::
498
+
499
+ sage: latex(E)
500
+ \mathbb{E}^{2}
501
+
502
+ But both the name and LaTeX names of the Euclidean space can be
503
+ customized::
504
+
505
+ sage: F = EuclideanSpace(2, name='F', latex_name=r'\mathcal{F}'); F
506
+ Euclidean plane F
507
+ sage: latex(F)
508
+ \mathcal{F}
509
+
510
+ By default, a Euclidean space is created with a single coordinate chart:
511
+ that of Cartesian coordinates::
512
+
513
+ sage: E.atlas()
514
+ [Chart (E^2, (x, y))]
515
+ sage: E.cartesian_coordinates()
516
+ Chart (E^2, (x, y))
517
+ sage: E.default_chart() is E.cartesian_coordinates()
518
+ True
519
+
520
+ The coordinate variables can be initialized, as the Python variables
521
+ ``x`` and ``y``, by::
522
+
523
+ sage: x, y = E.cartesian_coordinates()[:]
524
+
525
+ However, it is possible to both construct the Euclidean space and
526
+ initialize the coordinate variables in a single stage, thanks to
527
+ SageMath operator ``<,>``::
528
+
529
+ sage: E.<x,y> = EuclideanSpace()
530
+
531
+ Note that providing the dimension as an argument of ``EuclideanSpace`` is
532
+ not necessary in that case, since it can be deduced from the number of
533
+ coordinates within ``<,>``. Besides, the coordinate symbols can be
534
+ customized::
535
+
536
+ sage: E.<X,Y> = EuclideanSpace()
537
+ sage: E.cartesian_coordinates()
538
+ Chart (E^2, (X, Y))
539
+
540
+ By default, the LaTeX symbols of the coordinates coincide with the text
541
+ ones::
542
+
543
+ sage: latex(X+Y)
544
+ X + Y
545
+
546
+ However, it is possible to customize them, via the argument ``symbols``,
547
+ which must be a string, usually prefixed by ``r`` (for *raw* string, in
548
+ order to allow for the backslash character of LaTeX expressions). This
549
+ string contains the coordinate fields separated by a blank space; each
550
+ field contains the coordinate's text symbol and possibly the coordinate's
551
+ LaTeX symbol (when the latter is different from the text symbol), both
552
+ symbols being separated by a colon (``:``)::
553
+
554
+ sage: E.<xi,ze> = EuclideanSpace(symbols=r"xi:\xi ze:\zeta")
555
+ sage: E.cartesian_coordinates()
556
+ Chart (E^2, (xi, ze))
557
+ sage: latex(xi+ze)
558
+ {\xi} + {\zeta}
559
+
560
+ Thanks to the argument ``coordinates``, a Euclidean space can be
561
+ constructed with curvilinear coordinates initialized instead of the
562
+ Cartesian ones::
563
+
564
+ sage: E.<r,ph> = EuclideanSpace(coordinates='polar')
565
+ sage: E.atlas() # no Cartesian coordinates have been constructed
566
+ [Chart (E^2, (r, ph))]
567
+ sage: polar = E.polar_coordinates(); polar
568
+ Chart (E^2, (r, ph))
569
+ sage: E.default_chart() is polar
570
+ True
571
+ sage: latex(r+ph)
572
+ {\phi} + r
573
+
574
+ The Cartesian coordinates, along with the transition maps to and from
575
+ the curvilinear coordinates, can be constructed at any time by::
576
+
577
+ sage: cartesian.<x,y> = E.cartesian_coordinates()
578
+ sage: E.atlas() # both polar and Cartesian coordinates now exist
579
+ [Chart (E^2, (r, ph)), Chart (E^2, (x, y))]
580
+
581
+ The transition maps have been initialized by the command
582
+ ``E.cartesian_coordinates()``::
583
+
584
+ sage: E.coord_change(polar, cartesian).display()
585
+ x = r*cos(ph)
586
+ y = r*sin(ph)
587
+ sage: E.coord_change(cartesian, polar).display()
588
+ r = sqrt(x^2 + y^2)
589
+ ph = arctan2(y, x)
590
+
591
+ The default name of the Euclidean metric tensor is `g`::
592
+
593
+ sage: E.metric()
594
+ Riemannian metric g on the Euclidean plane E^2
595
+ sage: latex(_)
596
+ g
597
+
598
+ But this can be customized::
599
+
600
+ sage: E = EuclideanSpace(2, metric_name='h')
601
+ sage: E.metric()
602
+ Riemannian metric h on the Euclidean plane E^2
603
+ sage: latex(_)
604
+ h
605
+ sage: E = EuclideanSpace(2, metric_latex_name=r'\mathbf{g}')
606
+ sage: E.metric()
607
+ Riemannian metric g on the Euclidean plane E^2
608
+ sage: latex(_)
609
+ \mathbf{g}
610
+
611
+ A 4-dimensional Euclidean space::
612
+
613
+ sage: E = EuclideanSpace(4); E
614
+ 4-dimensional Euclidean space E^4
615
+ sage: latex(E)
616
+ \mathbb{E}^{4}
617
+
618
+ ``E`` is both a real smooth manifold of dimension `4` and a complete metric
619
+ space::
620
+
621
+ sage: E.category()
622
+ Join of Category of smooth manifolds over Real Field with 53 bits of
623
+ precision and Category of connected manifolds over Real Field with
624
+ 53 bits of precision and Category of complete metric spaces
625
+ sage: dim(E)
626
+ 4
627
+
628
+ It is endowed with a default coordinate chart, which is that of
629
+ Cartesian coordinates `(x_1,x_2,x_3,x_4)`::
630
+
631
+ sage: E.atlas()
632
+ [Chart (E^4, (x1, x2, x3, x4))]
633
+ sage: E.default_chart()
634
+ Chart (E^4, (x1, x2, x3, x4))
635
+ sage: E.default_chart() is E.cartesian_coordinates()
636
+ True
637
+
638
+ ``E`` is also endowed with a default metric tensor, which defines the
639
+ Euclidean scalar product::
640
+
641
+ sage: g = E.metric(); g
642
+ Riemannian metric g on the 4-dimensional Euclidean space E^4
643
+ sage: g.display()
644
+ g = dx1⊗dx1 + dx2⊗dx2 + dx3⊗dx3 + dx4⊗dx4
645
+ """
646
+ @staticmethod
647
+ def __classcall_private__(cls, n=None, name=None, latex_name=None,
648
+ coordinates='Cartesian', symbols=None,
649
+ metric_name='g', metric_latex_name=None,
650
+ start_index=1, names=None, unique_tag=None):
651
+ r"""
652
+ Determine the correct class to return based upon the input.
653
+
654
+ TESTS::
655
+
656
+ sage: E2.<x,y> = EuclideanSpace(); E2
657
+ Euclidean plane E^2
658
+ sage: type(E2)
659
+ <class 'sage.manifolds.differentiable.examples.euclidean.EuclideanPlane_with_category'>
660
+
661
+ sage: E3.<r,t,p> = EuclideanSpace(coordinates='spherical'); E3
662
+ Euclidean space E^3
663
+ sage: type(E3)
664
+ <class 'sage.manifolds.differentiable.examples.euclidean.Euclidean3dimSpace_with_category'>
665
+ sage: E3.default_frame()._latex_indices
666
+ (r, {\theta}, {\phi})
667
+ """
668
+ if n is None:
669
+ if names is None:
670
+ raise ValueError("either n or names must be specified")
671
+ n = len(names)
672
+
673
+ # Parse names into symbols
674
+ if names is not None and symbols is None:
675
+ if n == 2:
676
+ names = list(names)
677
+ if coordinates == 'polar':
678
+ if names[1] in ['p', 'ph', 'phi']:
679
+ names[1] += ':\\phi'
680
+ elif names[1] in ['t', 'th', 'theta']:
681
+ names[1] += ':\\theta'
682
+ elif n == 3:
683
+ names = list(names)
684
+ # We add the LaTeX symbols when relevant:
685
+ if coordinates == 'spherical':
686
+ if names[1] in ['t', 'th', 'theta']:
687
+ names[1] = names[1] + ':\\theta'
688
+ if names[2] in ['p', 'ph', 'phi']:
689
+ names[2] = names[2] + ':\\phi'
690
+ elif coordinates == 'cylindrical':
691
+ if names[0] in ['rh', 'rho']:
692
+ names[0] = names[0] + ':\\rho'
693
+ if names[1] in ['p', 'ph', 'phi']:
694
+ names[1] = names[1] + ':\\phi'
695
+
696
+ symbols = ' '.join(names)
697
+
698
+ # Technical bit for UniqueRepresentation
699
+ from time import time
700
+
701
+ from sage.misc.prandom import getrandbits
702
+ if unique_tag is None:
703
+ unique_tag = getrandbits(128) * time()
704
+
705
+ if n == 2:
706
+ return EuclideanPlane(name=name, latex_name=latex_name,
707
+ coordinates=coordinates, symbols=symbols,
708
+ metric_name=metric_name,
709
+ metric_latex_name=metric_latex_name,
710
+ start_index=start_index,
711
+ unique_tag=unique_tag)
712
+ if n == 3:
713
+ return Euclidean3dimSpace(name=name, latex_name=latex_name,
714
+ coordinates=coordinates, symbols=symbols,
715
+ metric_name=metric_name,
716
+ metric_latex_name=metric_latex_name,
717
+ start_index=start_index,
718
+ unique_tag=unique_tag)
719
+
720
+ return super().__classcall__(cls,
721
+ n, name=name, latex_name=latex_name,
722
+ coordinates=coordinates, symbols=symbols,
723
+ metric_name=metric_name,
724
+ metric_latex_name=metric_latex_name,
725
+ start_index=start_index,
726
+ unique_tag=unique_tag)
727
+
728
+ def __init__(self, n, name=None, latex_name=None,
729
+ coordinates='Cartesian', symbols=None, metric_name='g',
730
+ metric_latex_name=None, start_index=1, base_manifold=None,
731
+ category=None, init_coord_methods=None,
732
+ unique_tag=None):
733
+ r"""
734
+ Construct a Euclidean space.
735
+
736
+ INPUT:
737
+
738
+ This class also takes the following input:
739
+
740
+ - ``base_manifold`` -- (default: ``None``) if not ``None``, must be
741
+ a Euclidean space; the created object is then an open subset
742
+ of ``base_manifold``
743
+ - ``category`` -- (default: ``None``) to specify the category;
744
+ if ``None``,
745
+ ``Manifolds(RR).Smooth() & MetricSpaces().Complete()`` is assumed
746
+ - ``init_coord_methods`` -- (default: ``None``) dictionary of
747
+ methods to initialize the various type of coordinates, with each
748
+ key being a string describing the type of coordinates; to be
749
+ used by derived classes only
750
+ - ``unique_tag`` -- (default: ``None``) tag used to force the
751
+ construction of a new object when all the other arguments have
752
+ been used previously (without ``unique_tag``, the
753
+ :class:`~sage.structure.unique_representation.UniqueRepresentation`
754
+ behavior inherited from
755
+ :class:`~sage.manifolds.differentiable.pseudo_riemannian.PseudoRiemannianManifold`
756
+ would return the previously constructed object corresponding
757
+ to these arguments)
758
+
759
+ TESTS::
760
+
761
+ sage: E = EuclideanSpace(4); E
762
+ 4-dimensional Euclidean space E^4
763
+ sage: E.metric()
764
+ Riemannian metric g on the 4-dimensional Euclidean space E^4
765
+ sage: TestSuite(E).run()
766
+ """
767
+ if name is None:
768
+ name = 'E^{}'.format(n)
769
+ if latex_name is None:
770
+ latex_name = r'\mathbb{E}^{' + str(n) + '}'
771
+ if category is None:
772
+ category = Manifolds(RR).Smooth().Connected() & MetricSpaces().Complete()
773
+ # NB: RR is a proxy for the field of real numbers, until
774
+ # Issue #24456 is ready
775
+ PseudoRiemannianManifold.__init__(self, n, name, metric_name=metric_name,
776
+ signature=n, base_manifold=base_manifold,
777
+ latex_name=latex_name,
778
+ metric_latex_name=metric_latex_name,
779
+ start_index=start_index,
780
+ category=category)
781
+ if symbols is None:
782
+ if n == 1:
783
+ if coordinates == 'Cartesian':
784
+ symbols = 'x'
785
+ else:
786
+ raise TypeError("unknown coordinate type")
787
+ elif n > 3:
788
+ if coordinates == 'Cartesian':
789
+ symbols = ''
790
+ for i in self.irange():
791
+ symbols += "x{}".format(i) + r":x_{" + str(i) + r"} "
792
+ symbols = symbols[:-1]
793
+ else:
794
+ raise TypeError("unknown coordinate type")
795
+ else:
796
+ raise NotImplementedError("dimension not implemented yet")
797
+ self._cartesian_chart = None # to be constructed later if necessary
798
+ if init_coord_methods is None:
799
+ self._init_coordinates = {'Cartesian': self._init_cartesian}
800
+ else:
801
+ self._init_coordinates = init_coord_methods
802
+ self._init_coordinates[coordinates](symbols)
803
+
804
+ def _repr_(self):
805
+ r"""
806
+ Return a string representation of ``self``.
807
+
808
+ TESTS::
809
+
810
+ sage: E = EuclideanSpace(4)
811
+ sage: E._repr_()
812
+ '4-dimensional Euclidean space E^4'
813
+ sage: E # indirect doctest
814
+ 4-dimensional Euclidean space E^4
815
+ """
816
+ return "{}-dimensional Euclidean space {}".format(self._dim, self._name)
817
+
818
+ def _first_ngens(self, n):
819
+ r"""
820
+ Return the list of coordinates of the default chart.
821
+
822
+ This is useful only for the use of Sage preparser::
823
+
824
+ sage: preparse("E.<x,y,z> = EuclideanSpace()")
825
+ "E = EuclideanSpace(names=('x', 'y', 'z',));
826
+ (x, y, z,) = E._first_ngens(3)"
827
+
828
+ TESTS::
829
+
830
+ sage: E = EuclideanSpace(2)
831
+ sage: E._first_ngens(2)
832
+ (x, y)
833
+ sage: E.<u,v> = EuclideanSpace()
834
+ sage: E._first_ngens(2)
835
+ (u, v)
836
+ """
837
+ return self._def_chart[:]
838
+
839
+ def _init_cartesian(self, symbols):
840
+ r"""
841
+ Construct the chart of Cartesian coordinates and initialize the
842
+ components of the metric tensor in it.
843
+
844
+ TESTS::
845
+
846
+ sage: E = EuclideanSpace(2)
847
+ sage: E._init_cartesian('x y')
848
+ """
849
+ chart = self.chart(coordinates=symbols)
850
+ self._cartesian_chart = chart
851
+ frame = chart.frame()
852
+ # Renaming (∂/∂x, ∂/∂y, ...) to (e_x, e_y, ...):
853
+ coords = chart[:]
854
+ frame.set_name('e',
855
+ indices=tuple(str(x) for x in coords),
856
+ latex_indices=tuple(latex(x) for x in coords))
857
+ g = self.metric()
858
+ gc = g.add_comp(frame)
859
+ for i in self.irange():
860
+ gc[[i, i]] = self.one_scalar_field()
861
+ nabla = g.connection(init_coef=False) # False to avoid any computation
862
+ nabla.add_coef(frame) # initialize a zero set of coefficients
863
+
864
+ def cartesian_coordinates(self, symbols=None, names=None):
865
+ r"""
866
+ Return the chart of Cartesian coordinates, possibly creating it if it
867
+ does not already exist.
868
+
869
+ INPUT:
870
+
871
+ - ``symbols`` -- (default: ``None``) string defining the coordinate
872
+ text symbols and LaTeX symbols, with the same conventions as
873
+ the argument ``coordinates`` in
874
+ :class:`~sage.manifolds.differentiable.chart.RealDiffChart`; this is
875
+ used only if the Cartesian chart has not been already defined; if
876
+ ``None`` the symbols are generated as `(x_1,\ldots,x_n)`.
877
+ - ``names`` -- (default: ``None``) unused argument, except if
878
+ ``symbols`` is not provided; it must be a tuple containing
879
+ the coordinate symbols (this is guaranteed if the shortcut operator
880
+ ``<,>`` is used)
881
+
882
+ OUTPUT:
883
+
884
+ - the chart of Cartesian coordinates, as an instance of
885
+ :class:`~sage.manifolds.differentiable.chart.RealDiffChart`
886
+
887
+ EXAMPLES::
888
+
889
+ sage: E = EuclideanSpace(4)
890
+ sage: X = E.cartesian_coordinates(); X
891
+ Chart (E^4, (x1, x2, x3, x4))
892
+ sage: X.coord_range()
893
+ x1: (-oo, +oo); x2: (-oo, +oo); x3: (-oo, +oo); x4: (-oo, +oo)
894
+ sage: X[2]
895
+ x2
896
+ sage: X[:]
897
+ (x1, x2, x3, x4)
898
+ sage: latex(X[:])
899
+ \left({x_{1}}, {x_{2}}, {x_{3}}, {x_{4}}\right)
900
+ """
901
+ if self._cartesian_chart is None:
902
+ if symbols is None:
903
+ symbols = ''
904
+ if names is None:
905
+ for i in self.irange():
906
+ symbols += "x{}".format(i) + r":x_{" + str(i) + r"} "
907
+ else:
908
+ for x in names:
909
+ symbols += x + ' '
910
+ symbols = symbols[:-1]
911
+ self._init_cartesian(symbols)
912
+ return self._cartesian_chart
913
+
914
+ def cartesian_frame(self):
915
+ r"""
916
+ Return the orthonormal vector frame associated with Cartesian
917
+ coordinates.
918
+
919
+ OUTPUT: :class:`~sage.manifolds.differentiable.vectorframe.CoordFrame`
920
+
921
+ EXAMPLES::
922
+
923
+ sage: E = EuclideanSpace(2)
924
+ sage: E.cartesian_frame()
925
+ Coordinate frame (E^2, (e_x,e_y))
926
+ sage: E.cartesian_frame()[1]
927
+ Vector field e_x on the Euclidean plane E^2
928
+ sage: E.cartesian_frame()[:]
929
+ (Vector field e_x on the Euclidean plane E^2,
930
+ Vector field e_y on the Euclidean plane E^2)
931
+
932
+ For Cartesian coordinates, the orthonormal frame coincides with the
933
+ coordinate frame::
934
+
935
+ sage: E.cartesian_frame() is E.cartesian_coordinates().frame()
936
+ True
937
+ """
938
+ if self._cartesian_chart is None:
939
+ self.cartesian_coordinates() # creates the Cartesian chart
940
+ # Since the coordinate frame of Cartesian coordinates is orthonormal,
941
+ # we simply return this frame:
942
+ return self._cartesian_chart.frame()
943
+
944
+ def dist(self, p, q):
945
+ r"""
946
+ Euclidean distance between two points.
947
+
948
+ INPUT:
949
+
950
+ - ``p`` -- an element of ``self``
951
+ - ``q`` -- an element of ``self``
952
+
953
+ OUTPUT:
954
+
955
+ - the Euclidean distance `d(p, q)`
956
+
957
+ EXAMPLES::
958
+
959
+ sage: E.<x,y> = EuclideanSpace()
960
+ sage: p = E((1,0))
961
+ sage: q = E((0,2))
962
+ sage: E.dist(p, q)
963
+ sqrt(5)
964
+ sage: p.dist(q) # indirect doctest
965
+ sqrt(5)
966
+ """
967
+ chart = self.cartesian_coordinates()
968
+ coords_p = chart(p)
969
+ coords_q = chart(q)
970
+ d2 = 0
971
+ for xp, xq in zip(coords_p, coords_q):
972
+ dx = xp - xq
973
+ d2 += dx*dx
974
+ return sqrt(d2)
975
+
976
+ def sphere(self, radius=1, center=None, name=None, latex_name=None,
977
+ coordinates='spherical', names=None):
978
+ r"""
979
+ Return an `(n-1)`-sphere smoothly embedded in ``self``.
980
+
981
+ INPUT:
982
+
983
+ - ``radius`` -- (default: ``1``) the radius greater than 1 of the sphere
984
+ - ``center`` -- (default: ``None``) point on ``self`` representing the
985
+ barycenter of the sphere
986
+ - ``name`` -- (default: ``None``) string; name (symbol) given to the
987
+ sphere; if ``None``, the name will be generated according to the input
988
+ - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote
989
+ the sphere; if ``None``, the symbol will be generated according to
990
+ the input
991
+ - ``coordinates`` -- (default: ``'spherical'``) string describing the
992
+ type of coordinates to be initialized at the sphere's creation;
993
+ allowed values are
994
+
995
+ - ``'spherical'`` spherical coordinates (see
996
+ :meth:`~sage.manifolds.differentiable.examples.sphere.Sphere.spherical_coordinates`))
997
+ - ``'stereographic'`` stereographic coordinates given by the
998
+ stereographic projection (see
999
+ :meth:`~sage.manifolds.differentiable.examples.sphere.Sphere.stereographic_coordinates`)
1000
+
1001
+ - ``names`` -- (default: ``None``) must be a tuple containing
1002
+ the coordinate symbols (this guarantees the shortcut operator
1003
+ ``<,>`` to function); if ``None``, the usual conventions are used (see
1004
+ examples in
1005
+ :class:`~sage.manifolds.differentiable.examples.sphere.Sphere`
1006
+ for details)
1007
+
1008
+ EXAMPLES:
1009
+
1010
+ Define a 2-sphere with radius 2 centered at `(1,2,3)` in Cartesian
1011
+ coordinates::
1012
+
1013
+ sage: E3 = EuclideanSpace(3)
1014
+ sage: c = E3.point((1,2,3), name='c'); c
1015
+ Point c on the Euclidean space E^3
1016
+ sage: S2_2 = E3.sphere(radius=2, center=c); S2_2
1017
+ 2-sphere S^2_2(c) of radius 2 smoothly embedded in the Euclidean
1018
+ space E^3 centered at the Point c
1019
+
1020
+ The ambient space is precisely our previously defined Euclidean space::
1021
+
1022
+ sage: S2_2.ambient() is E3
1023
+ True
1024
+
1025
+ The embedding into Euclidean space::
1026
+
1027
+ sage: S2_2.embedding().display()
1028
+ iota: S^2_2(c) → E^3
1029
+ on A: (theta, phi) ↦ (x, y, z) = (2*cos(phi)*sin(theta) + 1,
1030
+ 2*sin(phi)*sin(theta) + 2,
1031
+ 2*cos(theta) + 3)
1032
+
1033
+ See :class:`~sage.manifolds.differentiable.examples.sphere.Sphere`
1034
+ for more examples.
1035
+ """
1036
+ n = self._dim
1037
+ if n == 1:
1038
+ raise ValueError('Euclidean space must have dimension of at least 2')
1039
+ from sage.manifolds.differentiable.examples.sphere import Sphere
1040
+ return Sphere(n-1, radius=radius, ambient_space=self,
1041
+ center=center, name=name, latex_name=latex_name,
1042
+ coordinates=coordinates, names=names)
1043
+
1044
+ ###############################################################################
1045
+
1046
+
1047
+ class EuclideanPlane(EuclideanSpace):
1048
+ r"""
1049
+ Euclidean plane.
1050
+
1051
+ A *Euclidean plane* is an affine space `E`, whose associated vector space
1052
+ is a 2-dimensional vector space over `\RR` and is equipped with a
1053
+ positive definite symmetric bilinear form, called the *scalar product* or
1054
+ *dot product*.
1055
+
1056
+ The class :class:`EuclideanPlane` inherits from
1057
+ :class:`~sage.manifolds.differentiable.pseudo_riemannian.PseudoRiemannianManifold`
1058
+ (via :class:`EuclideanSpace`) since a Euclidean plane can be viewed
1059
+ as a Riemannian manifold that is diffeomorphic to `\RR^2` and that has a
1060
+ flat metric `g`. The Euclidean scalar product is the one defined by the
1061
+ Riemannian metric `g`.
1062
+
1063
+ INPUT:
1064
+
1065
+ - ``name`` -- (default: ``None``) string; name (symbol) given to the
1066
+ Euclidean plane; if ``None``, the name will be set to ``'E^2'``
1067
+ - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the
1068
+ Euclidean plane; if ``None``, it is set to ``'\mathbb{E}^{2}'`` if
1069
+ ``name`` is ``None`` and to ``name`` otherwise
1070
+ - ``coordinates`` -- (default: ``'Cartesian'``) string describing the type
1071
+ of coordinates to be initialized at the Euclidean plane creation;
1072
+ allowed values are ``'Cartesian'`` (see :meth:`cartesian_coordinates`)
1073
+ and ``'polar'`` (see :meth:`polar_coordinates`)
1074
+ - ``symbols`` -- (default: ``None``) string defining the coordinate text
1075
+ symbols and LaTeX symbols, with the same conventions as the argument
1076
+ ``coordinates`` in
1077
+ :class:`~sage.manifolds.differentiable.chart.RealDiffChart`, namely
1078
+ ``symbols`` is a string of coordinate fields separated by a blank space,
1079
+ where each field contains the coordinate's text symbol and possibly the
1080
+ coordinate's LaTeX symbol (when the latter is different from the text
1081
+ symbol), both symbols being separated by a colon (``:``); if ``None``,
1082
+ the symbols will be automatically generated according to the value of
1083
+ ``coordinates``
1084
+ - ``metric_name`` -- (default: ``'g'``) string; name (symbol) given to the
1085
+ Euclidean metric tensor
1086
+ - ``metric_latex_name`` -- (default: ``None``) string; LaTeX symbol to
1087
+ denote the Euclidean metric tensor; if none is provided, it is set to
1088
+ ``metric_name``
1089
+ - ``start_index`` -- (default: 1) integer; lower value of the range of
1090
+ indices used for "indexed objects" in the Euclidean plane, e.g.
1091
+ coordinates of a chart
1092
+ - ``base_manifold`` -- (default: ``None``) if not ``None``, must be an
1093
+ Euclidean plane; the created object is then an open subset of ``base_manifold``
1094
+ - ``category`` -- (default: ``None``) to specify the category; if ``None``,
1095
+ ``Manifolds(RR).Smooth() & MetricSpaces().Complete()`` is assumed
1096
+ - ``names`` -- (default: ``None``) unused argument, except if
1097
+ ``symbols`` is not provided; it must then be a tuple containing
1098
+ the coordinate symbols (this is guaranteed if the shortcut operator
1099
+ ``<,>`` is used)
1100
+ - ``init_coord_methods`` -- (default: ``None``) dictionary of methods
1101
+ to initialize the various type of coordinates, with each key being a
1102
+ string describing the type of coordinates; to be used by derived classes
1103
+ only
1104
+ - ``unique_tag`` -- (default: ``None``) tag used to force the construction
1105
+ of a new object when all the other arguments have been used previously
1106
+ (without ``unique_tag``, the
1107
+ :class:`~sage.structure.unique_representation.UniqueRepresentation`
1108
+ behavior inherited from
1109
+ :class:`~sage.manifolds.differentiable.pseudo_riemannian.PseudoRiemannianManifold`
1110
+ would return the previously constructed object corresponding to these
1111
+ arguments)
1112
+
1113
+ EXAMPLES:
1114
+
1115
+ One creates a Euclidean plane ``E`` with::
1116
+
1117
+ sage: E.<x,y> = EuclideanSpace(); E
1118
+ Euclidean plane E^2
1119
+
1120
+ ``E`` is both a real smooth manifold of dimension `2` and a complete metric
1121
+ space::
1122
+
1123
+ sage: E.category()
1124
+ Join of Category of smooth manifolds over Real Field with 53 bits of
1125
+ precision and Category of connected manifolds over Real Field with
1126
+ 53 bits of precision and Category of complete metric spaces
1127
+ sage: dim(E)
1128
+ 2
1129
+
1130
+ It is endowed with a default coordinate chart, which is that
1131
+ of Cartesian coordinates `(x,y)`::
1132
+
1133
+ sage: E.atlas()
1134
+ [Chart (E^2, (x, y))]
1135
+ sage: E.default_chart()
1136
+ Chart (E^2, (x, y))
1137
+ sage: cartesian = E.cartesian_coordinates()
1138
+ sage: cartesian is E.default_chart()
1139
+ True
1140
+
1141
+ A point of ``E``::
1142
+
1143
+ sage: p = E((3,-2)); p
1144
+ Point on the Euclidean plane E^2
1145
+ sage: cartesian(p)
1146
+ (3, -2)
1147
+ sage: p in E
1148
+ True
1149
+ sage: p.parent() is E
1150
+ True
1151
+
1152
+ ``E`` is endowed with a default metric tensor, which defines the
1153
+ Euclidean scalar product::
1154
+
1155
+ sage: g = E.metric(); g
1156
+ Riemannian metric g on the Euclidean plane E^2
1157
+ sage: g.display()
1158
+ g = dx⊗dx + dy⊗dy
1159
+
1160
+ Curvilinear coordinates can be introduced on ``E``: see
1161
+ :meth:`polar_coordinates`.
1162
+
1163
+ .. SEEALSO::
1164
+
1165
+ :ref:`EuclideanSpace_example1`
1166
+ """
1167
+ def __init__(self, name=None, latex_name=None, coordinates='Cartesian',
1168
+ symbols=None, metric_name='g', metric_latex_name=None,
1169
+ start_index=1, base_manifold=None, category=None, unique_tag=None):
1170
+ r"""
1171
+ Construct a Euclidean plane.
1172
+
1173
+ TESTS::
1174
+
1175
+ sage: E = EuclideanSpace(2); E
1176
+ Euclidean plane E^2
1177
+ sage: E.metric()
1178
+ Riemannian metric g on the Euclidean plane E^2
1179
+ sage: TestSuite(E).run()
1180
+ """
1181
+ if coordinates not in ['Cartesian', 'polar']:
1182
+ raise TypeError("unknown coordinate type")
1183
+ if symbols is None:
1184
+ if coordinates == 'Cartesian':
1185
+ symbols = 'x y'
1186
+ elif coordinates == 'polar':
1187
+ symbols = 'r ph:\\phi'
1188
+ self._polar_chart = None # to be constructed later if necessary
1189
+ self._polar_frame = None # orthonormal frame associated to polar coord
1190
+ init_coord_methods = {'Cartesian': self._init_cartesian,
1191
+ 'polar': self._init_polar}
1192
+ EuclideanSpace.__init__(self, 2, name=name,
1193
+ latex_name=latex_name,
1194
+ coordinates=coordinates,
1195
+ symbols=symbols,
1196
+ metric_name=metric_name,
1197
+ metric_latex_name=metric_latex_name,
1198
+ start_index=start_index,
1199
+ base_manifold=base_manifold, category=category,
1200
+ init_coord_methods=init_coord_methods)
1201
+ if coordinates == 'polar':
1202
+ # The default frame is the polar coordinate frame; we change it
1203
+ # to the orthonormal polar frame
1204
+ self.set_default_frame(self.polar_frame())
1205
+
1206
+ def _repr_(self):
1207
+ r"""
1208
+ Return a string representation of ``self``.
1209
+
1210
+ TESTS::
1211
+
1212
+ sage: E = EuclideanSpace(2)
1213
+ sage: E._repr_()
1214
+ 'Euclidean plane E^2'
1215
+ sage: E # indirect doctest
1216
+ Euclidean plane E^2
1217
+ sage: E = EuclideanSpace(2, name='E')
1218
+ sage: E._repr_()
1219
+ 'Euclidean plane E'
1220
+ """
1221
+ return "Euclidean plane {}".format(self._name)
1222
+
1223
+ def _init_polar(self, symbols):
1224
+ r"""
1225
+ Construct the chart of polar coordinates and initialize the
1226
+ components of the metric tensor in it.
1227
+
1228
+ TESTS::
1229
+
1230
+ sage: E = EuclideanSpace(2)
1231
+ sage: E.atlas()
1232
+ [Chart (E^2, (x, y))]
1233
+ sage: E._init_polar(r"R Phi:\Phi")
1234
+ sage: E.atlas()
1235
+ [Chart (E^2, (x, y)), Chart (E^2, (R, Phi))]
1236
+ """
1237
+ coords = symbols.split() # list of strings, one per coordinate
1238
+ # Adding the coordinate ranges:
1239
+ coordinates = (coords[0] + ':(0,+oo) ' + coords[1]
1240
+ + ':(0,2*pi):periodic')
1241
+ chart = self.chart(coordinates=coordinates)
1242
+ self._polar_chart = chart
1243
+ frame = chart.frame()
1244
+ # Initialization of the metric components and the associated
1245
+ # Christoffel symbols
1246
+ g = self.metric()
1247
+ gc = g.add_comp(frame)
1248
+ i1 = self._sindex
1249
+ i2 = i1 + 1
1250
+ r, ph = chart[:]
1251
+ gc[i1, i1, chart] = 1
1252
+ gc[i2, i2, chart] = r**2
1253
+ # Orthonormal frame associated with polar coordinates:
1254
+ to_orthonormal = self.automorphism_field()
1255
+ to_orthonormal[frame, i1, i1, chart] = 1
1256
+ to_orthonormal[frame, i2, i2, chart] = 1 / r
1257
+ oframe = frame.new_frame(to_orthonormal, 'e',
1258
+ indices=(str(r), str(ph)),
1259
+ latex_indices=(latex(r), latex(ph)))
1260
+ self._polar_frame = oframe
1261
+ g.comp(oframe)
1262
+
1263
+ def _transition_polar_cartesian(self):
1264
+ r"""
1265
+ Transitions between polar and Cartesian coordinates.
1266
+
1267
+ TESTS::
1268
+
1269
+ sage: E = EuclideanSpace(2)
1270
+ sage: E._init_polar(r"r ph:\phi")
1271
+ sage: E.atlas()
1272
+ [Chart (E^2, (x, y)), Chart (E^2, (r, ph))]
1273
+ sage: E.coord_changes() # no transition map has been defined yet
1274
+ {}
1275
+ sage: E._transition_polar_cartesian()
1276
+ sage: len(E.coord_changes()) # 2 transition maps have been created
1277
+ 2
1278
+
1279
+ Tests of the change-of-frame formulas::
1280
+
1281
+ sage: polar = E.polar_coordinates()
1282
+ sage: polar_f = E.polar_frame()
1283
+ sage: cart_f = E.cartesian_frame()
1284
+ sage: E.change_of_frame(cart_f, polar_f)[:, polar]
1285
+ [ cos(ph) -sin(ph)]
1286
+ [ sin(ph) cos(ph)]
1287
+ sage: E.change_of_frame(polar_f, cart_f)[:, polar]
1288
+ [ cos(ph) sin(ph)]
1289
+ [-sin(ph) cos(ph)]
1290
+ """
1291
+ # Transition maps polar chart <-> Cartesian chart
1292
+ chart_cart = self._cartesian_chart
1293
+ chart_pol = self._polar_chart
1294
+ x, y = chart_cart[:]
1295
+ r, ph = chart_pol[:]
1296
+ pol_to_cart = chart_pol.transition_map(chart_cart,
1297
+ [r*cos(ph), r*sin(ph)])
1298
+ pol_to_cart.set_inverse(sqrt(x**2+y**2), atan2(y,x), check=False)
1299
+ # Automorphism Cartesian frame → orthonormal polar frame:
1300
+ oframe = self._polar_frame
1301
+ cframe = chart_cart.frame()
1302
+ sframe = chart_pol.frame()
1303
+ chg = self._frame_changes
1304
+ cframe_to_oframe = chg[(sframe, oframe)] * chg[(cframe, sframe)]
1305
+ # cframe_to_oframe has been computed only in sframe;
1306
+ # its components in oframe are computed by
1307
+ cmp_of = cframe_to_oframe.comp(oframe)
1308
+ # while its components in cframe are obtained by identifying the
1309
+ # matrices in cframe and oframe:
1310
+ cmp_cf = cframe_to_oframe.add_comp(cframe)
1311
+ for i in self.irange():
1312
+ for j in self.irange():
1313
+ cmp_cf[[i,j]] = cmp_of[[i,j]]
1314
+ # Automorphism orthonormal polar frame → Cartesian frame:
1315
+ oframe_to_cframe = chg[(sframe, cframe)] * chg[(oframe, sframe)]
1316
+ # oframe_to_cframe has been computed only in sframe;
1317
+ # its components in oframe are computed by
1318
+ cmp_of = oframe_to_cframe.comp(oframe)
1319
+ # while its components in cframe are obtained by identifying the
1320
+ # matrices in cframe and oframe:
1321
+ cmp_cf = oframe_to_cframe.add_comp(cframe)
1322
+ for i in self.irange():
1323
+ for j in self.irange():
1324
+ cmp_cf[[i,j]] = cmp_of[[i,j]]
1325
+ # Storage of the results:
1326
+ chg[(cframe, oframe)] = cframe_to_oframe
1327
+ chg[(oframe, cframe)] = oframe_to_cframe
1328
+ vmodule = self.vector_field_module()
1329
+ vmodule.set_change_of_basis(cframe, oframe, cframe_to_oframe,
1330
+ compute_inverse=False)
1331
+ vmodule.set_change_of_basis(oframe, cframe, oframe_to_cframe,
1332
+ compute_inverse=False)
1333
+
1334
+ def cartesian_coordinates(self, symbols=None, names=None):
1335
+ r"""
1336
+ Return the chart of Cartesian coordinates, possibly creating it if it
1337
+ does not already exist.
1338
+
1339
+ INPUT:
1340
+
1341
+ - ``symbols`` -- (default: ``None``) string defining the coordinate
1342
+ text symbols and LaTeX symbols, with the same conventions as the
1343
+ argument ``coordinates`` in
1344
+ :class:`~sage.manifolds.differentiable.chart.RealDiffChart`; this is
1345
+ used only if the Cartesian chart has not been already defined; if
1346
+ ``None`` the symbols are generated as `(x,y)`.
1347
+ - ``names`` -- (default: ``None``) unused argument, except if
1348
+ ``symbols`` is not provided; it must be a tuple containing
1349
+ the coordinate symbols (this is guaranteed if the shortcut operator
1350
+ ``<,>`` is used)
1351
+
1352
+ OUTPUT:
1353
+
1354
+ - the chart of Cartesian coordinates, as an instance of
1355
+ :class:`~sage.manifolds.differentiable.chart.RealDiffChart`
1356
+
1357
+ EXAMPLES::
1358
+
1359
+ sage: E = EuclideanSpace(2)
1360
+ sage: E.cartesian_coordinates()
1361
+ Chart (E^2, (x, y))
1362
+ sage: E.cartesian_coordinates().coord_range()
1363
+ x: (-oo, +oo); y: (-oo, +oo)
1364
+
1365
+ An example where the Cartesian coordinates have not been previously
1366
+ created::
1367
+
1368
+ sage: E = EuclideanSpace(2, coordinates='polar')
1369
+ sage: E.atlas() # only polar coordinates have been initialized
1370
+ [Chart (E^2, (r, ph))]
1371
+ sage: E.cartesian_coordinates(symbols='X Y')
1372
+ Chart (E^2, (X, Y))
1373
+ sage: E.atlas() # the Cartesian chart has been added to the atlas
1374
+ [Chart (E^2, (r, ph)), Chart (E^2, (X, Y))]
1375
+
1376
+ Note that if the Cartesian coordinates have been already initialized,
1377
+ the argument ``symbols`` has no effect::
1378
+
1379
+ sage: E.cartesian_coordinates(symbols='x y')
1380
+ Chart (E^2, (X, Y))
1381
+
1382
+ The coordinate variables are returned by the square bracket operator::
1383
+
1384
+ sage: E.cartesian_coordinates()[1]
1385
+ X
1386
+ sage: E.cartesian_coordinates()[2]
1387
+ Y
1388
+ sage: E.cartesian_coordinates()[:]
1389
+ (X, Y)
1390
+
1391
+ It is also possible to use the operator ``<,>`` to set symbolic
1392
+ variable containing the coordinates::
1393
+
1394
+ sage: E = EuclideanSpace(2, coordinates='polar')
1395
+ sage: cartesian.<u,v> = E.cartesian_coordinates()
1396
+ sage: cartesian
1397
+ Chart (E^2, (u, v))
1398
+ sage: u,v
1399
+ (u, v)
1400
+
1401
+ The command ``cartesian.<u,v> = E.cartesian_coordinates()`` is
1402
+ actually a shortcut for::
1403
+
1404
+ sage: cartesian = E.cartesian_coordinates(symbols='u v')
1405
+ sage: u, v = cartesian[:]
1406
+ """
1407
+ if self._cartesian_chart is None:
1408
+ if symbols is None:
1409
+ if names is None:
1410
+ symbols = 'x y'
1411
+ else:
1412
+ symbols = names[0] + ' ' + names[1]
1413
+ self._init_cartesian(symbols)
1414
+ if self._polar_chart:
1415
+ self._transition_polar_cartesian()
1416
+ return self._cartesian_chart
1417
+
1418
+ def polar_coordinates(self, symbols=None, names=None):
1419
+ r"""
1420
+ Return the chart of polar coordinates, possibly creating it if it
1421
+ does not already exist.
1422
+
1423
+ INPUT:
1424
+
1425
+ - ``symbols`` -- (default: ``None``) string defining the coordinate
1426
+ text symbols and LaTeX symbols, with the same conventions as the
1427
+ argument ``coordinates`` in
1428
+ :class:`~sage.manifolds.differentiable.chart.RealDiffChart`; this is
1429
+ used only if the polar chart has not been already defined; if
1430
+ ``None`` the symbols are generated as `(r,\phi)`.
1431
+ - ``names`` -- (default: ``None``) unused argument, except if
1432
+ ``symbols`` is not provided; it must be a tuple containing
1433
+ the coordinate symbols (this is guaranteed if the shortcut operator
1434
+ ``<,>`` is used)
1435
+
1436
+ OUTPUT:
1437
+
1438
+ - the chart of polar coordinates, as an instance of
1439
+ :class:`~sage.manifolds.differentiable.chart.RealDiffChart`
1440
+
1441
+ EXAMPLES::
1442
+
1443
+ sage: E = EuclideanSpace(2)
1444
+ sage: E.polar_coordinates()
1445
+ Chart (E^2, (r, ph))
1446
+ sage: latex(_)
1447
+ \left(\mathbb{E}^{2},(r, {\phi})\right)
1448
+ sage: E.polar_coordinates().coord_range()
1449
+ r: (0, +oo); ph: [0, 2*pi] (periodic)
1450
+
1451
+ The relation to Cartesian coordinates is::
1452
+
1453
+ sage: E.coord_change(E.polar_coordinates(),
1454
+ ....: E.cartesian_coordinates()).display()
1455
+ x = r*cos(ph)
1456
+ y = r*sin(ph)
1457
+ sage: E.coord_change(E.cartesian_coordinates(),
1458
+ ....: E.polar_coordinates()).display()
1459
+ r = sqrt(x^2 + y^2)
1460
+ ph = arctan2(y, x)
1461
+
1462
+ The coordinate variables are returned by the square bracket operator::
1463
+
1464
+ sage: E.polar_coordinates()[1]
1465
+ r
1466
+ sage: E.polar_coordinates()[2]
1467
+ ph
1468
+ sage: E.polar_coordinates()[:]
1469
+ (r, ph)
1470
+
1471
+ They can also be obtained via the operator ``<,>``::
1472
+
1473
+ sage: polar.<r,ph> = E.polar_coordinates(); polar
1474
+ Chart (E^2, (r, ph))
1475
+ sage: r, ph
1476
+ (r, ph)
1477
+
1478
+ Actually, ``polar.<r,ph> = E.polar_coordinates()`` is a shortcut for::
1479
+
1480
+ sage: polar = E.polar_coordinates()
1481
+ sage: r, ph = polar[:]
1482
+
1483
+ The coordinate symbols can be customized::
1484
+
1485
+ sage: E = EuclideanSpace(2)
1486
+ sage: E.polar_coordinates(symbols=r"r th:\theta")
1487
+ Chart (E^2, (r, th))
1488
+ sage: latex(E.polar_coordinates())
1489
+ \left(\mathbb{E}^{2},(r, {\theta})\right)
1490
+
1491
+ Note that if the polar coordinates have been already initialized, the
1492
+ argument ``symbols`` has no effect::
1493
+
1494
+ sage: E.polar_coordinates(symbols=r"R Th:\Theta")
1495
+ Chart (E^2, (r, th))
1496
+ """
1497
+ if self._polar_chart is None:
1498
+ if symbols is None:
1499
+ if names is None:
1500
+ symbols = 'r ph:\\phi'
1501
+ else:
1502
+ symbols = names[0] + ' ' + names[1]
1503
+ if names[1] in ['p', 'ph', 'phi']:
1504
+ symbols += ':\\phi'
1505
+ elif names[1] in ['t', 'th', 'theta']:
1506
+ symbols += ':\\theta'
1507
+ self._init_polar(symbols)
1508
+ if self._cartesian_chart:
1509
+ self._transition_polar_cartesian()
1510
+ return self._polar_chart
1511
+
1512
+ def polar_frame(self):
1513
+ r"""
1514
+ Return the orthonormal vector frame associated with polar
1515
+ coordinates.
1516
+
1517
+ OUTPUT:
1518
+
1519
+ - instance of
1520
+ :class:`~sage.manifolds.differentiable.vectorframe.VectorFrame`
1521
+
1522
+ EXAMPLES::
1523
+
1524
+ sage: E = EuclideanSpace(2)
1525
+ sage: E.polar_frame()
1526
+ Vector frame (E^2, (e_r,e_ph))
1527
+ sage: E.polar_frame()[1]
1528
+ Vector field e_r on the Euclidean plane E^2
1529
+ sage: E.polar_frame()[:]
1530
+ (Vector field e_r on the Euclidean plane E^2,
1531
+ Vector field e_ph on the Euclidean plane E^2)
1532
+
1533
+ The orthonormal polar frame expressed in terms of the Cartesian one::
1534
+
1535
+ sage: for e in E.polar_frame():
1536
+ ....: e.display(E.cartesian_frame(), E.polar_coordinates())
1537
+ e_r = cos(ph) e_x + sin(ph) e_y
1538
+ e_ph = -sin(ph) e_x + cos(ph) e_y
1539
+
1540
+ The orthonormal frame `(e_r, e_\phi)` expressed in terms of the
1541
+ coordinate frame
1542
+ `\left(\frac{\partial}{\partial r},
1543
+ \frac{\partial}{\partial\phi}\right)`::
1544
+
1545
+ sage: for e in E.polar_frame():
1546
+ ....: e.display(E.polar_coordinates())
1547
+ e_r = ∂/∂r
1548
+ e_ph = 1/r ∂/∂ph
1549
+ """
1550
+ if self._polar_frame is None:
1551
+ # create the polar chart and the associated orthonormal frame
1552
+ self.polar_coordinates()
1553
+ return self._polar_frame
1554
+
1555
+
1556
+ ###############################################################################
1557
+
1558
+ class Euclidean3dimSpace(EuclideanSpace):
1559
+ r"""
1560
+ 3-dimensional Euclidean space.
1561
+
1562
+ A *3-dimensional Euclidean space* is an affine space `E`, whose associated
1563
+ vector space is a 3-dimensional vector space over `\RR` and is equipped
1564
+ with a positive definite symmetric bilinear form, called the *scalar
1565
+ product* or *dot product*.
1566
+
1567
+ The class :class:`Euclidean3dimSpace` inherits from
1568
+ :class:`~sage.manifolds.differentiable.pseudo_riemannian.PseudoRiemannianManifold`
1569
+ (via :class:`EuclideanSpace`) since a 3-dimensional Euclidean space
1570
+ can be viewed as a Riemannian manifold that is diffeomorphic to `\RR^3` and
1571
+ that has a flat metric `g`. The Euclidean scalar product is the one defined
1572
+ by the Riemannian metric `g`.
1573
+
1574
+ INPUT:
1575
+
1576
+ - ``name`` -- (default: ``None``) string; name (symbol) given to the
1577
+ Euclidean 3-space; if ``None``, the name will be set to ``'E^3'``
1578
+ - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the
1579
+ Euclidean 3-space; if ``None``, it is set to ``'\mathbb{E}^{3}'`` if
1580
+ ``name`` is ``None`` and to ``name`` otherwise
1581
+ - ``coordinates`` -- (default: ``'Cartesian'``) string describing the type
1582
+ of coordinates to be initialized at the Euclidean 3-space creation;
1583
+ allowed values are ``'Cartesian'`` (see :meth:`cartesian_coordinates`),
1584
+ ``'spherical'`` (see :meth:`spherical_coordinates`) and ``'cylindrical'``
1585
+ (see :meth:`cylindrical_coordinates`)
1586
+ - ``symbols`` -- (default: ``None``) string defining the coordinate text
1587
+ symbols and LaTeX symbols, with the same conventions as the argument
1588
+ ``coordinates`` in
1589
+ :class:`~sage.manifolds.differentiable.chart.RealDiffChart`, namely
1590
+ ``symbols`` is a string of coordinate fields separated by a blank space,
1591
+ where each field contains the coordinate's text symbol and possibly the
1592
+ coordinate's LaTeX symbol (when the latter is different from the text
1593
+ symbol), both symbols being separated by a colon (``:``); if ``None``,
1594
+ the symbols will be automatically generated according to the value of
1595
+ ``coordinates``
1596
+ - ``metric_name`` -- (default: ``'g'``) string; name (symbol) given to the
1597
+ Euclidean metric tensor
1598
+ - ``metric_latex_name`` -- (default: ``None``) string; LaTeX symbol to
1599
+ denote the Euclidean metric tensor; if none is provided, it is set to
1600
+ ``metric_name``
1601
+ - ``start_index`` -- (default: 1) integer; lower value of the range of
1602
+ indices used for "indexed objects" in the Euclidean 3-space, e.g.
1603
+ coordinates of a chart
1604
+ - ``base_manifold`` -- (default: ``None``) if not ``None``, must be a
1605
+ Euclidean 3-space; the created object is then an open subset of
1606
+ ``base_manifold``
1607
+ - ``category`` -- (default: ``None``) to specify the category; if ``None``,
1608
+ ``Manifolds(RR).Smooth() & MetricSpaces().Complete()`` is assumed
1609
+ - ``names`` -- (default: ``None``) unused argument, except if
1610
+ ``symbols`` is not provided; it must then be a tuple containing
1611
+ the coordinate symbols (this is guaranteed if the shortcut operator
1612
+ ``<,>`` is used)
1613
+ - ``init_coord_methods`` -- (default: ``None``) dictionary of methods
1614
+ to initialize the various type of coordinates, with each key being a
1615
+ string describing the type of coordinates; to be used by derived classes
1616
+ only
1617
+ - ``unique_tag`` -- (default: ``None``) tag used to force the construction
1618
+ of a new object when all the other arguments have been used previously
1619
+ (without ``unique_tag``, the
1620
+ :class:`~sage.structure.unique_representation.UniqueRepresentation`
1621
+ behavior inherited from
1622
+ :class:`~sage.manifolds.differentiable.pseudo_riemannian.PseudoRiemannianManifold`
1623
+ would return the previously constructed object corresponding to these
1624
+ arguments)
1625
+
1626
+ EXAMPLES:
1627
+
1628
+ A 3-dimensional Euclidean space::
1629
+
1630
+ sage: E = EuclideanSpace(3); E
1631
+ Euclidean space E^3
1632
+ sage: latex(E)
1633
+ \mathbb{E}^{3}
1634
+
1635
+ ``E`` belongs to the class :class:`Euclidean3dimSpace` (actually to
1636
+ a dynamically generated subclass of it via SageMath's category framework)::
1637
+
1638
+ sage: type(E)
1639
+ <class 'sage.manifolds.differentiable.examples.euclidean.Euclidean3dimSpace_with_category'>
1640
+
1641
+ ``E`` is both a real smooth manifold of dimension `3` and a complete metric
1642
+ space::
1643
+
1644
+ sage: E.category()
1645
+ Join of Category of smooth manifolds over Real Field with 53 bits of
1646
+ precision and Category of connected manifolds over Real Field with
1647
+ 53 bits of precision and Category of complete metric spaces
1648
+ sage: dim(E)
1649
+ 3
1650
+
1651
+ It is endowed with a default coordinate chart, which is that of Cartesian
1652
+ coordinates `(x,y,z)`::
1653
+
1654
+ sage: E.atlas()
1655
+ [Chart (E^3, (x, y, z))]
1656
+ sage: E.default_chart()
1657
+ Chart (E^3, (x, y, z))
1658
+ sage: cartesian = E.cartesian_coordinates()
1659
+ sage: cartesian is E.default_chart()
1660
+ True
1661
+
1662
+ A point of ``E``::
1663
+
1664
+ sage: p = E((3,-2,1)); p
1665
+ Point on the Euclidean space E^3
1666
+ sage: cartesian(p)
1667
+ (3, -2, 1)
1668
+ sage: p in E
1669
+ True
1670
+ sage: p.parent() is E
1671
+ True
1672
+
1673
+ ``E`` is endowed with a default metric tensor, which defines the
1674
+ Euclidean scalar product::
1675
+
1676
+ sage: g = E.metric(); g
1677
+ Riemannian metric g on the Euclidean space E^3
1678
+ sage: g.display()
1679
+ g = dx⊗dx + dy⊗dy + dz⊗dz
1680
+
1681
+ Curvilinear coordinates can be introduced on ``E``: see
1682
+ :meth:`spherical_coordinates` and :meth:`cylindrical_coordinates`.
1683
+
1684
+ .. SEEALSO::
1685
+
1686
+ :ref:`EuclideanSpace_example2`
1687
+ """
1688
+ def __init__(self, name=None, latex_name=None, coordinates='Cartesian',
1689
+ symbols=None, metric_name='g', metric_latex_name=None,
1690
+ start_index=1, base_manifold=None, category=None, unique_tag=None):
1691
+ r"""
1692
+ Construct a Euclidean 3-space.
1693
+
1694
+ TESTS::
1695
+
1696
+ sage: E = EuclideanSpace(3); E
1697
+ Euclidean space E^3
1698
+ sage: E.metric()
1699
+ Riemannian metric g on the Euclidean space E^3
1700
+ sage: TestSuite(E).run()
1701
+ """
1702
+ if coordinates not in ['Cartesian', 'spherical', 'cylindrical']:
1703
+ raise TypeError("unknown coordinate type")
1704
+ if symbols is None:
1705
+ if coordinates == 'Cartesian':
1706
+ symbols = 'x y z'
1707
+ elif coordinates == 'spherical':
1708
+ symbols = 'r th:\\theta ph:\\phi'
1709
+ elif coordinates == 'cylindrical':
1710
+ symbols = 'r ph:\\phi z'
1711
+ self._spherical_chart = None # to be constructed later if necessary
1712
+ self._spherical_frame = None # orthonormal frame
1713
+ self._cylindrical_chart = None
1714
+ self._cylindrical_frame = None # orthonormal frame
1715
+ init_coord_methods = {'Cartesian': self._init_cartesian,
1716
+ 'spherical': self._init_spherical,
1717
+ 'cylindrical': self._init_cylindrical}
1718
+ EuclideanSpace.__init__(self, 3, name=name,
1719
+ latex_name=latex_name,
1720
+ coordinates=coordinates,
1721
+ symbols=symbols,
1722
+ metric_name=metric_name,
1723
+ metric_latex_name=metric_latex_name,
1724
+ start_index=start_index,
1725
+ base_manifold=base_manifold, category=category,
1726
+ init_coord_methods=init_coord_methods)
1727
+ if coordinates == 'spherical':
1728
+ # The default frame is the spherical coordinate frame; we change it
1729
+ # to the orthonormal spherical frame
1730
+ self.set_default_frame(self.spherical_frame())
1731
+ if coordinates == 'cylindrical':
1732
+ # The default frame is the cylindrical coordinate frame; we change
1733
+ # it to the orthonormal cylindrical frame
1734
+ self.set_default_frame(self.cylindrical_frame())
1735
+
1736
+ def _repr_(self):
1737
+ r"""
1738
+ Return a string representation of ``self``.
1739
+
1740
+ TESTS::
1741
+
1742
+ sage: E = EuclideanSpace(3)
1743
+ sage: E._repr_()
1744
+ 'Euclidean space E^3'
1745
+ sage: E # indirect doctest
1746
+ Euclidean space E^3
1747
+ sage: E = EuclideanSpace(3, name='E')
1748
+ sage: E._repr_()
1749
+ 'Euclidean space E'
1750
+ """
1751
+ return "Euclidean space {}".format(self._name)
1752
+
1753
+ def _init_spherical(self, symbols):
1754
+ r"""
1755
+ Construct the chart of spherical coordinates and initialize the
1756
+ components of the metric tensor in it.
1757
+
1758
+ TESTS::
1759
+
1760
+ sage: E = EuclideanSpace(3)
1761
+ sage: E.atlas()
1762
+ [Chart (E^3, (x, y, z))]
1763
+ sage: E._init_spherical(r"R Th:\Theta Ph:\Phi")
1764
+ sage: E.atlas()
1765
+ [Chart (E^3, (x, y, z)), Chart (E^3, (R, Th, Ph))]
1766
+ """
1767
+ coords = symbols.split() # list of strings, one per coordinate
1768
+ # Adding the coordinate ranges:
1769
+ coordinates = (coords[0] + ':(0,+oo) ' + coords[1] + ':(0,pi) '
1770
+ + coords[2] + ':(0,2*pi):periodic')
1771
+ chart = self.chart(coordinates=coordinates)
1772
+ self._spherical_chart = chart
1773
+ frame = chart.frame()
1774
+ # Initialization of the metric components and the associated
1775
+ # Christoffel symbols
1776
+ g = self.metric()
1777
+ gc = g.add_comp(frame)
1778
+ i1 = self._sindex
1779
+ i2 = i1 + 1
1780
+ i3 = i1 + 2
1781
+ r, th, ph = chart[:]
1782
+ gc[i1, i1, chart] = 1
1783
+ gc[i2, i2, chart] = r**2
1784
+ gc[i3, i3, chart] = (r*sin(th))**2
1785
+ # Orthonormal frame associated with spherical coordinates:
1786
+ to_orthonormal = self.automorphism_field()
1787
+ to_orthonormal[frame, i1, i1, chart] = 1
1788
+ to_orthonormal[frame, i2, i2, chart] = 1/r
1789
+ to_orthonormal[frame, i3, i3, chart] = 1/(r*sin(th))
1790
+ oframe = frame.new_frame(to_orthonormal, 'e',
1791
+ indices=(str(r), str(th), str(ph)),
1792
+ latex_indices=(latex(r), latex(th), latex(ph)))
1793
+ self._spherical_frame = oframe
1794
+ g.comp(oframe)
1795
+
1796
+ def _init_cylindrical(self, symbols):
1797
+ r"""
1798
+ Construct the chart of cylindrical coordinates and initialize the
1799
+ components of the metric tensor in it.
1800
+
1801
+ TESTS::
1802
+
1803
+ sage: E = EuclideanSpace(3)
1804
+ sage: E.atlas()
1805
+ [Chart (E^3, (x, y, z))]
1806
+ sage: E._init_cylindrical(r"r ph:\phi z")
1807
+ sage: E.atlas()
1808
+ [Chart (E^3, (x, y, z)), Chart (E^3, (r, ph, z))]
1809
+ """
1810
+ coords = symbols.split() # list of strings, one per coordinate
1811
+ # Adding the coordinate ranges:
1812
+ coordinates = (coords[0] + ':(0,+oo) ' + coords[1]
1813
+ + ':(0,2*pi):periodic ' + coords[2])
1814
+ chart = self.chart(coordinates=coordinates)
1815
+ self._cylindrical_chart = chart
1816
+ frame = chart.frame()
1817
+ # Initialization of the metric components and the associated
1818
+ # Christoffel symbols
1819
+ g = self.metric()
1820
+ gc = g.add_comp(frame)
1821
+ i1 = self._sindex
1822
+ i2 = i1 + 1
1823
+ i3 = i1 + 2
1824
+ rh, ph, z = chart[:]
1825
+ gc[i1, i1, chart] = 1
1826
+ gc[i2, i2, chart] = rh**2
1827
+ gc[i3, i3, chart] = 1
1828
+ # Orthonormal frame associated with cylindrical coordinates:
1829
+ to_orthonormal = self.automorphism_field()
1830
+ to_orthonormal[frame, i1, i1, chart] = 1
1831
+ to_orthonormal[frame, i2, i2, chart] = 1 / rh
1832
+ to_orthonormal[frame, i3, i3, chart] = 1
1833
+ oframe = frame.new_frame(to_orthonormal, 'e',
1834
+ indices=(str(rh), str(ph), str(z)),
1835
+ latex_indices=(latex(rh), latex(ph), latex(z)))
1836
+ self._cylindrical_frame = oframe
1837
+ g.comp(oframe)
1838
+
1839
+ def _transition_spherical_cartesian(self):
1840
+ r"""
1841
+ Transitions between spherical and Cartesian coordinates.
1842
+
1843
+ TESTS::
1844
+
1845
+ sage: E = EuclideanSpace(3)
1846
+ sage: E._init_spherical(r"r th:\theta ph:\phi")
1847
+ sage: E.atlas()
1848
+ [Chart (E^3, (x, y, z)), Chart (E^3, (r, th, ph))]
1849
+ sage: E.coord_changes() # no transition map has been defined yet
1850
+ {}
1851
+ sage: E._transition_spherical_cartesian() # long time
1852
+
1853
+ 2 transition maps have been created::
1854
+
1855
+ sage: len(E.coord_changes()) # long time
1856
+ 2
1857
+
1858
+ Tests of the change-of-frame formulas::
1859
+
1860
+ sage: # long time
1861
+ sage: spher = E.spherical_coordinates()
1862
+ sage: spher_f = E.spherical_frame()
1863
+ sage: cart_f = E.cartesian_frame()
1864
+ sage: E.change_of_frame(cart_f, spher_f)[:,spher]
1865
+ [cos(ph)*sin(th) cos(ph)*cos(th) -sin(ph)]
1866
+ [sin(ph)*sin(th) cos(th)*sin(ph) cos(ph)]
1867
+ [ cos(th) -sin(th) 0]
1868
+ sage: E.change_of_frame(spher_f, cart_f)[:,spher]
1869
+ [cos(ph)*sin(th) sin(ph)*sin(th) cos(th)]
1870
+ [cos(ph)*cos(th) cos(th)*sin(ph) -sin(th)]
1871
+ [ -sin(ph) cos(ph) 0]
1872
+ """
1873
+ # Transition maps spherical chart <-> Cartesian chart
1874
+ chart_cart = self._cartesian_chart
1875
+ chart_spher = self._spherical_chart
1876
+ x, y, z = chart_cart[:]
1877
+ r, th, ph = chart_spher[:]
1878
+ spher_to_cart = chart_spher.transition_map(chart_cart,
1879
+ [r*sin(th)*cos(ph), r*sin(th)*sin(ph), r*cos(th)])
1880
+ spher_to_cart.set_inverse(sqrt(x**2+y**2+z**2),
1881
+ atan2(sqrt(x**2+y**2),z), atan2(y, x),
1882
+ check=False)
1883
+ # Automorphism Cartesian frame → orthonormal spherical frame:
1884
+ oframe = self._spherical_frame
1885
+ cframe = chart_cart.frame()
1886
+ sframe = chart_spher.frame()
1887
+ chg = self._frame_changes
1888
+ cframe_to_oframe = chg[(sframe, oframe)] * chg[(cframe, sframe)]
1889
+ # cframe_to_oframe has been computed only in sframe;
1890
+ # its components in oframe are computed by
1891
+ cmp_of = cframe_to_oframe.comp(oframe)
1892
+ # while its components in cframe are obtained by identifying the
1893
+ # matrices in cframe and oframe:
1894
+ cmp_cf = cframe_to_oframe.add_comp(cframe)
1895
+ for i in self.irange():
1896
+ for j in self.irange():
1897
+ cmp_cf[[i,j]] = cmp_of[[i,j]]
1898
+ # Automorphism orthonormal spherical frame → Cartesian frame:
1899
+ oframe_to_cframe = chg[(sframe, cframe)] * chg[(oframe, sframe)]
1900
+ # oframe_to_cframe has been computed only in sframe;
1901
+ # its components in oframe are computed by
1902
+ cmp_of = oframe_to_cframe.comp(oframe)
1903
+ # while its components in cframe are obtained by identifying the
1904
+ # matrices in cframe and oframe:
1905
+ cmp_cf = oframe_to_cframe.add_comp(cframe)
1906
+ for i in self.irange():
1907
+ for j in self.irange():
1908
+ cmp_cf[[i,j]] = cmp_of[[i,j]]
1909
+ # Storage of the results:
1910
+ chg[(cframe, oframe)] = cframe_to_oframe
1911
+ chg[(oframe, cframe)] = oframe_to_cframe
1912
+ vmodule = self.vector_field_module()
1913
+ vmodule.set_change_of_basis(cframe, oframe, cframe_to_oframe,
1914
+ compute_inverse=False)
1915
+ vmodule.set_change_of_basis(oframe, cframe, oframe_to_cframe,
1916
+ compute_inverse=False)
1917
+
1918
+ def _transition_cylindrical_cartesian(self):
1919
+ r"""
1920
+ Transitions between cylindrical and Cartesian coordinates.
1921
+
1922
+ TESTS::
1923
+
1924
+ sage: E = EuclideanSpace(3)
1925
+ sage: E._init_cylindrical(r"r ph:\phi z")
1926
+ sage: E.atlas()
1927
+ [Chart (E^3, (x, y, z)), Chart (E^3, (r, ph, z))]
1928
+ sage: E.coord_changes() # no transition map has been defined yet
1929
+ {}
1930
+ sage: E._transition_cylindrical_cartesian() # long time
1931
+
1932
+ 2 transition maps have been created::
1933
+
1934
+ sage: len(E.coord_changes()) # long time
1935
+ 2
1936
+
1937
+ Tests of the change-of-frame formulas::
1938
+
1939
+ sage: # long time
1940
+ sage: cylind = E.cylindrical_coordinates()
1941
+ sage: cylind_f = E.cylindrical_frame()
1942
+ sage: cart_f= E.cartesian_frame()
1943
+ sage: E.change_of_frame(cart_f, cylind_f)[:,cylind]
1944
+ [ cos(ph) -sin(ph) 0]
1945
+ [ sin(ph) cos(ph) 0]
1946
+ [ 0 0 1]
1947
+ sage: E.change_of_frame(cylind_f, cart_f)[:,cylind]
1948
+ [ cos(ph) sin(ph) 0]
1949
+ [-sin(ph) cos(ph) 0]
1950
+ [ 0 0 1]
1951
+ """
1952
+ # Transition maps cylindrical chart <-> Cartesian chart
1953
+ chart_cart = self._cartesian_chart
1954
+ chart_cylind = self._cylindrical_chart
1955
+ x, y, z = chart_cart[:]
1956
+ rh, ph, z = chart_cylind[:]
1957
+ cylind_to_cart = chart_cylind.transition_map(chart_cart,
1958
+ [rh*cos(ph), rh*sin(ph), z])
1959
+ cylind_to_cart.set_inverse(sqrt(x**2+y**2), atan2(y, x), z, check=False)
1960
+ # Automorphism Cartesian frame → orthonormal cylindrical frame:
1961
+ oframe = self._cylindrical_frame
1962
+ cframe = chart_cart.frame()
1963
+ sframe = chart_cylind.frame()
1964
+ chg = self._frame_changes
1965
+ cframe_to_oframe = chg[(sframe, oframe)] * chg[(cframe, sframe)]
1966
+ # cframe_to_oframe has been computed only in sframe;
1967
+ # its components in oframe are computed by
1968
+ cmp_of = cframe_to_oframe.comp(oframe)
1969
+ # while its components in cframe are obtained by identifying the
1970
+ # matrices in cframe and oframe:
1971
+ cmp_cf = cframe_to_oframe.add_comp(cframe)
1972
+ for i in self.irange():
1973
+ for j in self.irange():
1974
+ cmp_cf[[i,j]] = cmp_of[[i,j]]
1975
+ # Automorphism orthonormal cylindrical frame → Cartesian frame:
1976
+ oframe_to_cframe = chg[(sframe, cframe)] * chg[(oframe, sframe)]
1977
+ # oframe_to_cframe has been computed only in sframe;
1978
+ # its components in oframe are computed by
1979
+ cmp_of = oframe_to_cframe.comp(oframe)
1980
+ # while its components in cframe are obtained by identifying the
1981
+ # matrices in cframe and oframe:
1982
+ cmp_cf = oframe_to_cframe.add_comp(cframe)
1983
+ for i in self.irange():
1984
+ for j in self.irange():
1985
+ cmp_cf[[i,j]] = cmp_of[[i,j]]
1986
+ # Storage of the results:
1987
+ chg[(cframe, oframe)] = cframe_to_oframe
1988
+ chg[(oframe, cframe)] = oframe_to_cframe
1989
+ vmodule = self.vector_field_module()
1990
+ vmodule.set_change_of_basis(cframe, oframe, cframe_to_oframe,
1991
+ compute_inverse=False)
1992
+ vmodule.set_change_of_basis(oframe, cframe, oframe_to_cframe,
1993
+ compute_inverse=False)
1994
+
1995
+ def _transition_spherical_cylindrical(self):
1996
+ r"""
1997
+ Transitions between spherical and cylindrical coordinates.
1998
+
1999
+ TESTS::
2000
+
2001
+ sage: E = EuclideanSpace(3, coordinates='cylindrical')
2002
+ sage: E._init_spherical(r"r th:\theta ph:\phi")
2003
+ sage: E.atlas()
2004
+ [Chart (E^3, (r, ph, z)), Chart (E^3, (r, th, ph))]
2005
+ sage: E.coord_changes() # no transition map has been defined yet
2006
+ {}
2007
+ sage: E._transition_spherical_cylindrical() # long time
2008
+
2009
+ 2 transition maps have been created::
2010
+
2011
+ sage: len(E.coord_changes()) # long time
2012
+ 2
2013
+
2014
+ Tests of the change-of-frame formulas::
2015
+
2016
+ sage: # long time
2017
+ sage: spher = E.spherical_coordinates()
2018
+ sage: spher_f = E.spherical_frame()
2019
+ sage: cylind_f = E.cylindrical_frame()
2020
+ sage: E.change_of_frame(cylind_f, spher_f)[:, spher]
2021
+ [ sin(th) cos(th) 0]
2022
+ [ 0 0 1]
2023
+ [ cos(th) -sin(th) 0]
2024
+ sage: E.change_of_frame(spher_f, cylind_f)[:, spher]
2025
+ [ sin(th) 0 cos(th)]
2026
+ [ cos(th) 0 -sin(th)]
2027
+ [ 0 1 0]
2028
+ """
2029
+ # Transition maps spherical chart <-> cylindrical chart
2030
+ cylind = self._cylindrical_chart
2031
+ spher = self._spherical_chart
2032
+ rh, ph, z = cylind[:]
2033
+ r, th, ph = spher[:]
2034
+ spher_to_cylind = spher.transition_map(cylind,
2035
+ [r*sin(th), ph, r*cos(th)])
2036
+ spher_to_cylind.set_inverse(sqrt(rh**2 + z**2), atan2(rh,z), ph,
2037
+ check=False)
2038
+ # Automorphism orthon. cylindrical frame -> orthon. spherical frame
2039
+ cf = cylind.frame() # coordinate cylindrical frame
2040
+ sf = spher.frame() # coordinate spherical frame
2041
+ ocf = self._cylindrical_frame # orthonormal cylindrical frame
2042
+ osf = self._spherical_frame # orthonormal spherical frame
2043
+ chg = self._frame_changes
2044
+ oc_to_os = chg[(sf, osf)] * chg[(cf, sf)] * chg[(ocf, cf)]
2045
+ # oc_to_os has been computed only in sf frame; its components in osf
2046
+ # frame are computed by:
2047
+ cmp_osf = oc_to_os.comp(osf)
2048
+ # while its components in ocf frame are obtained by identifying the
2049
+ # matrices in ocf frame and osf oframe:
2050
+ cmp_ocf = oc_to_os.add_comp(ocf)
2051
+ for i in self.irange():
2052
+ for j in self.irange():
2053
+ cmp_ocf[[i,j]] = cmp_osf[[i,j]]
2054
+ # Automorphism orthon. spherical frame -> orthon. cylindrical frame
2055
+ os_to_oc = chg[(cf, ocf)] * chg[(sf, cf)] * chg[(osf, sf)]
2056
+ # oc_to_os has been computed only in cf frame; its components in ocf
2057
+ # frame are computed by
2058
+ cmp_ocf = os_to_oc.comp(ocf)
2059
+ # while its components in osf frame are obtained by identifying the
2060
+ # matrices in osf frame and ocf oframe:
2061
+ cmp_osf = os_to_oc.add_comp(osf)
2062
+ for i in self.irange():
2063
+ for j in self.irange():
2064
+ cmp_osf[[i,j]] = cmp_ocf[[i,j]]
2065
+ # Storage of the results:
2066
+ chg[(ocf, osf)] = oc_to_os
2067
+ chg[(osf, ocf)] = os_to_oc
2068
+ vmodule = self.vector_field_module()
2069
+ vmodule.set_change_of_basis(ocf, osf, oc_to_os, compute_inverse=False)
2070
+ vmodule.set_change_of_basis(osf, ocf, os_to_oc, compute_inverse=False)
2071
+
2072
+ def cartesian_coordinates(self, symbols=None, names=None):
2073
+ r"""
2074
+ Return the chart of Cartesian coordinates, possibly creating it if it
2075
+ does not already exist.
2076
+
2077
+ INPUT:
2078
+
2079
+ - ``symbols`` -- (default: ``None``) string defining the coordinate
2080
+ text symbols and LaTeX symbols, with the same conventions as the
2081
+ argument ``coordinates`` in
2082
+ :class:`~sage.manifolds.differentiable.chart.RealDiffChart`; this is
2083
+ used only if the Cartesian chart has not been already defined; if
2084
+ ``None`` the symbols are generated as `(x,y,z)`.
2085
+ - ``names`` -- (default: ``None``) unused argument, except if
2086
+ ``symbols`` is not provided; it must be a tuple containing
2087
+ the coordinate symbols (this is guaranteed if the shortcut operator
2088
+ ``<,>`` is used)
2089
+
2090
+ OUTPUT:
2091
+
2092
+ - the chart of Cartesian coordinates, as an instance of
2093
+ :class:`~sage.manifolds.differentiable.chart.RealDiffChart`
2094
+
2095
+ EXAMPLES::
2096
+
2097
+ sage: E = EuclideanSpace(3)
2098
+ sage: E.cartesian_coordinates()
2099
+ Chart (E^3, (x, y, z))
2100
+ sage: E.cartesian_coordinates().coord_range()
2101
+ x: (-oo, +oo); y: (-oo, +oo); z: (-oo, +oo)
2102
+
2103
+ An example where the Cartesian coordinates have not been previously
2104
+ created::
2105
+
2106
+ sage: E = EuclideanSpace(3, coordinates='spherical')
2107
+ sage: E.atlas() # only spherical coordinates have been initialized
2108
+ [Chart (E^3, (r, th, ph))]
2109
+ sage: E.cartesian_coordinates(symbols='X Y Z')
2110
+ Chart (E^3, (X, Y, Z))
2111
+ sage: E.atlas() # the Cartesian chart has been added to the atlas
2112
+ [Chart (E^3, (r, th, ph)), Chart (E^3, (X, Y, Z))]
2113
+
2114
+ The coordinate variables are returned by the square bracket operator::
2115
+
2116
+ sage: E.cartesian_coordinates()[1]
2117
+ X
2118
+ sage: E.cartesian_coordinates()[3]
2119
+ Z
2120
+ sage: E.cartesian_coordinates()[:]
2121
+ (X, Y, Z)
2122
+
2123
+ It is also possible to use the operator ``<,>`` to set symbolic
2124
+ variable containing the coordinates::
2125
+
2126
+ sage: E = EuclideanSpace(3, coordinates='spherical')
2127
+ sage: cartesian.<u,v,w> = E.cartesian_coordinates()
2128
+ sage: cartesian
2129
+ Chart (E^3, (u, v, w))
2130
+ sage: u, v, w
2131
+ (u, v, w)
2132
+
2133
+ The command ``cartesian.<u,v,w> = E.cartesian_coordinates()``
2134
+ is actually a shortcut for::
2135
+
2136
+ sage: cartesian = E.cartesian_coordinates(symbols='u v w')
2137
+ sage: u, v, w = cartesian[:]
2138
+ """
2139
+ if self._cartesian_chart is None:
2140
+ if symbols is None:
2141
+ if names is None:
2142
+ symbols = 'x y z'
2143
+ else:
2144
+ symbols = names[0] + ' ' + names[1] + ' ' + names[2]
2145
+ self._init_cartesian(symbols)
2146
+ if self._spherical_chart:
2147
+ self._transition_spherical_cartesian()
2148
+ if self._cylindrical_chart:
2149
+ self._transition_cylindrical_cartesian()
2150
+ return self._cartesian_chart
2151
+
2152
+ def spherical_coordinates(self, symbols=None, names=None):
2153
+ r"""
2154
+ Return the chart of spherical coordinates, possibly creating it if it
2155
+ does not already exist.
2156
+
2157
+ INPUT:
2158
+
2159
+ - ``symbols`` -- (default: ``None``) string defining the coordinate
2160
+ text symbols and LaTeX symbols, with the same conventions as the
2161
+ argument ``coordinates`` in
2162
+ :class:`~sage.manifolds.differentiable.chart.RealDiffChart`; this is
2163
+ used only if the spherical chart has not been already defined; if
2164
+ ``None`` the symbols are generated as `(r,\theta,\phi)`.
2165
+ - ``names`` -- (default: ``None``) unused argument, except if
2166
+ ``symbols`` is not provided; it must be a tuple containing
2167
+ the coordinate symbols (this is guaranteed if the shortcut operator
2168
+ ``<,>`` is used)
2169
+
2170
+ OUTPUT:
2171
+
2172
+ - the chart of spherical coordinates, as an instance of
2173
+ :class:`~sage.manifolds.differentiable.chart.RealDiffChart`
2174
+
2175
+ EXAMPLES::
2176
+
2177
+ sage: E = EuclideanSpace(3)
2178
+ sage: E.spherical_coordinates()
2179
+ Chart (E^3, (r, th, ph))
2180
+ sage: latex(_)
2181
+ \left(\mathbb{E}^{3},(r, {\theta}, {\phi})\right)
2182
+ sage: E.spherical_coordinates().coord_range()
2183
+ r: (0, +oo); th: (0, pi); ph: [0, 2*pi] (periodic)
2184
+
2185
+ The relation to Cartesian coordinates is::
2186
+
2187
+ sage: E.coord_change(E.spherical_coordinates(),
2188
+ ....: E.cartesian_coordinates()).display()
2189
+ x = r*cos(ph)*sin(th)
2190
+ y = r*sin(ph)*sin(th)
2191
+ z = r*cos(th)
2192
+ sage: E.coord_change(E.cartesian_coordinates(),
2193
+ ....: E.spherical_coordinates()).display()
2194
+ r = sqrt(x^2 + y^2 + z^2)
2195
+ th = arctan2(sqrt(x^2 + y^2), z)
2196
+ ph = arctan2(y, x)
2197
+
2198
+ The coordinate variables are returned by the square bracket operator::
2199
+
2200
+ sage: E.spherical_coordinates()[1]
2201
+ r
2202
+ sage: E.spherical_coordinates()[3]
2203
+ ph
2204
+ sage: E.spherical_coordinates()[:]
2205
+ (r, th, ph)
2206
+
2207
+ They can also be obtained via the operator ``<,>``::
2208
+
2209
+ sage: spherical.<r,th,ph> = E.spherical_coordinates()
2210
+ sage: spherical
2211
+ Chart (E^3, (r, th, ph))
2212
+ sage: r, th, ph
2213
+ (r, th, ph)
2214
+
2215
+ Actually, ``spherical.<r,th,ph> = E.spherical_coordinates()`` is a
2216
+ shortcut for::
2217
+
2218
+ sage: spherical = E.spherical_coordinates()
2219
+ sage: r, th, ph = spherical[:]
2220
+
2221
+ The coordinate symbols can be customized::
2222
+
2223
+ sage: E = EuclideanSpace(3)
2224
+ sage: E.spherical_coordinates(symbols=r"R T:\Theta F:\Phi")
2225
+ Chart (E^3, (R, T, F))
2226
+ sage: latex(E.spherical_coordinates())
2227
+ \left(\mathbb{E}^{3},(R, {\Theta}, {\Phi})\right)
2228
+
2229
+ Note that if the spherical coordinates have been already initialized,
2230
+ the argument ``symbols`` has no effect::
2231
+
2232
+ sage: E.spherical_coordinates(symbols=r"r th:\theta ph:\phi")
2233
+ Chart (E^3, (R, T, F))
2234
+ """
2235
+ if self._spherical_chart is None:
2236
+ if symbols is None:
2237
+ if names is None:
2238
+ symbols = 'r th:\\theta ph:\\phi'
2239
+ else:
2240
+ names = list(names)
2241
+ if names[1] in ['t', 'th', 'theta']:
2242
+ names[1] = names[1] + ':\\theta'
2243
+ if names[2] in ['p', 'ph', 'phi']:
2244
+ names[2] = names[2] + ':\\phi'
2245
+ symbols = names[0] + ' ' + names[1] + ' ' + names[2]
2246
+ self._init_spherical(symbols)
2247
+ if self._cartesian_chart:
2248
+ self._transition_spherical_cartesian()
2249
+ if self._cylindrical_chart:
2250
+ self._transition_spherical_cylindrical()
2251
+ return self._spherical_chart
2252
+
2253
+ def spherical_frame(self):
2254
+ r"""
2255
+ Return the orthonormal vector frame associated with spherical
2256
+ coordinates.
2257
+
2258
+ OUTPUT: :class:`~sage.manifolds.differentiable.vectorframe.VectorFrame`
2259
+
2260
+ EXAMPLES::
2261
+
2262
+ sage: E = EuclideanSpace(3)
2263
+ sage: E.spherical_frame()
2264
+ Vector frame (E^3, (e_r,e_th,e_ph))
2265
+ sage: E.spherical_frame()[1]
2266
+ Vector field e_r on the Euclidean space E^3
2267
+ sage: E.spherical_frame()[:]
2268
+ (Vector field e_r on the Euclidean space E^3,
2269
+ Vector field e_th on the Euclidean space E^3,
2270
+ Vector field e_ph on the Euclidean space E^3)
2271
+
2272
+ The spherical frame expressed in terms of the Cartesian one::
2273
+
2274
+ sage: for e in E.spherical_frame():
2275
+ ....: e.display(E.cartesian_frame(), E.spherical_coordinates())
2276
+ e_r = cos(ph)*sin(th) e_x + sin(ph)*sin(th) e_y + cos(th) e_z
2277
+ e_th = cos(ph)*cos(th) e_x + cos(th)*sin(ph) e_y - sin(th) e_z
2278
+ e_ph = -sin(ph) e_x + cos(ph) e_y
2279
+
2280
+ The orthonormal frame `(e_r, e_\theta, e_\phi)` expressed in terms of
2281
+ the coordinate frame
2282
+ `\left(\frac{\partial}{\partial r}, \frac{\partial}{\partial\theta},
2283
+ \frac{\partial}{\partial\phi}\right)`::
2284
+
2285
+ sage: for e in E.spherical_frame():
2286
+ ....: e.display(E.spherical_coordinates())
2287
+ e_r = ∂/∂r
2288
+ e_th = 1/r ∂/∂th
2289
+ e_ph = 1/(r*sin(th)) ∂/∂ph
2290
+ """
2291
+ if self._spherical_frame is None:
2292
+ # create the spherical chart and the associated orthonormal frame
2293
+ self.spherical_coordinates()
2294
+ return self._spherical_frame
2295
+
2296
+ def cylindrical_coordinates(self, symbols=None, names=None):
2297
+ r"""
2298
+ Return the chart of cylindrical coordinates, possibly creating it if it
2299
+ does not already exist.
2300
+
2301
+ INPUT:
2302
+
2303
+ - ``symbols`` -- (default: ``None``) string defining the coordinate
2304
+ text symbols and LaTeX symbols, with the same conventions as the
2305
+ argument ``coordinates`` in
2306
+ :class:`~sage.manifolds.differentiable.chart.RealDiffChart`; this is
2307
+ used only if the cylindrical chart has not been already defined; if
2308
+ ``None`` the symbols are generated as `(\rho,\phi,z)`.
2309
+ - ``names`` -- (default: ``None``) unused argument, except if
2310
+ ``symbols`` is not provided; it must be a tuple containing
2311
+ the coordinate symbols (this is guaranteed if the shortcut operator
2312
+ ``<,>`` is used)
2313
+
2314
+ OUTPUT:
2315
+
2316
+ - the chart of cylindrical coordinates, as an instance of
2317
+ :class:`~sage.manifolds.differentiable.chart.RealDiffChart`
2318
+
2319
+ EXAMPLES::
2320
+
2321
+ sage: E = EuclideanSpace(3)
2322
+ sage: E.cylindrical_coordinates()
2323
+ Chart (E^3, (rh, ph, z))
2324
+ sage: latex(_)
2325
+ \left(\mathbb{E}^{3},({\rho}, {\phi}, z)\right)
2326
+ sage: E.cylindrical_coordinates().coord_range()
2327
+ rh: (0, +oo); ph: [0, 2*pi] (periodic); z: (-oo, +oo)
2328
+
2329
+ The relation to Cartesian coordinates is::
2330
+
2331
+ sage: E.coord_change(E.cylindrical_coordinates(),
2332
+ ....: E.cartesian_coordinates()).display()
2333
+ x = rh*cos(ph)
2334
+ y = rh*sin(ph)
2335
+ z = z
2336
+ sage: E.coord_change(E.cartesian_coordinates(),
2337
+ ....: E.cylindrical_coordinates()).display()
2338
+ rh = sqrt(x^2 + y^2)
2339
+ ph = arctan2(y, x)
2340
+ z = z
2341
+
2342
+ The coordinate variables are returned by the square bracket operator::
2343
+
2344
+ sage: E.cylindrical_coordinates()[1]
2345
+ rh
2346
+ sage: E.cylindrical_coordinates()[3]
2347
+ z
2348
+ sage: E.cylindrical_coordinates()[:]
2349
+ (rh, ph, z)
2350
+
2351
+ They can also be obtained via the operator ``<,>``::
2352
+
2353
+ sage: cylindrical.<rh,ph,z> = E.cylindrical_coordinates()
2354
+ sage: cylindrical
2355
+ Chart (E^3, (rh, ph, z))
2356
+ sage: rh, ph, z
2357
+ (rh, ph, z)
2358
+
2359
+ Actually, ``cylindrical.<rh,ph,z> = E.cylindrical_coordinates()`` is a
2360
+ shortcut for::
2361
+
2362
+ sage: cylindrical = E.cylindrical_coordinates()
2363
+ sage: rh, ph, z = cylindrical[:]
2364
+
2365
+ The coordinate symbols can be customized::
2366
+
2367
+ sage: E = EuclideanSpace(3)
2368
+ sage: E.cylindrical_coordinates(symbols=r"R Phi:\Phi Z")
2369
+ Chart (E^3, (R, Phi, Z))
2370
+ sage: latex(E.cylindrical_coordinates())
2371
+ \left(\mathbb{E}^{3},(R, {\Phi}, Z)\right)
2372
+
2373
+ Note that if the cylindrical coordinates have been already initialized,
2374
+ the argument ``symbols`` has no effect::
2375
+
2376
+ sage: E.cylindrical_coordinates(symbols=r"rh:\rho ph:\phi z")
2377
+ Chart (E^3, (R, Phi, Z))
2378
+ """
2379
+ if self._cylindrical_chart is None:
2380
+ if symbols is None:
2381
+ if names is None:
2382
+ symbols = 'rh:\\rho ph:\\phi z'
2383
+ else:
2384
+ names = list(names)
2385
+ if names[0] in ['rh', 'rho']:
2386
+ names[0] = names[0] + ':\\rho'
2387
+ if names[1] in ['p', 'ph', 'phi']:
2388
+ names[1] = names[1] + ':\\phi'
2389
+ symbols = names[0] + ' ' + names[1] + ' ' + names[2]
2390
+ self._init_cylindrical(symbols)
2391
+ if self._cartesian_chart:
2392
+ self._transition_cylindrical_cartesian()
2393
+ if self._spherical_chart:
2394
+ self._transition_spherical_cylindrical()
2395
+ return self._cylindrical_chart
2396
+
2397
+ def cylindrical_frame(self):
2398
+ r"""
2399
+ Return the orthonormal vector frame associated with cylindrical
2400
+ coordinates.
2401
+
2402
+ OUTPUT: :class:`~sage.manifolds.differentiable.vectorframe.VectorFrame`
2403
+
2404
+ EXAMPLES::
2405
+
2406
+ sage: E = EuclideanSpace(3)
2407
+ sage: E.cylindrical_frame()
2408
+ Vector frame (E^3, (e_rh,e_ph,e_z))
2409
+ sage: E.cylindrical_frame()[1]
2410
+ Vector field e_rh on the Euclidean space E^3
2411
+ sage: E.cylindrical_frame()[:]
2412
+ (Vector field e_rh on the Euclidean space E^3,
2413
+ Vector field e_ph on the Euclidean space E^3,
2414
+ Vector field e_z on the Euclidean space E^3)
2415
+
2416
+ The cylindrical frame expressed in terms of the Cartesian one::
2417
+
2418
+ sage: for e in E.cylindrical_frame():
2419
+ ....: e.display(E.cartesian_frame(), E.cylindrical_coordinates())
2420
+ e_rh = cos(ph) e_x + sin(ph) e_y
2421
+ e_ph = -sin(ph) e_x + cos(ph) e_y
2422
+ e_z = e_z
2423
+
2424
+ The orthonormal frame `(e_r, e_\phi, e_z)` expressed in terms of
2425
+ the coordinate frame
2426
+ `\left(\frac{\partial}{\partial r}, \frac{\partial}{\partial\phi},
2427
+ \frac{\partial}{\partial z}\right)`::
2428
+
2429
+ sage: for e in E.cylindrical_frame():
2430
+ ....: e.display(E.cylindrical_coordinates())
2431
+ e_rh = ∂/∂rh
2432
+ e_ph = 1/rh ∂/∂ph
2433
+ e_z = ∂/∂z
2434
+ """
2435
+ if self._cylindrical_frame is None:
2436
+ # create the cylindrical chart and the associated orthonormal frame
2437
+ self.cylindrical_coordinates()
2438
+ return self._cylindrical_frame
2439
+
2440
+ def scalar_triple_product(self, name=None, latex_name=None):
2441
+ r"""
2442
+ Return the scalar triple product operator, as a 3-form.
2443
+
2444
+ The *scalar triple product* (also called *mixed product*) of three
2445
+ vector fields `u`, `v` and `w` defined on a Euclidean space `E`
2446
+ is the scalar field
2447
+
2448
+ .. MATH::
2449
+
2450
+ \epsilon(u,v,w) = u \cdot (v \times w).
2451
+
2452
+ The scalar triple product operator `\epsilon` is a *3-form*, i.e. a
2453
+ field of fully antisymmetric trilinear forms; it is also called the
2454
+ *volume form* of `E` or the *Levi-Civita tensor* of `E`.
2455
+
2456
+ INPUT:
2457
+
2458
+ - ``name`` -- (default: ``None``) string; name given to the scalar
2459
+ triple product operator; if ``None``, ``'epsilon'`` is used
2460
+ - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote
2461
+ the scalar triple product; if ``None``, it is set to ``r'\epsilon'``
2462
+ if ``name`` is ``None`` and to ``name`` otherwise.
2463
+
2464
+ OUTPUT:
2465
+
2466
+ - the scalar triple product operator `\epsilon`, as an instance of
2467
+ :class:`~sage.manifolds.differentiable.diff_form.DiffFormParal`
2468
+
2469
+ EXAMPLES::
2470
+
2471
+ sage: E.<x,y,z> = EuclideanSpace()
2472
+ sage: triple_product = E.scalar_triple_product()
2473
+ sage: triple_product
2474
+ 3-form epsilon on the Euclidean space E^3
2475
+ sage: latex(triple_product)
2476
+ \epsilon
2477
+ sage: u = E.vector_field(x, y, z, name='u')
2478
+ sage: v = E.vector_field(-y, x, 0, name='v')
2479
+ sage: w = E.vector_field(y*z, x*z, x*y, name='w')
2480
+ sage: s = triple_product(u, v, w); s
2481
+ Scalar field epsilon(u,v,w) on the Euclidean space E^3
2482
+ sage: s.display()
2483
+ epsilon(u,v,w): E^3 → ℝ
2484
+ (x, y, z) ↦ x^3*y + x*y^3 - 2*x*y*z^2
2485
+ sage: s.expr()
2486
+ x^3*y + x*y^3 - 2*x*y*z^2
2487
+ sage: latex(s)
2488
+ \epsilon\left(u,v,w\right)
2489
+ sage: s == - triple_product(w, v, u)
2490
+ True
2491
+
2492
+ Check of the identity `\epsilon(u,v,w) = u\cdot(v\times w)`::
2493
+
2494
+ sage: s == u.dot(v.cross(w))
2495
+ True
2496
+
2497
+ Customizing the name::
2498
+
2499
+ sage: E.scalar_triple_product(name='S')
2500
+ 3-form S on the Euclidean space E^3
2501
+ sage: latex(_)
2502
+ S
2503
+ sage: E.scalar_triple_product(name='Omega', latex_name=r'\Omega')
2504
+ 3-form Omega on the Euclidean space E^3
2505
+ sage: latex(_)
2506
+ \Omega
2507
+ """
2508
+ eps = self.volume_form()
2509
+ if latex_name is None:
2510
+ if name is None:
2511
+ latex_name = r'\epsilon'
2512
+ else:
2513
+ latex_name = name
2514
+ if name is None:
2515
+ name = 'epsilon'
2516
+ eps.set_name(name=name, latex_name=latex_name)
2517
+ return eps