passagemath-symbolics 10.6.43__cp314-cp314t-musllinux_1_2_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 (171) hide show
  1. passagemath_symbolics/__init__.py +3 -0
  2. passagemath_symbolics-10.6.43.dist-info/METADATA +187 -0
  3. passagemath_symbolics-10.6.43.dist-info/RECORD +171 -0
  4. passagemath_symbolics-10.6.43.dist-info/WHEEL +5 -0
  5. passagemath_symbolics-10.6.43.dist-info/top_level.txt +3 -0
  6. sage/all__sagemath_symbolics.py +17 -0
  7. sage/calculus/all.py +14 -0
  8. sage/calculus/calculus.py +2826 -0
  9. sage/calculus/desolvers.py +1866 -0
  10. sage/calculus/predefined.py +51 -0
  11. sage/calculus/tests.py +225 -0
  12. sage/calculus/var.cpython-314t-x86_64-linux-musl.so +0 -0
  13. sage/calculus/var.pyx +401 -0
  14. sage/dynamics/all__sagemath_symbolics.py +6 -0
  15. sage/dynamics/complex_dynamics/all.py +5 -0
  16. sage/dynamics/complex_dynamics/mandel_julia.py +765 -0
  17. sage/dynamics/complex_dynamics/mandel_julia_helper.cpython-314t-x86_64-linux-musl.so +0 -0
  18. sage/dynamics/complex_dynamics/mandel_julia_helper.pyx +1035 -0
  19. sage/ext/all__sagemath_symbolics.py +1 -0
  20. sage/ext_data/kenzo/CP2.txt +45 -0
  21. sage/ext_data/kenzo/CP3.txt +349 -0
  22. sage/ext_data/kenzo/CP4.txt +4774 -0
  23. sage/ext_data/kenzo/README.txt +49 -0
  24. sage/ext_data/kenzo/S4.txt +20 -0
  25. sage/ext_data/magma/latex/latex.m +1021 -0
  26. sage/ext_data/magma/latex/latex.spec +1 -0
  27. sage/ext_data/magma/sage/basic.m +356 -0
  28. sage/ext_data/magma/sage/sage.spec +1 -0
  29. sage/ext_data/magma/spec +9 -0
  30. sage/geometry/all__sagemath_symbolics.py +8 -0
  31. sage/geometry/hyperbolic_space/all.py +5 -0
  32. sage/geometry/hyperbolic_space/hyperbolic_coercion.py +743 -0
  33. sage/geometry/hyperbolic_space/hyperbolic_constants.py +5 -0
  34. sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +2409 -0
  35. sage/geometry/hyperbolic_space/hyperbolic_interface.py +206 -0
  36. sage/geometry/hyperbolic_space/hyperbolic_isometry.py +1082 -0
  37. sage/geometry/hyperbolic_space/hyperbolic_model.py +1502 -0
  38. sage/geometry/hyperbolic_space/hyperbolic_point.py +621 -0
  39. sage/geometry/riemannian_manifolds/all.py +7 -0
  40. sage/geometry/riemannian_manifolds/parametrized_surface3d.py +1632 -0
  41. sage/geometry/riemannian_manifolds/surface3d_generators.py +461 -0
  42. sage/interfaces/all__sagemath_symbolics.py +1 -0
  43. sage/interfaces/magma.py +3017 -0
  44. sage/interfaces/magma_free.py +92 -0
  45. sage/interfaces/maple.py +1397 -0
  46. sage/interfaces/mathematica.py +1345 -0
  47. sage/interfaces/mathics.py +1312 -0
  48. sage/interfaces/sympy.py +1398 -0
  49. sage/interfaces/sympy_wrapper.py +197 -0
  50. sage/interfaces/tides.py +938 -0
  51. sage/libs/all__sagemath_symbolics.py +6 -0
  52. sage/manifolds/all.py +7 -0
  53. sage/manifolds/calculus_method.py +555 -0
  54. sage/manifolds/catalog.py +437 -0
  55. sage/manifolds/chart.py +4019 -0
  56. sage/manifolds/chart_func.py +3419 -0
  57. sage/manifolds/continuous_map.py +2183 -0
  58. sage/manifolds/continuous_map_image.py +155 -0
  59. sage/manifolds/differentiable/affine_connection.py +2475 -0
  60. sage/manifolds/differentiable/all.py +1 -0
  61. sage/manifolds/differentiable/automorphismfield.py +1383 -0
  62. sage/manifolds/differentiable/automorphismfield_group.py +604 -0
  63. sage/manifolds/differentiable/bundle_connection.py +1445 -0
  64. sage/manifolds/differentiable/characteristic_cohomology_class.py +1840 -0
  65. sage/manifolds/differentiable/chart.py +1241 -0
  66. sage/manifolds/differentiable/curve.py +1028 -0
  67. sage/manifolds/differentiable/de_rham_cohomology.py +541 -0
  68. sage/manifolds/differentiable/degenerate.py +559 -0
  69. sage/manifolds/differentiable/degenerate_submanifold.py +1671 -0
  70. sage/manifolds/differentiable/diff_form.py +1658 -0
  71. sage/manifolds/differentiable/diff_form_module.py +1062 -0
  72. sage/manifolds/differentiable/diff_map.py +1315 -0
  73. sage/manifolds/differentiable/differentiable_submanifold.py +291 -0
  74. sage/manifolds/differentiable/examples/all.py +1 -0
  75. sage/manifolds/differentiable/examples/euclidean.py +2517 -0
  76. sage/manifolds/differentiable/examples/real_line.py +897 -0
  77. sage/manifolds/differentiable/examples/sphere.py +1186 -0
  78. sage/manifolds/differentiable/examples/symplectic_space.py +187 -0
  79. sage/manifolds/differentiable/examples/symplectic_space_test.py +40 -0
  80. sage/manifolds/differentiable/integrated_curve.py +4035 -0
  81. sage/manifolds/differentiable/levi_civita_connection.py +841 -0
  82. sage/manifolds/differentiable/manifold.py +4254 -0
  83. sage/manifolds/differentiable/manifold_homset.py +1826 -0
  84. sage/manifolds/differentiable/metric.py +3032 -0
  85. sage/manifolds/differentiable/mixed_form.py +1507 -0
  86. sage/manifolds/differentiable/mixed_form_algebra.py +559 -0
  87. sage/manifolds/differentiable/multivector_module.py +800 -0
  88. sage/manifolds/differentiable/multivectorfield.py +1520 -0
  89. sage/manifolds/differentiable/poisson_tensor.py +268 -0
  90. sage/manifolds/differentiable/pseudo_riemannian.py +755 -0
  91. sage/manifolds/differentiable/pseudo_riemannian_submanifold.py +1839 -0
  92. sage/manifolds/differentiable/scalarfield.py +1343 -0
  93. sage/manifolds/differentiable/scalarfield_algebra.py +472 -0
  94. sage/manifolds/differentiable/symplectic_form.py +910 -0
  95. sage/manifolds/differentiable/symplectic_form_test.py +220 -0
  96. sage/manifolds/differentiable/tangent_space.py +412 -0
  97. sage/manifolds/differentiable/tangent_vector.py +616 -0
  98. sage/manifolds/differentiable/tensorfield.py +4665 -0
  99. sage/manifolds/differentiable/tensorfield_module.py +963 -0
  100. sage/manifolds/differentiable/tensorfield_paral.py +2450 -0
  101. sage/manifolds/differentiable/tensorfield_paral_test.py +16 -0
  102. sage/manifolds/differentiable/vector_bundle.py +1728 -0
  103. sage/manifolds/differentiable/vectorfield.py +1717 -0
  104. sage/manifolds/differentiable/vectorfield_module.py +2445 -0
  105. sage/manifolds/differentiable/vectorframe.py +1832 -0
  106. sage/manifolds/family.py +270 -0
  107. sage/manifolds/local_frame.py +1490 -0
  108. sage/manifolds/manifold.py +3090 -0
  109. sage/manifolds/manifold_homset.py +452 -0
  110. sage/manifolds/operators.py +359 -0
  111. sage/manifolds/point.py +994 -0
  112. sage/manifolds/scalarfield.py +3718 -0
  113. sage/manifolds/scalarfield_algebra.py +629 -0
  114. sage/manifolds/section.py +3111 -0
  115. sage/manifolds/section_module.py +831 -0
  116. sage/manifolds/structure.py +229 -0
  117. sage/manifolds/subset.py +2764 -0
  118. sage/manifolds/subsets/all.py +1 -0
  119. sage/manifolds/subsets/closure.py +131 -0
  120. sage/manifolds/subsets/pullback.py +885 -0
  121. sage/manifolds/topological_submanifold.py +891 -0
  122. sage/manifolds/trivialization.py +733 -0
  123. sage/manifolds/utilities.py +1348 -0
  124. sage/manifolds/vector_bundle.py +1342 -0
  125. sage/manifolds/vector_bundle_fiber.py +332 -0
  126. sage/manifolds/vector_bundle_fiber_element.py +111 -0
  127. sage/matrix/all__sagemath_symbolics.py +1 -0
  128. sage/matrix/matrix_symbolic_dense.cpython-314t-x86_64-linux-musl.so +0 -0
  129. sage/matrix/matrix_symbolic_dense.pxd +6 -0
  130. sage/matrix/matrix_symbolic_dense.pyx +1022 -0
  131. sage/matrix/matrix_symbolic_sparse.cpython-314t-x86_64-linux-musl.so +0 -0
  132. sage/matrix/matrix_symbolic_sparse.pxd +6 -0
  133. sage/matrix/matrix_symbolic_sparse.pyx +1029 -0
  134. sage/modules/all__sagemath_symbolics.py +1 -0
  135. sage/modules/vector_callable_symbolic_dense.py +105 -0
  136. sage/modules/vector_symbolic_dense.py +116 -0
  137. sage/modules/vector_symbolic_sparse.py +118 -0
  138. sage/rings/all__sagemath_symbolics.py +4 -0
  139. sage/rings/asymptotic/all.py +6 -0
  140. sage/rings/asymptotic/asymptotic_expansion_generators.py +1485 -0
  141. sage/rings/asymptotic/asymptotic_ring.py +4858 -0
  142. sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +4153 -0
  143. sage/rings/asymptotic/growth_group.py +5373 -0
  144. sage/rings/asymptotic/growth_group_cartesian.py +1400 -0
  145. sage/rings/asymptotic/term_monoid.py +5237 -0
  146. sage/rings/function_field/all__sagemath_symbolics.py +2 -0
  147. sage/rings/polynomial/all__sagemath_symbolics.py +1 -0
  148. sage/symbolic/all.py +15 -0
  149. sage/symbolic/assumptions.py +985 -0
  150. sage/symbolic/benchmark.py +93 -0
  151. sage/symbolic/callable.py +459 -0
  152. sage/symbolic/complexity_measures.py +35 -0
  153. sage/symbolic/constants.py +1287 -0
  154. sage/symbolic/expression_conversion_algebraic.py +310 -0
  155. sage/symbolic/expression_conversion_sympy.py +317 -0
  156. sage/symbolic/expression_conversions.py +1713 -0
  157. sage/symbolic/function_factory.py +355 -0
  158. sage/symbolic/integration/all.py +1 -0
  159. sage/symbolic/integration/external.py +270 -0
  160. sage/symbolic/integration/integral.py +1115 -0
  161. sage/symbolic/maxima_wrapper.py +162 -0
  162. sage/symbolic/operators.py +267 -0
  163. sage/symbolic/random_tests.py +462 -0
  164. sage/symbolic/relation.py +1907 -0
  165. sage/symbolic/ring.cpython-314t-x86_64-linux-musl.so +0 -0
  166. sage/symbolic/ring.pxd +5 -0
  167. sage/symbolic/ring.pyx +1396 -0
  168. sage/symbolic/subring.py +1025 -0
  169. sage/symbolic/symengine.py +19 -0
  170. sage/symbolic/tests.py +40 -0
  171. sage/symbolic/units.py +1470 -0
@@ -0,0 +1,4035 @@
1
+ # sage_setup: distribution = sagemath-symbolics
2
+ r"""
3
+ Integrated Curves and Geodesics in Manifolds
4
+
5
+ Given a differentiable manifold `M`, an *integrated curve* in `M`
6
+ is a differentiable curve constructed as a solution to a system of
7
+ second order differential equations.
8
+
9
+ Integrated curves are implemented by the class :class:`IntegratedCurve`, from
10
+ which the classes :class:`IntegratedAutoparallelCurve` and
11
+ :class:`IntegratedGeodesic` inherit.
12
+
13
+ .. RUBRIC:: Example: a geodesic in the hyperbolic plane
14
+
15
+ First declare the hyperbolic plane as a 2-dimensional Riemannian manifold ``M``
16
+ and introduce the chart ``X`` corresponding to the Poincaré half-plane model::
17
+
18
+ sage: M = Manifold(2, 'M', structure='Riemannian')
19
+ sage: X.<x,y> = M.chart('x y:(0,+oo)')
20
+
21
+ Then set the metric to be the hyperbolic one::
22
+
23
+ sage: g = M.metric()
24
+ sage: g[0,0], g[1,1] = 1/y^2, 1/y^2
25
+ sage: g.display()
26
+ g = y^(-2) dx⊗dx + y^(-2) dy⊗dy
27
+
28
+ Pick an initial point and an initial tangent vector::
29
+
30
+ sage: p = M((0,1), name='p')
31
+ sage: v = M.tangent_space(p)((1,3/2), name='v')
32
+ sage: v.display()
33
+ v = ∂/∂x + 3/2 ∂/∂y
34
+
35
+ Declare a geodesic with such initial conditions, denoting by `t` the
36
+ corresponding affine parameter::
37
+
38
+ sage: t = var('t')
39
+ sage: c = M.integrated_geodesic(g, (t, 0, 10), v, name='c')
40
+
41
+ Numerically integrate the geodesic (see :meth:`~IntegratedCurve.solve` for
42
+ all possible options, including the choice of the numerical algorithm)::
43
+
44
+ sage: sol = c.solve() # needs scipy
45
+
46
+ Plot the geodesic after interpolating the solution ``sol``::
47
+
48
+ sage: interp = c.interpolate() # needs scipy
49
+
50
+ sage: # needs scipy sage.plot
51
+ sage: graph = c.plot_integrated()
52
+ sage: p_plot = p.plot(size=30, label_offset=-0.07, fontsize=20)
53
+ sage: v_plot = v.plot(label_offset=0.05, fontsize=20)
54
+ sage: graph + p_plot + v_plot
55
+ Graphics object consisting of 5 graphics primitives
56
+
57
+ .. PLOT::
58
+
59
+ M = Manifold(2, 'M', structure='Riemannian')
60
+ X = M.chart('x y'); x, y = X[:]
61
+ g = M.metric()
62
+ g[0,0], g[1,1] = 1/y**2, 1/y**2
63
+ p = M((0,1), name='p')
64
+ v = M.tangent_space(p)((1,3/2), name='v')
65
+ t = var('t')
66
+ c = M.integrated_geodesic(g, (t, 0, 10), v, name='c')
67
+ sol = c.solve()
68
+ interp = c.interpolate()
69
+ graph = c.plot_integrated()
70
+ p_plot = p.plot(size=30, label_offset=-0.07, fontsize=20)
71
+ v_plot = v.plot(label_offset=0.05, fontsize=20)
72
+ sphinx_plot(graph + p_plot + v_plot)
73
+
74
+ `c` is a differentiable curve in `M` and inherits from the properties of
75
+ :class:`~sage.manifolds.differentiable.curve.DifferentiableCurve`::
76
+
77
+ sage: c.domain()
78
+ Real interval (0, 10)
79
+ sage: c.codomain()
80
+ 2-dimensional Riemannian manifold M
81
+ sage: c.display()
82
+ c: (0, 10) → M
83
+
84
+ In particular, its value at `t=1` is::
85
+
86
+ sage: c(1) # needs scipy
87
+ Point on the 2-dimensional Riemannian manifold M
88
+
89
+ which corresponds to the following `(x, y)` coordinates::
90
+
91
+ sage: X(c(1)) # abs tol 1e-12 # needs scipy
92
+ (2.4784140715580136, 1.5141683866138937)
93
+
94
+ AUTHORS:
95
+
96
+ - Karim Van Aelst (2017): initial version
97
+ - Florentin Jaffredo (2018): integration over multiple charts, use of
98
+ ``fast_callable`` to improve the computation speed
99
+ """
100
+
101
+ # **********************************************************************
102
+ # Copyright (C) 2017 Karim Van Aelst <karim.van-aelst@obspm.fr>
103
+ #
104
+ # Distributed under the terms of the GNU General Public License (GPL)
105
+ # as published by the Free Software Foundation; either version 2 of
106
+ # the License, or (at your option) any later version.
107
+ # https://www.gnu.org/licenses/
108
+ # **********************************************************************
109
+
110
+ from random import shuffle
111
+
112
+ from sage.arith.srange import srange
113
+ from sage.calculus.desolvers import desolve_odeint, desolve_system_rk4
114
+ from sage.calculus.interpolation import Spline
115
+ from sage.ext.fast_callable import fast_callable
116
+ from sage.manifolds.chart import Chart
117
+ from sage.manifolds.differentiable.curve import DifferentiableCurve
118
+ from sage.manifolds.differentiable.tangent_vector import TangentVector
119
+ from sage.misc.decorators import options
120
+ from sage.misc.functional import numerical_approx
121
+ from sage.misc.lazy_import import lazy_import
122
+ from sage.rings.infinity import Infinity
123
+ from sage.symbolic.expression import Expression
124
+ from sage.symbolic.ring import SR
125
+
126
+ lazy_import('scipy.integrate', 'ode')
127
+
128
+
129
+ class IntegratedCurve(DifferentiableCurve):
130
+ r"""
131
+ Given a chart with coordinates denoted `(x_{1}, \ldots, x_{n})`,
132
+ an instance of :class:`IntegratedCurve` is a curve
133
+ `t \mapsto (x_{1}(t), \ldots, x_{n}(t))` constructed as a
134
+ solution to a system of second order differential equations
135
+ satisfied by the coordinate curves `t \mapsto x_{i}(t)`.
136
+
137
+ INPUT:
138
+
139
+ - ``parent`` --
140
+ :class:`~sage.manifolds.differentiable.manifold_homset.IntegratedCurveSet`
141
+ the set of curves `\mathrm{Hom_{integrated}}(I, M)` to which the
142
+ curve belongs
143
+ - ``equations_rhs`` -- list of the right-hand sides of the equations
144
+ on the velocities only (the term *velocity* referring to the
145
+ derivatives `d x_{i} / dt` of the coordinate curves)
146
+ - ``velocities`` -- list of the symbolic expressions used in
147
+ ``equations_rhs`` to denote the velocities
148
+ - ``curve_parameter`` -- symbolic expression used in
149
+ ``equations_rhs`` to denote the parameter of the curve (denoted
150
+ `t` in the descriptions above)
151
+ - ``initial_tangent_vector`` --
152
+ :class:`~sage.manifolds.differentiable.tangent_vector.TangentVector`
153
+ initial tangent vector of the curve
154
+ - ``chart`` -- (default: ``None``) chart on the manifold in
155
+ which the equations are given; if ``None`` the default chart
156
+ of the manifold is assumed
157
+ - ``name`` -- (default: ``None``) string; symbol given to the curve
158
+ - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to
159
+ denote the curve; if none is provided, ``name`` will be used
160
+
161
+ EXAMPLES:
162
+
163
+ Motion of a charged particle in an axial magnetic field linearly
164
+ increasing in time and exponentially decreasing in space:
165
+
166
+ .. MATH::
167
+
168
+ \mathbf{B}(t,\mathbf{x}) = \frac{B_{0}t}{T} \exp \left(
169
+ -\frac{ x_{1}^{2} + x_{2}^{2} }{ L^{2} } \right) \mathbf{e_{3}}.
170
+
171
+ Equations of motion are:
172
+
173
+ .. MATH::
174
+
175
+ \begin{aligned}
176
+ \ddot{x}_{1}(t) &= \frac{qB(t,\mathbf{x}(t))}{m} \dot{x}_{2}(t), \\
177
+ \ddot{x}_{2}(t) &= -\frac{qB(t, \mathbf{x}(t))}{m} \dot{x}_{1}(t), \\
178
+ \ddot{x}_{3}(t) &= 0.
179
+ \end{aligned}
180
+
181
+ Start with declaring a chart on a 3-dimensional manifold and the
182
+ symbolic expressions denoting the velocities and the various
183
+ parameters::
184
+
185
+ sage: M = Manifold(3, 'M', start_index=1)
186
+ sage: X.<x1,x2,x3> = M.chart()
187
+ sage: var('t B_0 m q L T')
188
+ (t, B_0, m, q, L, T)
189
+ sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2)
190
+ sage: D = X.symbolic_velocities(); D
191
+ [Dx1, Dx2, Dx3]
192
+ sage: eqns = [q*B/m*D[1], -q*B/m*D[0], 0]
193
+
194
+ Set the initial conditions::
195
+
196
+ sage: p = M.point((0,0,0), name='p')
197
+ sage: Tp = M.tangent_space(p)
198
+ sage: v = Tp((1,0,1))
199
+
200
+ Declare an integrated curve and display information relative to it::
201
+
202
+ sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c',
203
+ ....: verbose=True)
204
+ The curve was correctly set.
205
+ Parameters appearing in the differential system defining the
206
+ curve are [B_0, L, T, m, q].
207
+ sage: c
208
+ Integrated curve c in the 3-dimensional differentiable
209
+ manifold M
210
+ sage: sys = c.system(verbose=True)
211
+ Curve c in the 3-dimensional differentiable manifold M
212
+ integrated over the Real interval (0, 5) as a solution to the
213
+ following system, written with respect to
214
+ Chart (M, (x1, x2, x3)):
215
+ <BLANKLINE>
216
+ Initial point: Point p on the 3-dimensional differentiable
217
+ manifold M with coordinates [0, 0, 0] with respect to
218
+ Chart (M, (x1, x2, x3))
219
+ Initial tangent vector: Tangent vector at Point p on
220
+ the 3-dimensional differentiable manifold M with
221
+ components [1, 0, 1] with respect to Chart (M, (x1, x2, x3))
222
+ <BLANKLINE>
223
+ d(x1)/dt = Dx1
224
+ d(x2)/dt = Dx2
225
+ d(x3)/dt = Dx3
226
+ d(Dx1)/dt = B_0*Dx2*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m)
227
+ d(Dx2)/dt = -B_0*Dx1*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m)
228
+ d(Dx3)/dt = 0
229
+ <BLANKLINE>
230
+
231
+ Generate a solution of the system and an interpolation of this
232
+ solution::
233
+
234
+ sage: sol = c.solve(step=0.2, # needs scipy
235
+ ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1},
236
+ ....: solution_key='carac time 1', verbose=True)
237
+ Performing numerical integration with method 'odeint'...
238
+ Numerical integration completed.
239
+ <BLANKLINE>
240
+ Checking all points are in the chart domain...
241
+ All points are in the chart domain.
242
+ <BLANKLINE>
243
+ The resulting list of points was associated with the key
244
+ 'carac time 1' (if this key already referred to a former
245
+ numerical solution, such a solution was erased).
246
+ sage: interp = c.interpolate(solution_key='carac time 1', # needs scipy
247
+ ....: interpolation_key='interp 1', verbose=True)
248
+ Performing cubic spline interpolation by default...
249
+ Interpolation completed and associated with the key 'interp 1'
250
+ (if this key already referred to a former interpolation,
251
+ such an interpolation was erased).
252
+
253
+ Such an interpolation is required to evaluate the curve and the
254
+ vector tangent to the curve for any value of the curve parameter::
255
+
256
+ sage: # needs scipy
257
+ sage: p = c(1.9, verbose=True)
258
+ Evaluating point coordinates from the interpolation associated
259
+ with the key 'interp 1' by default...
260
+ sage: p
261
+ Point on the 3-dimensional differentiable manifold M
262
+ sage: p.coordinates() # abs tol 1e-12
263
+ (1.377689074756845, -0.900114533011232, 1.9)
264
+ sage: v2 = c.tangent_vector_eval_at(4.3, verbose=True)
265
+ Evaluating tangent vector components from the interpolation
266
+ associated with the key 'interp 1' by default...
267
+ sage: v2
268
+ Tangent vector at Point on the 3-dimensional differentiable
269
+ manifold M
270
+ sage: v2[:] # abs tol 1e-12
271
+ [-0.9425156073651124, -0.33724314284285434, 1.0]
272
+
273
+ Plotting a numerical solution (with or without its tangent vector
274
+ field) also requires the solution to be interpolated at least once::
275
+
276
+ sage: c_plot_2d_1 = c.plot_integrated(ambient_coords=[x1, x2], # needs scipy
277
+ ....: interpolation_key='interp 1', thickness=2.5,
278
+ ....: display_tangent=True, plot_points=200,
279
+ ....: plot_points_tangent=10, scale=0.5,
280
+ ....: color='blue', color_tangent='red',
281
+ ....: verbose=True)
282
+ A tiny final offset equal to 0.000251256281407035 was introduced
283
+ for the last point in order to safely compute it from the
284
+ interpolation.
285
+ sage: c_plot_2d_1 # needs scipy sage.plot
286
+ Graphics object consisting of 11 graphics primitives
287
+
288
+ .. PLOT::
289
+
290
+ M = Manifold(3, 'M')
291
+ X = M.chart('x1 x2 x3'); x1, x2, x3 = X[:]
292
+ t, B_0, m, q, L, T = var('t B_0 m q L T')
293
+ B = B_0*t/T*exp(-(x1**2 + x2**2)/L**2)
294
+ D = X.symbolic_velocities()
295
+ eqns = [q*B/m*D[1], -q*B/m*D[0], 0]
296
+ p = M.point((0,0,0), name='p')
297
+ Tp = M.tangent_space(p)
298
+ v = Tp((1,0,1))
299
+ c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c')
300
+ sol = c.solve(step=0.2,
301
+ parameters_values={B_0:1, m:1, q:1, L:10, T:1},
302
+ solution_key='carac time 1')
303
+ interp = c.interpolate(solution_key='carac time 1',
304
+ interpolation_key='interp 1')
305
+ c_plot_2d_1 = c.plot_integrated(ambient_coords=[x1, x2],
306
+ interpolation_key='interp 1', thickness=2.5,
307
+ display_tangent=True, plot_points=200,
308
+ plot_points_tangent=10, scale=0.5, color='blue',
309
+ color_tangent='red')
310
+ sphinx_plot(c_plot_2d_1)
311
+
312
+ An instance of :class:`IntegratedCurve` may store several numerical
313
+ solutions and interpolations::
314
+
315
+ sage: # needs scipy
316
+ sage: sol = c.solve(step=0.2,
317
+ ....: parameters_values={B_0:1, m:1, q:1, L:10, T:100},
318
+ ....: solution_key='carac time 100')
319
+ sage: interp = c.interpolate(solution_key='carac time 100',
320
+ ....: interpolation_key='interp 100')
321
+ sage: c_plot_3d_100 = c.plot_integrated(interpolation_key='interp 100', # needs sage.plot
322
+ ....: thickness=2.5, display_tangent=True,
323
+ ....: plot_points=200, plot_points_tangent=10,
324
+ ....: scale=0.5, color='green',
325
+ ....: color_tangent='orange')
326
+ sage: c_plot_3d_1 = c.plot_integrated(interpolation_key='interp 1', # needs sage.plot
327
+ ....: thickness=2.5, display_tangent=True,
328
+ ....: plot_points=200, plot_points_tangent=10,
329
+ ....: scale=0.5, color='blue',
330
+ ....: color_tangent='red')
331
+ sage: c_plot_3d_1 + c_plot_3d_100 # needs sage.plot
332
+ Graphics3d Object
333
+
334
+ .. PLOT::
335
+
336
+ M = Manifold(3, 'M')
337
+ X = M.chart('x1 x2 x3'); x1, x2, x3 = X[:]
338
+ t, B_0, m, q, L, T = var('t B_0 m q L T')
339
+ B = B_0*t/T*exp(-(x1**2 + x2**2)/L**2)
340
+ D = X.symbolic_velocities()
341
+ eqns = [q*B/m*D[1], -q*B/m*D[0], 0]
342
+ p = M.point((0,0,0), name='p')
343
+ Tp = M.tangent_space(p)
344
+ v = Tp((1,0,1))
345
+ c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c')
346
+ sol = c.solve(step=0.2, parameters_values={B_0:1, m:1, q:1, L:10, T:1},
347
+ solution_key='carac time 1')
348
+ interp = c.interpolate(solution_key='carac time 1',
349
+ interpolation_key='interp 1')
350
+ sol = c.solve(step=0.2, parameters_values={B_0:1, m:1, q:1, L:10, T:100},
351
+ solution_key='carac time 100')
352
+ interp = c.interpolate(solution_key='carac time 100',
353
+ interpolation_key='interp 100')
354
+ c_plot_3d_1 = c.plot_integrated(interpolation_key='interp 1',
355
+ thickness=2.5, display_tangent=True,
356
+ plot_points=200, plot_points_tangent=10,
357
+ scale=0.5, color='blue', color_tangent='red')
358
+ c_plot_3d_100 = c.plot_integrated(interpolation_key='interp 100',
359
+ thickness=2.5, display_tangent=True,
360
+ plot_points=200, plot_points_tangent=10,
361
+ scale=0.5, color='green',
362
+ color_tangent='orange')
363
+ graph = c_plot_3d_1 + c_plot_3d_100
364
+ sphinx_plot(graph)
365
+ """
366
+
367
+ def __init__(self, parent, equations_rhs, velocities,
368
+ curve_parameter, initial_tangent_vector, chart=None,
369
+ name=None, latex_name=None, verbose=False,
370
+ across_charts=False):
371
+ r"""
372
+ Construct a curve defined by a system of second order
373
+ differential equations in the coordinate functions.
374
+
375
+ TESTS::
376
+
377
+ sage: M = Manifold(3, 'M')
378
+ sage: X.<x1,x2,x3> = M.chart()
379
+ sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T')
380
+ sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2)
381
+ sage: D = X.symbolic_velocities()
382
+ sage: eqns = [q*B/m*D[1], -q*B/m*D[0], 0]
383
+ sage: p = M.point((0,0,0), name='p')
384
+ sage: Tp = M.tangent_space(p)
385
+ sage: v = Tp((1,0,1))
386
+ sage: c = M.integrated_curve(eqns + [x1], D, (t, 0, 5), v,
387
+ ....: name='c')
388
+ Traceback (most recent call last):
389
+ ...
390
+ ValueError: number of equations should equal codomain
391
+ dimension
392
+ sage: c = M.integrated_curve(eqns, D + [x1], (t, 0, 5), v,
393
+ ....: name='c')
394
+ Traceback (most recent call last):
395
+ ...
396
+ ValueError: number of velocities should equal codomain
397
+ dimension
398
+ sage: c = M.integrated_curve(eqns, D,(t,-oo,5), v, name='c')
399
+ Traceback (most recent call last):
400
+ ...
401
+ ValueError: both boundaries of the interval defining the
402
+ domain of a Homset of integrated curves need to be finite
403
+ sage: c = M.integrated_curve(eqns, D, (t,0,5), x1, name='c')
404
+ Traceback (most recent call last):
405
+ ...
406
+ TypeError: x1 should be a tangent vector
407
+
408
+ sage: c = M.integrated_curve(eqns, D, (x1,0,5), v, name='c')
409
+ Traceback (most recent call last):
410
+ ...
411
+ ValueError: x1 should not be used as the curve parameter
412
+ since it also denotes a coordinate or a velocity
413
+ sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c'); c
414
+ Integrated curve c in the 3-dimensional differentiable
415
+ manifold M
416
+ sage: TestSuite(c).run()
417
+
418
+ Check that :issue:`28669` is fixed::
419
+
420
+ sage: E.<r,phi> = EuclideanSpace(coordinates='polar')
421
+ sage: p = E((1, 0)) # the initial point
422
+ sage: v = E.tangent_space(p)((2, 1)) # the initial vector
423
+ sage: t = var('t')
424
+ sage: c = E.integrated_geodesic(E.metric(), (t, 0, 10), v); c
425
+ Integrated geodesic in the Euclidean plane E^2
426
+ """
427
+ from sage.symbolic.ring import SR
428
+
429
+ # start with parent class method to initialize the four last
430
+ # arguments:
431
+ DifferentiableCurve.__init__(self, parent, name=name,
432
+ latex_name=latex_name)
433
+
434
+ # check argument 'parent': 't_min' and 't_max' below are only
435
+ # allowed to be either expressions of finite real values:
436
+ domain = self.domain()
437
+ t_min = domain.lower_bound()
438
+ t_max = domain.upper_bound()
439
+ if t_min == -Infinity or t_max == +Infinity:
440
+ raise ValueError("both boundaries of the interval " +
441
+ "need to be finite")
442
+
443
+ codomain = self.codomain()
444
+
445
+ # check argument 'equations_rhs':
446
+ dim = codomain.dim()
447
+
448
+ if not isinstance(equations_rhs, dict):
449
+ if len(equations_rhs) != dim:
450
+ raise ValueError("number of equations should equal " +
451
+ "codomain dimension")
452
+ else:
453
+ for eq in equations_rhs.values():
454
+ if len(eq) != dim:
455
+ raise ValueError("number of equations should equal " +
456
+ "codomain dimension")
457
+
458
+ # check the chart:
459
+ if chart is not None:
460
+ if chart not in codomain.atlas():
461
+ raise ValueError("{} should be a chart ".format(chart) +
462
+ "on the {}".format(codomain))
463
+ else:
464
+ chart = codomain.default_chart()
465
+
466
+ # check argument 'velocities':
467
+ if len(velocities) != dim:
468
+ raise ValueError("number of velocities should equal " +
469
+ "codomain dimension")
470
+ # in particular, check that no velocity coincides with a
471
+ # coordinate:
472
+ for vel in velocities:
473
+ if vel in chart[:]:
474
+ str_error = "{} should not be used as a ".format(vel)
475
+ str_error += "velocity since it also denotes "
476
+ str_error += "a coordinate"
477
+ raise ValueError(str_error)
478
+
479
+ # check argument 'curve_parameter':
480
+ if not isinstance(curve_parameter, Expression):
481
+ raise TypeError("{} should be ".format(curve_parameter) +
482
+ "a symbolic expression")
483
+ # in particular, check that it does not coincide with a
484
+ # coordinate or a velocity:
485
+ coords_vels = list(chart[:]) + list(velocities)
486
+ if curve_parameter in coords_vels:
487
+ str_error = "{} should not be used ".format(curve_parameter)
488
+ str_error += "as the curve parameter since it also denotes "
489
+ str_error += "a coordinate or a velocity"
490
+ raise ValueError(str_error)
491
+ # the various algorithms called in 'solve' method are in charge
492
+ # of raising errors about possibly remaining problems regarding
493
+ # 'curve_parameter'
494
+
495
+ # check argument 'initial_tangent_vector':
496
+ if not isinstance(initial_tangent_vector, TangentVector):
497
+ raise TypeError("{} ".format(initial_tangent_vector) +
498
+ "should be a tangent vector")
499
+ initial_pt = initial_tangent_vector.parent().base_point()
500
+ # line above retrieves the initial point as the base point of
501
+ # the tangent space to which the initial tangent vector belongs
502
+ initial_pt_coords = initial_pt.coordinates(chart)
503
+ # prepare attribute '_parameters':
504
+ announced_variables = set(coords_vels + [curve_parameter])
505
+ parameters = set()
506
+ # extract all the variables appearing in the equations:
507
+ for eqn in equations_rhs:
508
+ if isinstance(eqn, Expression):
509
+ # some right hand sides
510
+ # might merely be real numbers and not expressions, so that
511
+ # they do not contain any variable, and method 'variables'
512
+ # could not be called on them
513
+ parameters = parameters.union(eqn.variables())
514
+ # remove the Expressions that should not be treated as
515
+ # parameters (i.e. the coordinate functions, the velocities and
516
+ # the curve parameter):
517
+ parameters = parameters.difference(announced_variables)
518
+ # extract all the variables appearing in the boundaries:
519
+ if isinstance(t_min, Expression):
520
+ parameters = parameters.union(t_min.variables())
521
+ if isinstance(t_max, Expression):
522
+ parameters = parameters.union(t_max.variables())
523
+ # extract all the variables appearing in the initial point
524
+ # coordinates:
525
+ for coord in initial_pt_coords:
526
+ if isinstance(coord, Expression):
527
+ parameters = parameters.union(coord.variables())
528
+ # extract all the variables appearing in the initial tangent
529
+ # vector components:
530
+ initial_coord_basis = chart.frame().at(initial_pt)
531
+ initial_tgt_vec_comps = initial_tangent_vector[initial_coord_basis,:]
532
+ for comp in initial_tgt_vec_comps:
533
+ if isinstance(comp, Expression):
534
+ parameters = parameters.union(comp.variables())
535
+
536
+ # check at this stage that no parameter coincides with a
537
+ # coordinate, a velocity, or the curve parameter; this would
538
+ # mean that an Expression used to denote either a bound, a
539
+ # coordinate of the initial point or a component of the initial
540
+ # tangent vector coincides with a coordinate, a velocity or the
541
+ # curve parameter (which would make no sense):
542
+ if len(parameters) != 0:
543
+ for param in parameters:
544
+ if param in announced_variables:
545
+ str_error = "{} should not be used ".format(param)
546
+ str_error += "as a parameter since it also denotes "
547
+ str_error += "a coordinate, a velocity or the "
548
+ str_error += "curve parameter"
549
+ raise ValueError(str_error)
550
+
551
+ # define all attributes
552
+ if not isinstance(equations_rhs, dict):
553
+ self._equations_rhs = list(equations_rhs) # converts to list
554
+ # since might not already be a list (which is later required)
555
+ else: # case multi charts
556
+ self._equations_rhs = equations_rhs
557
+
558
+ self._across_charts = across_charts
559
+ if across_charts:
560
+ # pre-compute the changes of chart for faster switching
561
+ # approx gain : 200 ms per switch
562
+ self._fast_changes_of_frame = {}
563
+ self._fast_changes_of_chart = {}
564
+ for CoF in self._codomain.changes_of_frame():
565
+ M = self._codomain.changes_of_frame()[CoF][CoF[1], :, CoF[1]._chart]
566
+ M = M.apply_map(lambda e: e.expr())
567
+ M = M.numpy()
568
+ for i in range(dim):
569
+ for j in range(dim):
570
+ M[i, j] = fast_callable(SR(M[i, j]), vars=list(CoF[1]._chart[:]), domain=float)
571
+
572
+ import numpy as np
573
+
574
+ def fast_CoF(pos, vel, M=M):
575
+ # using default arguments for binding (ugly python)
576
+ # print(det(*pos))
577
+ return list(np.dot([[M[j, i](*pos) for i in range(dim)]
578
+ for j in range(dim)], vel))
579
+
580
+ self._fast_changes_of_frame[CoF] = fast_CoF
581
+
582
+ for CoC in self._codomain._coord_changes:
583
+ transf = self._codomain._coord_changes[CoC]._transf
584
+ fast_transf = [fast_callable(f.expr(), vars=list(CoC[0][:]), domain=float)
585
+ for f in transf]
586
+ self._fast_changes_of_chart[CoC] = fast_transf
587
+
588
+ self._velocities = list(velocities) # converts to list
589
+ # since might not already be a list (which is later required)
590
+ self._curve_parameter = curve_parameter
591
+ self._initial_tangent_vector = initial_tangent_vector
592
+ self._chart = chart
593
+ self._parameters = parameters
594
+ self._ode_solver = None # if needed, becomes an instance of
595
+ # 'ode_solver', which performs most of the numerical integrations
596
+ # offered by method 'solve'
597
+ self._solutions = {} # dictionary containing all numerically
598
+ # computed lists of points of the curve, the keys being chosen
599
+ # by the user when calling method 'solve'
600
+ self._interpolations = {} # dictionary containing lists of
601
+ # interpolation objects, each interpolation object implementing
602
+ # the interpolation of one of the numerical coordinate curves,
603
+ # and the keys being chosen by the user when calling
604
+ # method 'interpolate'
605
+
606
+ if verbose:
607
+ print("The curve was correctly set.")
608
+ if self._parameters:
609
+ print("Parameters appearing in the differential " +
610
+ "system defining the curve are " +
611
+ "{}.".format(sorted(self._parameters, key=str)))
612
+ else:
613
+ print("No parameter appears in the differential " +
614
+ "system defining the curve.")
615
+
616
+ def _repr_(self):
617
+ r"""
618
+ Return a string representation of ``self``.
619
+
620
+ TESTS::
621
+
622
+ sage: M = Manifold(3, 'M')
623
+ sage: X.<x1,x2,x3> = M.chart()
624
+ sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T')
625
+ sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2)
626
+ sage: D = X.symbolic_velocities()
627
+ sage: eqns = [q*B/m*D[1], -q*B/m*D[0], 0]
628
+ sage: p = M.point((0,0,0), name='p')
629
+ sage: Tp = M.tangent_space(p)
630
+ sage: v = Tp((1,0,1))
631
+ sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v) ; c
632
+ Integrated curve in the 3-dimensional differentiable
633
+ manifold M
634
+ sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c'); c
635
+ Integrated curve c in the 3-dimensional differentiable
636
+ manifold M
637
+ """
638
+
639
+ description = "Integrated curve "
640
+ if self._name is not None:
641
+ description += self._name + " "
642
+ description += "in the {}".format(self._codomain)
643
+ return description
644
+
645
+ def __reduce__(self):
646
+ r"""
647
+ Reduction function for the pickle protocole.
648
+
649
+ TESTS::
650
+
651
+ sage: M = Manifold(3, 'M')
652
+ sage: X.<x1,x2,x3> = M.chart()
653
+ sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T')
654
+ sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2)
655
+ sage: D = X.symbolic_velocities()
656
+ sage: eqns = [q*B/m*D[1], -q*B/m*D[0], 0]
657
+ sage: p = M.point((0,0,0), name='p')
658
+ sage: Tp = M.tangent_space(p)
659
+ sage: v = Tp((1,0,1))
660
+ sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c')
661
+ sage: c.__reduce__()
662
+ (<class 'sage.manifolds.differentiable.manifold_homset.IntegratedCurveSet_with_category.element_class'>,
663
+ (Set of Morphisms from Real interval (0, 5) to
664
+ 3-dimensional differentiable manifold M in Category of homsets of
665
+ topological spaces which actually are integrated curves,
666
+ [B_0*Dx2*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m),
667
+ -B_0*Dx1*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m),
668
+ 0],
669
+ [Dx1, Dx2, Dx3],
670
+ t,
671
+ Tangent vector at Point p on the 3-dimensional
672
+ differentiable manifold M,
673
+ Chart (M, (x1, x2, x3)),
674
+ 'c',
675
+ 'c',
676
+ False,
677
+ False))
678
+
679
+ Test of pickling::
680
+
681
+ sage: loads(dumps(c))
682
+ Integrated curve c in the 3-dimensional differentiable
683
+ manifold M
684
+ """
685
+ return (type(self), (self.parent(), self._equations_rhs,
686
+ self._velocities, self._curve_parameter,
687
+ self._initial_tangent_vector, self._chart,
688
+ self._name, self._latex_name, False, self._across_charts))
689
+
690
+ def system(self, verbose=False):
691
+ r"""
692
+ Provide a detailed description of the system defining the curve
693
+ and return the system defining it: chart, equations and initial
694
+ conditions.
695
+
696
+ INPUT:
697
+
698
+ - ``verbose`` -- boolean (default: ``False``); prints a detailed
699
+ description of the curve
700
+
701
+ OUTPUT: list containing
702
+
703
+ * the equations
704
+ * the initial conditions
705
+ * the chart
706
+
707
+ EXAMPLES:
708
+
709
+ System defining an integrated curve::
710
+
711
+ sage: M = Manifold(3, 'M')
712
+ sage: X.<x1,x2,x3> = M.chart()
713
+ sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T')
714
+ sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2)
715
+ sage: D = X.symbolic_velocities()
716
+ sage: eqns = [q*B/m*D[1], -q*B/m*D[0], 0]
717
+ sage: p = M.point((0,0,0), name='p')
718
+ sage: Tp = M.tangent_space(p)
719
+ sage: v = Tp((1,0,1))
720
+ sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c')
721
+ sage: sys = c.system(verbose=True)
722
+ Curve c in the 3-dimensional differentiable manifold M
723
+ integrated over the Real interval (0, 5) as a solution to
724
+ the following system, written with respect to
725
+ Chart (M, (x1, x2, x3)):
726
+ <BLANKLINE>
727
+ Initial point: Point p on the 3-dimensional differentiable
728
+ manifold M with coordinates [0, 0, 0] with respect to
729
+ Chart (M, (x1, x2, x3))
730
+ Initial tangent vector: Tangent vector at Point p on the
731
+ 3-dimensional differentiable manifold M with
732
+ components [1, 0, 1] with respect to Chart (M, (x1, x2, x3))
733
+ <BLANKLINE>
734
+ d(x1)/dt = Dx1
735
+ d(x2)/dt = Dx2
736
+ d(x3)/dt = Dx3
737
+ d(Dx1)/dt = B_0*Dx2*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m)
738
+ d(Dx2)/dt = -B_0*Dx1*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m)
739
+ d(Dx3)/dt = 0
740
+ <BLANKLINE>
741
+ sage: sys_mute = c.system()
742
+ sage: sys_mute == sys
743
+ True
744
+ """
745
+
746
+ v0 = self._initial_tangent_vector
747
+ chart = self._chart
748
+
749
+ if verbose:
750
+ initial_tgt_space = v0.parent()
751
+ initial_pt = initial_tgt_space.base_point() # retrieves
752
+ # the initial point as the base point of the tangent space
753
+ # to which initial tangent vector belongs
754
+ initial_pt_coords = list(initial_pt.coordinates(chart))
755
+ # previous line converts to list since would otherwise be a
756
+ # tuple ; will raise error if coordinates in chart are not
757
+ # known
758
+
759
+ initial_coord_basis = chart.frame().at(initial_pt)
760
+ initial_tgt_vec_comps = v0[initial_coord_basis,:] # will
761
+ # raise error if components in coordinate basis are not
762
+ # known
763
+
764
+ description = "Curve "
765
+ if self._name is not None:
766
+ description += self._name + " "
767
+ description += "in the {} ".format(self.codomain())
768
+ description += "integrated over the "
769
+ description += "{} ".format(self.domain())
770
+ description += "as a solution to the following system, "
771
+ description += "written with respect to "
772
+ description += "{}:\n\n".format(chart)
773
+
774
+ description += "Initial point: {} ".format(initial_pt)
775
+ description += "with coordinates "
776
+ description += "{} ".format(initial_pt_coords)
777
+ description += "with respect to {}\n".format(chart)
778
+
779
+ description += "Initial tangent vector: {} ".format(v0)
780
+ description += "with components "
781
+ description += "{}".format(initial_tgt_vec_comps)
782
+ description += " with respect to {}\n\n".format(chart)
783
+
784
+ for coord_func,velocity in zip(chart[:],self._velocities):
785
+ description += "d({})/d{} = {}\n".format(coord_func,
786
+ self._curve_parameter,
787
+ velocity)
788
+
789
+ for velocity,eqn in zip(self._velocities,self._equations_rhs):
790
+ description += "d({})/d{} = {}\n".format(velocity,
791
+ self._curve_parameter,
792
+ eqn)
793
+
794
+ print(description)
795
+
796
+ return [self._equations_rhs, v0, chart]
797
+
798
+ def solve_analytical(self, verbose=False):
799
+ r"""
800
+ Solve the differential system defining ``self`` analytically.
801
+
802
+ Solve analytically the differential system defining a curve
803
+ using Maxima via Sage solver ``desolve_system``.
804
+ In case of success, the analytical expressions are added to the
805
+ dictionary of expressions representing the curve.
806
+ Pay attention to the fact that ``desolve_system`` only considers
807
+ initial conditions given at an initial parameter value equal to
808
+ zero, although the parameter range may not contain zero.
809
+ Yet, assuming that it does, values of the coordinates functions
810
+ at such zero initial parameter value are denoted by the name of
811
+ the coordinate function followed by the string ``'_0'``.
812
+
813
+ OUTPUT:
814
+
815
+ - list of the analytical expressions of the coordinate functions
816
+ (when the differential system could be solved analytically),
817
+ or boolean ``False`` (in case the differential system could
818
+ not be solved analytically)
819
+
820
+ EXAMPLES:
821
+
822
+ Analytical expression of the trajectory of a charged particle in
823
+ a uniform, stationary magnetic field::
824
+
825
+ sage: M = Manifold(3, 'M')
826
+ sage: X.<x1,x2,x3> = M.chart()
827
+ sage: [t, B_0, m, q] = var('t B_0 m q')
828
+ sage: D = X.symbolic_velocities()
829
+ sage: eqns = [q*B_0/m*D[1], -q*B_0/m*D[0], 0]
830
+ sage: p = M.point((0,0,0), name='p')
831
+ sage: Tp = M.tangent_space(p)
832
+ sage: v = Tp((1,0,1))
833
+ sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c')
834
+ sage: sys = c.system(verbose=True)
835
+ Curve c in the 3-dimensional differentiable manifold M
836
+ integrated over the Real interval (0, 5) as a solution to
837
+ the following system, written with respect to
838
+ Chart (M, (x1, x2, x3)):
839
+ <BLANKLINE>
840
+ Initial point: Point p on the 3-dimensional differentiable
841
+ manifold M with coordinates [0, 0, 0] with respect to
842
+ Chart (M, (x1, x2, x3))
843
+ Initial tangent vector: Tangent vector at Point p on the
844
+ 3-dimensional differentiable manifold M with components
845
+ [1, 0, 1] with respect to Chart (M, (x1, x2, x3))
846
+ <BLANKLINE>
847
+ d(x1)/dt = Dx1
848
+ d(x2)/dt = Dx2
849
+ d(x3)/dt = Dx3
850
+ d(Dx1)/dt = B_0*Dx2*q/m
851
+ d(Dx2)/dt = -B_0*Dx1*q/m
852
+ d(Dx3)/dt = 0
853
+ <BLANKLINE>
854
+ sage: sol = c.solve_analytical()
855
+ sage: c.expr()
856
+ ((B_0*q*x1_0 - Dx2_0*m*cos(B_0*q*t/m) +
857
+ Dx1_0*m*sin(B_0*q*t/m) + Dx2_0*m)/(B_0*q),
858
+ (B_0*q*x2_0 + Dx1_0*m*cos(B_0*q*t/m) +
859
+ Dx2_0*m*sin(B_0*q*t/m) - Dx1_0*m)/(B_0*q),
860
+ Dx3_0*t + x3_0)
861
+ """
862
+
863
+ from sage.calculus.desolvers import desolve_system
864
+ from sage.calculus.functional import diff
865
+ from sage.calculus.var import function
866
+ from sage.symbolic.assumptions import assume, forget
867
+ from sage.symbolic.ring import var
868
+
869
+ dim = self.codomain().dim()
870
+ i0 = self.codomain().start_index()
871
+ des = self._velocities + self._equations_rhs
872
+ par = self._curve_parameter
873
+
874
+ for param in self._parameters:
875
+ assume(param != 0)
876
+
877
+ y = []
878
+ for i in range(2*dim):
879
+ name = "y{}".format(i+i0)
880
+ y += [function(name)(par)]
881
+
882
+ for i in range(dim):
883
+ vel = self._velocities[i]
884
+ des[i] = des[i].substitute({vel: y[dim+i]})
885
+ des[i] = diff(y[i],par) == des[i]
886
+ for j in range(dim):
887
+ coord = self._chart[:][j] # important to use '[:]' on
888
+ # 'chart' to avoid problems due to nonzero starting
889
+ # index (i0)
890
+ veloc = self._velocities[j]
891
+ des[dim+i] = des[dim+i].substitute({coord: y[j]})
892
+ des[dim+i] = des[dim+i].substitute({veloc: y[dim+j]})
893
+ des[dim+i] = (diff(y[dim+i], par) == des[dim+i])
894
+
895
+ dvars = y
896
+ ics = [0]
897
+ y_ics_first_half = []
898
+ y_ics_second_half = []
899
+ for i in range(dim):
900
+ coord = self._chart[:][i] # important to use '[:]'
901
+ # on 'chart' to avoid problems due to nonzero
902
+ # starting index (i0)
903
+ veloc = self._velocities[i]
904
+ str_var_coord = "{}_0".format(coord)
905
+ str_var_veloc = "{}_0".format(veloc)
906
+ y_coord_0 = var(str_var_coord)
907
+ y_veloc_0 = var(str_var_veloc)
908
+ y_ics_first_half += [y_coord_0]
909
+ y_ics_second_half += [y_veloc_0]
910
+ ics += y_ics_first_half + y_ics_second_half
911
+
912
+ try:
913
+ sol = desolve_system(des, dvars, ivar=self._curve_parameter, ics=ics)
914
+ except NotImplementedError:
915
+ coords_sol_expr = False
916
+ if verbose:
917
+ print("The system could not be solved analytically.")
918
+ else:
919
+ coords_sol_expr = []
920
+ for relation in sol[:dim]:
921
+ expr = relation.rhs().simplify_full()
922
+ coords_sol_expr += [expr]
923
+ self.add_expr(self.domain().default_chart(), self._chart,
924
+ coords_sol_expr)
925
+
926
+ for param in self._parameters:
927
+ forget(param != 0)
928
+
929
+ return tuple(coords_sol_expr)
930
+
931
+ def solve(self, step=None, method='odeint', solution_key=None,
932
+ parameters_values=None, verbose=False, **control_param):
933
+ r"""
934
+ Integrate the curve numerically over the domain of definition.
935
+
936
+ INPUT:
937
+
938
+ - ``step`` -- (default: ``None``) step of integration; default
939
+ value is a hundredth of the domain of integration if none is
940
+ provided
941
+ - ``method`` -- (default: ``'odeint'``) numerical scheme to
942
+ use for the integration of the curve; available algorithms are:
943
+
944
+ * ``'odeint'`` -- makes use of
945
+ :func:`scipy:scipy.integrate.odeint`
946
+ via Sage solver
947
+ :func:`~sage.calculus.desolvers.desolve_odeint`; ``odeint`` invokes
948
+ the LSODA algorithm of the
949
+ `ODEPACK suite <https://www.netlib.org/odepack/>`_, which
950
+ automatically selects between implicit Adams method (for non-stiff
951
+ problems) and a method based on backward differentiation formulas
952
+ (BDF) (for stiff problems).
953
+ * ``'rk4_maxima'`` -- 4th order classical Runge-Kutta, which
954
+ makes use of Maxima's dynamics package via Sage solver
955
+ :func:`~sage.calculus.desolvers.desolve_system_rk4` (quite slow)
956
+ * ``'dopri5'`` -- Dormand-Prince Runge-Kutta of order (4)5 provided by
957
+ :obj:`scipy:scipy.integrate.ode`
958
+ * ``'dop853'`` -- Dormand-Prince Runge-Kutta of order 8(5,3) provided by
959
+ :obj:`scipy:scipy.integrate.ode`
960
+
961
+ and those provided by ``GSL`` via Sage class
962
+ :class:`~sage.calculus.ode.ode_solver`:
963
+
964
+ * ``'rk2'`` -- embedded Runge-Kutta (2,3)
965
+ * ``'rk4'`` -- 4th order classical Runge-Kutta
966
+ * ``'rkf45'`` -- Runge-Kutta-Felhberg (4,5)
967
+ * ``'rkck'`` -- embedded Runge-Kutta-Cash-Karp (4,5)
968
+ * ``'rk8pd'`` -- Runge-Kutta Prince-Dormand (8,9)
969
+ * ``'rk2imp'`` -- implicit 2nd order Runge-Kutta at Gaussian points
970
+ * ``'rk4imp'`` -- implicit 4th order Runge-Kutta at Gaussian points
971
+ * ``'gear1'`` -- `M=1` implicit Gear
972
+ * ``'gear2'`` -- `M=2` implicit Gear
973
+ * ``'bsimp'`` -- implicit Bulirsch-Stoer (requires Jacobian)
974
+
975
+ - ``solution_key`` -- (default: ``None``) key which the
976
+ resulting numerical solution will be associated to; a default
977
+ value is given if none is provided
978
+ - ``parameters_values`` -- (default: ``None``) list of numerical
979
+ values of the parameters present in the system defining the
980
+ curve, to be substituted in the equations before integration
981
+ - ``verbose`` -- boolean (default: ``False``); prints information about
982
+ the computation in progress
983
+ - ``**control_param`` -- extra control parameters to be passed to the
984
+ chosen solver; see the example with ``rtol`` and ``atol`` below
985
+
986
+ OUTPUT: list of the numerical points of the computed solution
987
+
988
+ EXAMPLES:
989
+
990
+ Computing a numerical solution::
991
+
992
+ sage: M = Manifold(3, 'M')
993
+ sage: X.<x1,x2,x3> = M.chart()
994
+ sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T')
995
+ sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2)
996
+ sage: D = X.symbolic_velocities()
997
+ sage: eqns = [q*B/m*D[1], -q*B/m*D[0], 0]
998
+ sage: p = M.point((0,0,0), name='p')
999
+ sage: Tp = M.tangent_space(p)
1000
+ sage: v = Tp((1,0,1))
1001
+ sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c')
1002
+ sage: sol = c.solve(parameters_values={B_0:1, m:1, q:1, L:10, T:1}, # needs scipy
1003
+ ....: verbose=True)
1004
+ Performing numerical integration with method 'odeint'...
1005
+ Resulting list of points will be associated with the key
1006
+ 'odeint' by default.
1007
+ Numerical integration completed.
1008
+ <BLANKLINE>
1009
+ Checking all points are in the chart domain...
1010
+ All points are in the chart domain.
1011
+ <BLANKLINE>
1012
+ The resulting list of points was associated with the key
1013
+ 'odeint' (if this key already referred to a former
1014
+ numerical solution, such a solution was erased).
1015
+
1016
+ The first 3 points of the solution, in the form ``[t, x1, x2, x3]``::
1017
+
1018
+ sage: sol[:3] # abs tol 1e-12 # needs scipy
1019
+ [[0.0, 0.0, 0.0, 0.0],
1020
+ [0.05, 0.04999999218759271, -2.083327338392213e-05, 0.05],
1021
+ [0.1, 0.09999975001847655, -0.00016666146190783666, 0.1]]
1022
+
1023
+ The default is ``verbose=False``::
1024
+
1025
+ sage: sol_mute = c.solve(parameters_values={B_0:1, m:1, q:1, # needs scipy
1026
+ ....: L:10, T:1})
1027
+ sage: sol_mute == sol # needs scipy
1028
+ True
1029
+
1030
+ Specifying the relative and absolute error tolerance parameters to
1031
+ be used in :func:`~sage.calculus.desolvers.desolve_odeint`::
1032
+
1033
+ sage: sol = c.solve(parameters_values={B_0:1, m:1, q:1, L:10, T:1}, # needs scipy
1034
+ ....: rtol=1e-12, atol=1e-12)
1035
+
1036
+ Using a numerical method different from the default one::
1037
+
1038
+ sage: sol = c.solve(parameters_values={B_0:1, m:1, q:1, L:10, T:1}, # needs scipy
1039
+ ....: method='rk8pd')
1040
+
1041
+
1042
+ TESTS::
1043
+
1044
+ sage: sol = c.solve(parameters_values={m:1, q:1, L:10, T:1}) # needs scipy
1045
+ Traceback (most recent call last):
1046
+ ...
1047
+ ValueError: numerical values should be provided for each of
1048
+ the parameters [B_0, L, T, m, q]
1049
+ sage: sol = c.solve(method='my method', # needs scipy
1050
+ ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1})
1051
+ Traceback (most recent call last):
1052
+ ...
1053
+ ValueError: no available method of integration referred to
1054
+ as 'my method'
1055
+ """
1056
+ from sage.symbolic.ring import SR
1057
+
1058
+ if verbose:
1059
+ print("Performing numerical integration with method '" +
1060
+ method + "'...")
1061
+
1062
+ if solution_key is None:
1063
+ solution_key = method
1064
+ if verbose:
1065
+ print("Resulting list of points will be associated " +
1066
+ "with the key '{}' ".format(solution_key) +
1067
+ "by default.")
1068
+
1069
+ t_min = self.domain().lower_bound()
1070
+ t_max = self.domain().upper_bound()
1071
+
1072
+ eqns_num = list(self._equations_rhs)
1073
+ # 'self._equations_rhs' needs not to be modified ever, because we
1074
+ # want to keep track of the most general form of the equations
1075
+ # defining self, since those may contain parameters (which, for
1076
+ # instance, we want to display as their original expressions
1077
+ # when calling 'system' method with option 'verbose', and not
1078
+ # substituted with some numerical values).
1079
+ # This is why 'eqns_num' is declared: it will contain copies of
1080
+ # the equations of 'self._equations_rhs' in which the parameters
1081
+ # will be substituted with numerical values.
1082
+ # It was then important to declare it as above, in order to make
1083
+ # independent copies of each equations of 'self._equations_rhs',
1084
+ # rather than declaring 'eqns_num = self._equations_rhs', in which
1085
+ # case making substitutions in 'eqns_num' would have meant making
1086
+ # the same substitutions in the original equations of
1087
+ # 'self._equations_rhs'
1088
+
1089
+ v0 = self._initial_tangent_vector
1090
+ chart = self._chart
1091
+
1092
+ initial_tgt_space = v0.parent()
1093
+ initial_pt = initial_tgt_space.base_point() # retrieves
1094
+ # the initial point as the base point of the tangent space
1095
+ # to which the initial tangent vector belongs
1096
+ initial_pt_coords = list(initial_pt.coordinates(chart))
1097
+ # previous line converts to list since would otherwise be a
1098
+ # tuple (yet might need to be added to [t_min] later); will
1099
+ # raise error if coordinates in chart cannot be obtained
1100
+
1101
+ initial_coord_basis = chart.frame().at(initial_pt)
1102
+ initial_tgt_vec_comps = list(v0[initial_coord_basis,:]) # idem
1103
+
1104
+ dim = self.codomain().dim()
1105
+
1106
+ if self._parameters:
1107
+ if parameters_values is None or len(parameters_values) != len(self._parameters):
1108
+ raise ValueError("numerical values should be " +
1109
+ "provided for each of the " +
1110
+ "parameters "
1111
+ "{}".format(sorted(self._parameters, key=str)))
1112
+ for key in parameters_values:
1113
+ # Get numerical values in case some parameters values
1114
+ # contain expressions such as pi; will raise error if
1115
+ # any element of parameters_values is not numerical
1116
+ parameters_values[key] = numerical_approx(parameters_values[key])
1117
+
1118
+ if isinstance(t_min, Expression):
1119
+ t_min = parameters_values[t_min]
1120
+ if t_min == -Infinity or t_min == +Infinity:
1121
+ raise ValueError("both boundaries of the " +
1122
+ "interval need to be finite")
1123
+
1124
+ if isinstance(t_max, Expression):
1125
+ t_max = parameters_values[t_max]
1126
+ if t_max == -Infinity or t_max == +Infinity:
1127
+ raise ValueError("both boundaries of the " +
1128
+ "interval need to be finite")
1129
+
1130
+ for i in range(dim):
1131
+ if isinstance(eqns_num[i], Expression): # some right
1132
+ # hand sides might merely be real numbers and not
1133
+ # expressions, so that they do not contain any variable,
1134
+ # and hence no substitution is required
1135
+ eqns_num[i] = eqns_num[i].substitute(parameters_values)
1136
+
1137
+ for i in range(dim):
1138
+ if isinstance(initial_pt_coords[i], Expression):
1139
+ AUX = initial_pt_coords[i]
1140
+ AUX = AUX.substitute(parameters_values)
1141
+ initial_pt_coords[i] = AUX
1142
+ if isinstance(initial_tgt_vec_comps[i], Expression):
1143
+ AUX2 = initial_tgt_vec_comps[i]
1144
+ AUX2 = AUX2.substitute(parameters_values)
1145
+ initial_tgt_vec_comps[i] = AUX2
1146
+ # 'AUX' and 'AUX2' only used for the lines of
1147
+ # source code to be shorter
1148
+
1149
+ t_min = numerical_approx(t_min)
1150
+ t_max = numerical_approx(t_max)
1151
+
1152
+ for i in range(dim):
1153
+ if not isinstance(eqns_num[i], Expression):
1154
+ # in case of a
1155
+ # right hand side that is not an Expression (and then is a
1156
+ # number), it is needed to be converted to an Expression
1157
+ # since some solvers called below require only expressions
1158
+ eqns_num[i] = SR(eqns_num[i])
1159
+
1160
+ if step is None:
1161
+ step = (t_max - t_min) / 100
1162
+
1163
+ step = numerical_approx(step)
1164
+
1165
+ initial_pt_coords = [numerical_approx(coord) for coord
1166
+ in initial_pt_coords]
1167
+ initial_tgt_vec_comps = [numerical_approx(comp) for comp
1168
+ in initial_tgt_vec_comps]
1169
+ # the last two instructions retrieve numerical values even
1170
+ # if no parameters had to be substituted, in case some
1171
+ # coordinates or components contain expressions such as pi,
1172
+ # or are not RealNumber, since variable 'ics' of
1173
+ # 'desolve_system_rk4' used below needs to be a list of
1174
+ # RealNumber
1175
+
1176
+ if not chart.valid_coordinates(*initial_pt_coords):
1177
+ raise ValueError("initial point should be in the " +
1178
+ "domain of the chart")
1179
+
1180
+ ode_solver_methods = ["rk2", "rk4", "rkf45", "rkck", "rk8pd",
1181
+ "rk2imp", "rk4imp", "gear1", "gear2", "bsimp"]
1182
+
1183
+ if method == 'rk4_maxima':
1184
+ des = self._velocities + eqns_num
1185
+ dvars = list(chart[:]) + self._velocities
1186
+ ics = [t_min] + initial_pt_coords + initial_tgt_vec_comps
1187
+
1188
+ sol = desolve_system_rk4(des, dvars,
1189
+ ivar=self._curve_parameter,
1190
+ ics=ics,
1191
+ end_points=[t_min, t_max],
1192
+ step=step)
1193
+
1194
+ # The value of 'step' being set by the user when calling
1195
+ # method 'solve', the value of (t_max - tmin)/step is not
1196
+ # necessarily an integer.
1197
+ # As a result, when the solver 'desolve_system_rk4' reaches
1198
+ # a curve parameter that is distant to 't_max' by less than
1199
+ # 'step', it computes one last point evaluated for a curve
1200
+ # parameter exactly equal to 't_max'.
1201
+ # Therefore, the difference between the curve parameter
1202
+ # corresponding to this last point and that corresponding
1203
+ # to the previous one is strictly less than 'step'. If this
1204
+ # difference is too small (that is, if the solver considered
1205
+ # that it did not reach 't_max', and hence computed one more
1206
+ # point, although it was already very close to 't_max'),
1207
+ # problems arise when using an interpolation of this
1208
+ # solution (such as getting points with coordinates 'nan').
1209
+ # As a result, we choose to remove the last point of a
1210
+ # solution when it is a point that was added by the solver
1211
+ # and threatens to be too close to the previous one
1212
+ # (arbitrarily, we consider two points to be too close if
1213
+ # their curve parameters are separated by less than 90% of a
1214
+ # step).
1215
+ if len(sol) > 1 and abs(sol[-1][0] - sol[-2][0]) < 0.9 * step:
1216
+ del sol[-1]
1217
+
1218
+ elif method in ["odeint", "ode_int"]:
1219
+ # "ode_int" is here only for backward compatibility
1220
+ des = [fast_callable(eq, vars=tuple(list(self._chart[:])
1221
+ + self._velocities
1222
+ + [self._curve_parameter]),
1223
+ domain=float)
1224
+ for eq in (self._velocities + eqns_num)]
1225
+ ics = initial_pt_coords + initial_tgt_vec_comps
1226
+ times = srange(t_min, t_max, step, include_endpoint=True)
1227
+ dvars = list(chart[:]) + self._velocities
1228
+ # Setting 1.e-10 as default value for the error control
1229
+ # parameters rtol and atol:
1230
+ if 'rtol' not in control_param:
1231
+ control_param['rtol'] = 1.e-10
1232
+ if 'atol' not in control_param:
1233
+ control_param['atol'] = 1.e-10
1234
+ sol0 = desolve_odeint(des, ics, times, dvars,
1235
+ ivar=self._curve_parameter, **control_param)
1236
+
1237
+ # rewrite the solution to prepare for the extraction (which
1238
+ # removes information about the velocities), and convert
1239
+ # elements of type 'numpy.float64' to standard type 'float'
1240
+
1241
+ import numpy as np
1242
+ sol = np.column_stack((times, sol0)) # tolist() done later
1243
+
1244
+ elif method in ["dopri5", "dop853"]:
1245
+ import numpy as np
1246
+ des = [fast_callable(eq, vars=tuple(list(self._chart[:])
1247
+ + self._velocities), domain=float)
1248
+ for eq in (self._velocities + eqns_num)]
1249
+ ics = initial_pt_coords + initial_tgt_vec_comps
1250
+ times = np.linspace(t_min, t_max, int((t_max-t_min)/step) + 1,
1251
+ endpoint=True)
1252
+ # ode accepts a function returning a list, and not a list of functions
1253
+ r = ode(lambda t, y: [de(*y) for de in des]).set_integrator(method,
1254
+ **control_param)
1255
+ r.set_initial_value(ics, t_min)
1256
+ r.set_solout(lambda t, y: 0 if chart.valid_coordinates_numerical(*y[0:dim])
1257
+ else -1)
1258
+
1259
+ nt = len(times)
1260
+ sol0 = np.zeros((nt, 2*dim))
1261
+ sol0[0,:] = np.array(ics)
1262
+ for i in range(1, nt):
1263
+ sol0[i,:] = r.integrate(times[i])
1264
+ if not r.successful():
1265
+ break
1266
+ sol = np.column_stack((times, sol0)) # tolist() done later
1267
+
1268
+ elif method in ode_solver_methods:
1269
+ T = self._ode_solver
1270
+
1271
+ if T is None:
1272
+ def system(t, y):
1273
+ syst = self._velocities + eqns_num
1274
+ par = self._curve_parameter
1275
+ for i in range(dim):
1276
+ vel = self._velocities[i]
1277
+ syst[i] = syst[i].substitute({vel:y[dim+i]})
1278
+ syst[dim+i] = syst[dim+i].substitute({par:t})
1279
+ for j in range(dim):
1280
+ coord = chart[:][j] # important to use '[:]'
1281
+ # on 'chart' to avoid problems due to non
1282
+ # zero starting index (i0)
1283
+ veloc = self._velocities[j]
1284
+ syst[dim+i] = syst[dim+i].substitute({coord:y[j]})
1285
+ syst[dim+i] = syst[dim+i].substitute({veloc:y[dim+j]})
1286
+ return syst
1287
+ from sage.calculus.ode import ode_solver
1288
+ T = ode_solver(function=system, **control_param)
1289
+
1290
+ T.algorithm = method
1291
+ y_0 = initial_pt_coords + initial_tgt_vec_comps
1292
+ t_span = srange(t_min, t_max, step, include_endpoint=True)
1293
+
1294
+ if method == "bsimp":
1295
+ # this method requires the expression of the Jacobian
1296
+ # matrix of the application defining the right-hand side
1297
+ # of the system to be provided
1298
+
1299
+ if T.jacobian is None:
1300
+ def jacobian(t, y):
1301
+ jac = []
1302
+ par = self._curve_parameter
1303
+ for i in range(dim):
1304
+ new_row = [0] * (2*dim)
1305
+ new_row[dim + i] = 1
1306
+ jac += [new_row]
1307
+
1308
+ for i in range(dim):
1309
+ semi_row_coords = []
1310
+ semi_row_vels = []
1311
+ for j in range(dim):
1312
+ coord = chart[:][j] # important to use
1313
+ # '[:]' on 'chart' to avoid problems due
1314
+ # to nonzero starting index (i0)
1315
+ vel = self._velocities[j]
1316
+ AUX = eqns_num[i].derivative(coord)
1317
+ AUX2 = eqns_num[i].derivative(vel)
1318
+ AUX = AUX.substitute({par: t})
1319
+ AUX2 = AUX2.substitute({par: t})
1320
+ for k in range(dim):
1321
+ coordin = chart[:][k] # important to
1322
+ # use '[:]' on 'chart' to avoid
1323
+ # problems due to nonzero starting
1324
+ # index (i0)
1325
+ veloc = self._velocities[k]
1326
+ AUX = AUX.substitute({coordin: y[k]})
1327
+ AUX = AUX.substitute({veloc: y[dim+k]})
1328
+ AUX2 = AUX2.substitute({coordin: y[k]})
1329
+ AUX2 = AUX2.substitute({veloc: y[dim+k]})
1330
+ semi_row_coords += [AUX]
1331
+ semi_row_vels += [AUX2]
1332
+ jac += [semi_row_coords + semi_row_vels]
1333
+
1334
+ last_semi_row_coords = [0] * dim
1335
+ last_semi_row_vels = []
1336
+ for j in range(dim):
1337
+ AUX3 = eqns_num[j].derivative(par)
1338
+ AUX3 = AUX3.substitute({par: t})
1339
+ for m in range(dim):
1340
+ coordin = chart[:][m] # important to use
1341
+ # '[:]' on 'chart' to avoid problems due
1342
+ # to nonzero starting index (i0)
1343
+ veloc = self._velocities[m]
1344
+ AUX3 = AUX3.substitute({coordin: y[m]})
1345
+ AUX3 = AUX3.substitute({veloc: y[dim+m]})
1346
+ last_semi_row_vels += [AUX3]
1347
+ jac += [last_semi_row_coords + last_semi_row_vels]
1348
+ # 'AUX', 'AUX2' and 'AUX3' only used for the lines
1349
+ # of source code to be shorter
1350
+ return jac
1351
+ T.jacobian = jacobian
1352
+
1353
+ T.ode_solve(y_0=y_0, t_span=t_span)
1354
+
1355
+ sol0 = T.solution
1356
+ sol = []
1357
+ for point in sol0:
1358
+ sol += [[point[0]] + point[1]]
1359
+ # above loop rewrites the solution in the same form than
1360
+ # that provided by other methods ('rk4_maxima' and
1361
+ # 'odeint'), in order to extract the time and corresponding
1362
+ # coordinate values a few lines below, in the same way for
1363
+ # all methods
1364
+
1365
+ else:
1366
+ raise ValueError("no available method of integration " +
1367
+ "referred to as '{}'".format(method))
1368
+
1369
+ # eventually, extract the time and corresponding coordinate
1370
+ # values from each point of the solution computed (thus removing
1371
+ # information about the values of the velocities ; should the
1372
+ # latter be conserved ? They could turn useful in method
1373
+ # 'tangent_vector_eval_at', and in 'plot' when plotting the
1374
+ # tangent vectors.)
1375
+
1376
+ if isinstance(sol, list):
1377
+ coords_sol = [point[0:dim + 1] for point in sol]
1378
+ else:
1379
+ coords_sol = sol[:, 0:dim + 1].tolist() # far faster in numpy
1380
+
1381
+ if verbose:
1382
+ print("Numerical integration completed.\n\n" +
1383
+ "Checking all points are in the chart domain...")
1384
+
1385
+ N = len(coords_sol)
1386
+ n = 0
1387
+ while n < N and chart.valid_coordinates_numerical(*coords_sol[n][1:dim+1]):
1388
+ n += 1
1389
+
1390
+ if n < N:
1391
+ raise ValueError("the {}th point ".format(n) +
1392
+ "(initial point being the '0th' point) " +
1393
+ "of the numerical solution (obtained " +
1394
+ "for a curve parameter equal " +
1395
+ "to {}) is out ".format(sol[n][0]) +
1396
+ "of the chart domain; a curve with a " +
1397
+ "smaller maximal value of the curve " +
1398
+ "parameter, or a smaller initial tangent " +
1399
+ "vector, might be considered. You can also try " +
1400
+ "'solve_across_charts' in order not to be " +
1401
+ "confined to a single chart")
1402
+ else:
1403
+ self._solutions[solution_key] = coords_sol
1404
+ if verbose:
1405
+ print("All points are in the chart domain.\n\n" +
1406
+ "The resulting list of points was associated " +
1407
+ "with the key '{}' ".format(solution_key) +
1408
+ "(if this key already referred to a former " +
1409
+ "numerical solution, such a solution was erased).")
1410
+ return self._solutions[solution_key]
1411
+
1412
+ def solve_across_charts(self, charts=None, step=None, solution_key=None,
1413
+ parameters_values=None, verbose=False,
1414
+ **control_param):
1415
+ r"""
1416
+ Integrate the curve numerically over the domain of integration, with
1417
+ the ability to switch chart mid-integration.
1418
+
1419
+ The only supported solver is :obj:`scipy:scipy.integrate.ode`,
1420
+ because it supports basic event handling, needed to detect when the
1421
+ curve is reaching the frontier of the chart. This is an adaptive step
1422
+ solver. So the ``step`` is not the step of integration but instead the
1423
+ step used to peak at the current chart, and switch if needed.
1424
+
1425
+ INPUT:
1426
+
1427
+ - ``step`` -- (default: ``None``) step of chart checking; default
1428
+ value is a hundredth of the domain of integration if none is
1429
+ provided. If your curve can't find a new frame on exiting the
1430
+ current frame, consider reducing this parameter.
1431
+ - ``charts`` -- (default: ``None``) list of chart allowed. The
1432
+ integration stops once it leaves those charts. By default the whole
1433
+ atlas is taken (only the top-charts).
1434
+ - ``solution_key`` -- (default: ``None``) key which the
1435
+ resulting numerical solution will be associated to; a default
1436
+ value is given if none is provided
1437
+ - ``parameters_values`` -- (default: ``None``) list of numerical
1438
+ values of the parameters present in the system defining the
1439
+ curve, to be substituted in the equations before integration
1440
+ - ``verbose`` -- boolean (default: ``False``); prints information about
1441
+ the computation in progress
1442
+ - ``**control_param`` -- extra control parameters to be passed to the
1443
+ solver
1444
+
1445
+ OUTPUT: list of the numerical points of the computed solution
1446
+
1447
+ EXAMPLES:
1448
+
1449
+ Let us use :meth:`solve_across_charts` to integrate a geodesic of the
1450
+ Euclidean plane (a straight line) in polar coordinates.
1451
+
1452
+ In pure polar coordinates `(r, \theta)`, artefacts can appear near
1453
+ the origin because of the fast variation of `\theta`, resulting in
1454
+ the direction of the geodesic being different before and after
1455
+ getting close to the origin.
1456
+
1457
+ The solution to this problem is to switch to Cartesian coordinates
1458
+ near `(0,0)` to avoid any singularity.
1459
+
1460
+ First let's declare the plane as a 2-dimensional manifold, with two
1461
+ charts `P` en `C` (for "Polar" and "Cartesian") and their transition
1462
+ maps::
1463
+
1464
+ sage: M = Manifold(2, 'M', structure='Riemannian')
1465
+ sage: C.<x,y> = M.chart(coord_restrictions=lambda x,y: x**2+y**2 < 3**2)
1466
+ sage: P.<r,th> = M.chart(coord_restrictions=lambda r, th: r > 2)
1467
+ sage: P_to_C = P.transition_map(C,(r*cos(th), r*sin(th)))
1468
+ sage: C_to_P = C.transition_map(P,(sqrt(x**2+y**2), atan2(y,x)))
1469
+
1470
+ Here we added restrictions on those charts, to avoid any
1471
+ singularity. The intersection is the donut region `2 < r < 3`.
1472
+
1473
+ We still have to define the metric. This is done in the Cartesian
1474
+ frame. The metric in the polar frame is computed automatically::
1475
+
1476
+ sage: g = M.metric()
1477
+ sage: g[0,0,C]=1
1478
+ sage: g[1,1,C]=1
1479
+ sage: g[P.frame(), : ,P]
1480
+ [ 1 0]
1481
+ [ 0 r^2]
1482
+
1483
+ To visualize our manifold, let's declare a mapping between every chart
1484
+ and the Cartesian chart, and then plot each chart in term of this
1485
+ mapping::
1486
+
1487
+ sage: phi = M.diff_map(M, {(C,C): [x, y], (P,C): [r*cos(th), r*sin(th)]})
1488
+ sage: fig = P.plot(number_values=9, chart=C, mapping=phi, # needs sage.plot
1489
+ ....: color='grey', ranges= {r:(2, 6), th:(0,2*pi)})
1490
+ sage: fig += C.plot(number_values=13, chart=C, mapping=phi, # needs sage.plot
1491
+ ....: color='grey', ranges= {x:(-3, 3), y:(-3, 3)})
1492
+
1493
+ There is a clear non-empty intersection between the two
1494
+ charts. This is the key point to successfully switch chart during the
1495
+ integration. Indeed, at least 2 points must fall in the intersection.
1496
+
1497
+ .. RUBRIC:: Geodesic integration
1498
+
1499
+ Let's define the time as `t`, the initial point as `p`, and the
1500
+ initial velocity vector as `v` (define as a member of the tangent
1501
+ space `T_p`). The chosen geodesic should enter the central region
1502
+ from the left and leave it to the right::
1503
+
1504
+ sage: t = var('t')
1505
+ sage: p = M((5,pi+0.3), P)
1506
+ sage: Tp = M.tangent_space(p)
1507
+ sage: v = Tp((-1,-0.03), P.frame().at(p))
1508
+
1509
+ While creating the integrated geodesic, we need to specify the
1510
+ optional argument ``across_chart=True``, to prepare the compiled
1511
+ version of the changes of charts::
1512
+
1513
+ sage: c = M.integrated_geodesic(g, (t, 0, 10), v, across_charts=True)
1514
+
1515
+ The integration is done as usual, but using the method
1516
+ :meth:`solve_across_charts` instead of :meth:`solve`. This forces the
1517
+ use of :obj:`scipy:scipy.integrate.ode` as the solver, because of event
1518
+ handling support.
1519
+
1520
+ The argument ``verbose=True`` will cause the solver to write a small
1521
+ message each time it is switching chart::
1522
+
1523
+ sage: sol = c.solve_across_charts(step=0.1, verbose=True) # needs scipy
1524
+ Performing numerical integration with method 'ode'.
1525
+ Integration will take place on the whole manifold domain.
1526
+ Resulting list of points will be associated with the key 'ode_multichart' by default.
1527
+ ...
1528
+ Exiting chart, trying to switch to another chart.
1529
+ New chart found. Resuming integration.
1530
+ Exiting chart, trying to switch to another chart.
1531
+ New chart found. Resuming integration.
1532
+ Integration successful.
1533
+
1534
+ As expected, two changes of chart occur.
1535
+
1536
+ The returned solution is a list of pairs ``(chart, solution)``,
1537
+ where each solution is given on a unique chart, and the last
1538
+ point of a solution is the first of the next.
1539
+
1540
+ The following code prints the corresponding charts::
1541
+
1542
+ sage: for chart, solution in sol: # needs scipy
1543
+ ....: print(chart)
1544
+ Chart (M, (r, th))
1545
+ Chart (M, (x, y))
1546
+ Chart (M, (r, th))
1547
+
1548
+ The interpolation is done as usual::
1549
+
1550
+ sage: interp = c.interpolate() # needs scipy
1551
+
1552
+ To plot the result, you must first be sure that the mapping
1553
+ encompasses all the chart, which is the case here.
1554
+ You must also specify ``across_charts=True`` in order to call
1555
+ :meth:`plot_integrated` again on each part.
1556
+ Finally, ``color`` can be a list, which will be cycled through::
1557
+
1558
+ sage: fig += c.plot_integrated(mapping=phi, color=["green","red"], # needs scipy sage.plot
1559
+ ....: thickness=3, plot_points=100, across_charts=True)
1560
+ sage: fig # needs scipy sage.plot
1561
+ Graphics object consisting of 43 graphics primitives
1562
+
1563
+ .. PLOT::
1564
+
1565
+ M = Manifold(2, 'M', structure='Riemannian')
1566
+ C= M.chart(names = ("x", "y"), coord_restrictions=lambda x,y: x**2+y**2 < 3**2)
1567
+ x, y = C[:]
1568
+ P = M.chart(names = ("r", "th"), coord_restrictions=lambda r,th: r > 2)
1569
+ r, th = P[:]
1570
+ P_to_C = P.transition_map(C,(r*cos(th), r*sin(th)))
1571
+ C_to_P = C.transition_map(P,(sqrt(x**2+y**2), atan2(y,x)))
1572
+ g = M.metric()
1573
+ g[0,0,C] = 1
1574
+ g[1,1,C] = 1
1575
+ g[P.frame(), : , P]
1576
+ phi = M.diff_map(M, {(C,C): [x, y], (P,C): [r*cos(th), r*sin(th)]})
1577
+ fig = P.plot(number_values=9, chart=C, mapping=phi, color='grey',
1578
+ ranges= {r:(2, 6), th:(0,2*pi)})
1579
+ fig += C.plot(number_values=13, chart=C, mapping=phi, color='grey',
1580
+ ranges= {x:(-3, 3), y:(-3, 3)})
1581
+ t = var('t')
1582
+ p = M((5,pi+0.3), P)
1583
+ Tp = M.tangent_space(p)
1584
+ v = Tp((-1,-0.03), P.frame().at(p))
1585
+ c = M.integrated_geodesic(g, (t, 0, 10), v, across_charts=True)
1586
+ sol = c.solve_across_charts(step=0.1, verbose=True)
1587
+ interp = c.interpolate()
1588
+ fig += c.plot_integrated(mapping=phi, color=["green","red"],
1589
+ thickness=3, plot_points=100, across_charts=True)
1590
+ sphinx_plot(fig)
1591
+ """
1592
+ import numpy as np
1593
+
1594
+ if verbose:
1595
+ print("Performing numerical integration with method 'ode'.")
1596
+
1597
+ if charts is None:
1598
+ charts = self._codomain.top_charts()
1599
+ if verbose:
1600
+ print("Integration will take place on the whole manifold domain.")
1601
+ else:
1602
+ for c in charts:
1603
+ if not isinstance(c, Chart) or c.domain() is not self._codomain:
1604
+ raise ValueError("'charts' needs to be a list of "
1605
+ "charts of the manifold")
1606
+ print("Integration will take place on {} charts.".format(len(charts)))
1607
+
1608
+ if solution_key is None:
1609
+ solution_key = "ode_multichart"
1610
+ if verbose:
1611
+ print("Resulting list of points will be associated " +
1612
+ "with the key '{}' ".format(solution_key) +
1613
+ "by default.")
1614
+ print(" ...")
1615
+
1616
+ t_min = self.domain().lower_bound()
1617
+ t_max = self.domain().upper_bound()
1618
+
1619
+ eqns_num = self._equations_rhs.copy()
1620
+
1621
+ v0 = self._initial_tangent_vector
1622
+
1623
+ initial_tgt_space = v0.parent()
1624
+ initial_pt = initial_tgt_space.base_point()
1625
+
1626
+ # Find a suitable initial chart, ie top chart in which the coordinates
1627
+ # of the initial point are known.
1628
+
1629
+ for ichart in set(initial_pt._coordinates.keys()).intersection(charts):
1630
+
1631
+ initial_chart = ichart
1632
+
1633
+ initial_pt_coords = list(initial_pt.coordinates(initial_chart))
1634
+ initial_coord_basis = initial_chart.frame().at(initial_pt)
1635
+ initial_tgt_vec_comps = list(v0[initial_coord_basis, :])
1636
+
1637
+ if step is None:
1638
+ step = (t_max - t_min) / 100
1639
+
1640
+ dim = self.codomain().dim()
1641
+
1642
+ if self._parameters:
1643
+ if parameters_values is None or len(parameters_values) != len(self._parameters):
1644
+ raise ValueError("numerical values should be " +
1645
+ "provided for each of the " +
1646
+ "parameters "
1647
+ "{}".format(sorted(self._parameters, key=str)))
1648
+ for key in parameters_values:
1649
+ parameters_values[key] = numerical_approx(parameters_values[key])
1650
+
1651
+ if isinstance(t_min, Expression):
1652
+ t_min = parameters_values[t_min]
1653
+ if t_min == -Infinity or t_min == +Infinity:
1654
+ raise ValueError("both boundaries of the " +
1655
+ "interval need to be finite")
1656
+
1657
+ if isinstance(t_max, Expression):
1658
+ t_max = parameters_values[t_max]
1659
+ if t_max == -Infinity or t_max == +Infinity:
1660
+ raise ValueError("both boundaries of the " +
1661
+ "interval need to be finite")
1662
+
1663
+ for i in range(dim):
1664
+ for chart in eqns_num:
1665
+ if isinstance(eqns_num[chart][i], Expression):
1666
+ eqns_num[chart][i] = eqns_num[chart][i].substitute(parameters_values)
1667
+
1668
+ for i in range(dim):
1669
+ if isinstance(initial_pt_coords[i], Expression):
1670
+ AUX = initial_pt_coords[i]
1671
+ AUX = AUX.substitute(parameters_values)
1672
+ initial_pt_coords[i] = AUX
1673
+ if isinstance(initial_tgt_vec_comps[i], Expression):
1674
+ AUX2 = initial_tgt_vec_comps[i]
1675
+ AUX2 = AUX2.substitute(parameters_values)
1676
+ initial_tgt_vec_comps[i] = AUX2
1677
+
1678
+ step = numerical_approx(step)
1679
+
1680
+ initial_pt_coords = [numerical_approx(coord) for coord
1681
+ in initial_pt_coords]
1682
+ initial_tgt_vec_comps = [numerical_approx(comp) for comp
1683
+ in initial_tgt_vec_comps]
1684
+
1685
+ t_min = numerical_approx(t_min)
1686
+ t_max = numerical_approx(t_max)
1687
+
1688
+ if initial_chart.valid_coordinates(*initial_pt_coords):
1689
+ # found acceptable initial chart
1690
+ break
1691
+
1692
+ else:
1693
+ # No initial chart found
1694
+ raise ValueError("initial point should be in the " +
1695
+ "domain of its chart")
1696
+
1697
+ # Transformation to fast_callable happens here
1698
+ des = {chart: [fast_callable(SR(eq), vars=tuple(
1699
+ list(chart[:]) + chart.symbolic_velocities()), domain=float)
1700
+ for eq in (chart.symbolic_velocities() + eqns_num[chart])]
1701
+ for chart in charts}
1702
+
1703
+ ics = initial_pt_coords + initial_tgt_vec_comps
1704
+ times = np.linspace(t_min, t_max, int((t_max - t_min) / step) + 1,
1705
+ endpoint=True)
1706
+ nt = len(times)
1707
+
1708
+ sol = []
1709
+
1710
+ chart = initial_chart
1711
+
1712
+ start_index = 0 # current index while entering each new chart
1713
+ sol_chart = np.zeros((nt, 2 * dim)) # current chart solution
1714
+ sol_chart[0, :] = np.array(ics) # starting with initial condition
1715
+
1716
+ # Current equation to integrate, with initial and stop conditions
1717
+ r = ode(lambda t, y: [de(*y) for de in des[chart]]).set_integrator('dopri5',
1718
+ **control_param)
1719
+ r.set_initial_value(ics, t_min)
1720
+ r.set_solout(lambda t, y: 0 if chart.valid_coordinates_numerical(*y[0:dim]) else -1)
1721
+
1722
+ i = 1
1723
+ tried_charts = set() # set of charts already searched at this step
1724
+
1725
+ # Integration loop
1726
+ while i < nt:
1727
+
1728
+ current_sol = r.integrate(times[i])
1729
+ if not r.successful():
1730
+ raise RuntimeError("unsuccessful integration")
1731
+
1732
+ # step leads outside of the chart domain
1733
+ if abs(r.t-times[i]) > 1e-8:
1734
+ if verbose:
1735
+ print("Exiting chart, trying to switch to another chart.")
1736
+
1737
+ # Last known point
1738
+ last_pts = sol_chart[i-2-start_index, :dim]
1739
+ last_vel = sol_chart[i-2-start_index, dim:]
1740
+
1741
+ random_order = list(set(charts).difference(tried_charts))
1742
+ shuffle(random_order)
1743
+ for new_chart in random_order:
1744
+ tried_charts.add(new_chart)
1745
+ if new_chart not in chart._subcharts: # includes new != old
1746
+
1747
+ inter = chart.domain().intersection(new_chart.domain())
1748
+
1749
+ # The change of chart is performed here
1750
+ new_pts = [f(*last_pts) for f in
1751
+ self._fast_changes_of_chart[(chart.restrict(inter),
1752
+ new_chart.restrict(inter))]]
1753
+ # If this line throws an error, check your changes
1754
+ # of chart
1755
+
1756
+ if new_chart.valid_coordinates_numerical(*new_pts):
1757
+ if verbose:
1758
+ print("New chart found. Resuming integration.")
1759
+ if start_index != i - 1: # len(1) solution are ditched
1760
+ # col-stack the times
1761
+ sol_stacked = np.column_stack((times[start_index:i-1],
1762
+ sol_chart[:i-start_index-1, :]))
1763
+ # add it to the global solution
1764
+ sol.append((chart, sol_stacked))
1765
+
1766
+ # unfortunately building the tangent space is too
1767
+ # slow, so we have to cheat a little and apply the
1768
+ # change of frame manually (with a precompiled
1769
+ # function)
1770
+
1771
+ new_vel = self._fast_changes_of_frame[(new_chart.frame().restrict(inter),
1772
+ chart.frame().restrict(inter))](last_pts, last_vel)
1773
+
1774
+ ics = new_pts + new_vel
1775
+ chart = new_chart
1776
+
1777
+ start_index = i - 1
1778
+ sol_chart = np.zeros((nt, 2 * dim))
1779
+ sol_chart[0, :] = np.array(ics)
1780
+
1781
+ r = ode(lambda t, y: [de(*y) for de in des[chart]])\
1782
+ .set_integrator('dopri5')
1783
+ r.set_initial_value(ics, times[i - 1])
1784
+ r.set_solout(lambda t, y: 0 if chart.
1785
+ valid_coordinates_numerical(*y[0:dim]) else -1)
1786
+ i -= 1 # go back in the past to redo failed step
1787
+ break
1788
+ # every chart was tried
1789
+ else:
1790
+ if verbose:
1791
+ print("No chart found, stopping integration.")
1792
+ # col-stack the times
1793
+ sol_chart = np.column_stack((times[start_index:i-1],
1794
+ sol_chart[:i-start_index-1, :]))
1795
+ # add it to the global solution
1796
+ sol.append((chart, sol_chart))
1797
+ break
1798
+
1799
+ # the integration step was successful
1800
+ else:
1801
+ sol_chart[i-start_index, :] = current_sol # register the result
1802
+ tried_charts.clear() # the set is reset.
1803
+
1804
+ i += 1
1805
+
1806
+ else: # integration finishes successfully
1807
+ if verbose:
1808
+ print("Integration successful.")
1809
+ # col-stack the times
1810
+ sol_chart = np.column_stack((times[start_index:i-1],
1811
+ sol_chart[:i-start_index-1, :]))
1812
+ # add it to the global solution
1813
+ sol.append((chart, sol_chart))
1814
+
1815
+ coords_sol = []
1816
+ for chart, chart_sol in sol:
1817
+ coords_sol.append((chart, chart_sol[:, 0:dim + 1])) # remove velocities
1818
+
1819
+ self._solutions[solution_key] = coords_sol
1820
+
1821
+ return self._solutions[solution_key]
1822
+
1823
+ def solution(self, solution_key=None, verbose=False):
1824
+ r"""
1825
+ Return the solution (list of points) associated with the given
1826
+ key.
1827
+
1828
+ INPUT:
1829
+
1830
+ - ``solution_key`` -- (default: ``None``) key which the
1831
+ requested numerical solution is associated to; a default
1832
+ value is chosen if none is provided
1833
+ - ``verbose`` -- boolean (default: ``False``); prints information about
1834
+ the solution returned
1835
+
1836
+ OUTPUT: list of the numerical points of the solution requested
1837
+
1838
+ EXAMPLES:
1839
+
1840
+ Requesting a numerical solution previously computed::
1841
+
1842
+ sage: M = Manifold(3, 'M')
1843
+ sage: X.<x1,x2,x3> = M.chart()
1844
+ sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T')
1845
+ sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2)
1846
+ sage: D = X.symbolic_velocities()
1847
+ sage: eqns = [q*B/m*D[1], -q*B/m*D[0], 0]
1848
+ sage: p = M.point((0,0,0), name='p')
1849
+ sage: Tp = M.tangent_space(p)
1850
+ sage: v = Tp((1,0,1))
1851
+ sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c')
1852
+
1853
+ sage: # needs scipy
1854
+ sage: sol = c.solve(solution_key='sol_T1',
1855
+ ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1})
1856
+ sage: sol_bis = c.solution(verbose=True)
1857
+ Returning the numerical solution associated with the key
1858
+ 'sol_T1' by default...
1859
+ sage: sol_bis == sol
1860
+ True
1861
+ sage: sol_ter = c.solution(solution_key='sol_T1')
1862
+ sage: sol_ter == sol
1863
+ True
1864
+ sage: sol_mute = c.solution()
1865
+ sage: sol_mute == sol
1866
+ True
1867
+ """
1868
+ if solution_key is None:
1869
+ if 'odeint' in self._solutions:
1870
+ solution_key = 'odeint'
1871
+ else:
1872
+ solution_key = next(iter(self._solutions))
1873
+ # will raise an error if self._solutions is empty
1874
+ if verbose:
1875
+ print("Returning the numerical solution associated " +
1876
+ "with the key '{}' ".format(solution_key) +
1877
+ "by default...")
1878
+ elif solution_key not in self._solutions:
1879
+ raise ValueError("no existing key " +
1880
+ "'{}' ".format(solution_key) +
1881
+ "referring to any numerical solution")
1882
+
1883
+ return self._solutions[solution_key]
1884
+
1885
+ def interpolate(self, solution_key=None, method=None,
1886
+ interpolation_key=None, verbose=False):
1887
+ r"""
1888
+ Interpolate the chosen numerical solution using the given
1889
+ interpolation method.
1890
+
1891
+ INPUT:
1892
+
1893
+ - ``solution_key`` -- (default: ``None``) key which the
1894
+ numerical solution to interpolate is associated to ; a default
1895
+ value is chosen if none is provided
1896
+ - ``method`` -- (default: ``None``) interpolation scheme to use;
1897
+ algorithms available are
1898
+
1899
+ * ``'cubic spline'``, which makes use of ``GSL`` via
1900
+ :class:`~sage.calculus.interpolation.Spline`
1901
+
1902
+ - ``interpolation_key`` -- (default: ``None``) key which the
1903
+ resulting interpolation will be associated to ; a default
1904
+ value is given if none is provided
1905
+ - ``verbose`` -- boolean (default: ``False``); prints information about
1906
+ the interpolation in progress
1907
+
1908
+ OUTPUT: built interpolation object
1909
+
1910
+ EXAMPLES:
1911
+
1912
+ Interpolating a numerical solution previously computed::
1913
+
1914
+ sage: M = Manifold(3, 'M')
1915
+ sage: X.<x1,x2,x3> = M.chart()
1916
+ sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T')
1917
+ sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2)
1918
+ sage: D = X.symbolic_velocities()
1919
+ sage: eqns = [q*B/m*D[1], -q*B/m*D[0], 0]
1920
+ sage: p = M.point((0,0,0), name='p')
1921
+ sage: Tp = M.tangent_space(p)
1922
+ sage: v = Tp((1,0,1))
1923
+ sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c')
1924
+
1925
+ sage: # needs scipy
1926
+ sage: sol = c.solve(method='odeint',
1927
+ ....: solution_key='sol_T1',
1928
+ ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1})
1929
+ sage: interp = c.interpolate(method='cubic spline',
1930
+ ....: solution_key='sol_T1',
1931
+ ....: interpolation_key='interp_T1',
1932
+ ....: verbose=True)
1933
+ Interpolation completed and associated with the key
1934
+ 'interp_T1' (if this key already referred to a former
1935
+ interpolation, such an interpolation was erased).
1936
+ sage: interp = c.interpolate(verbose=True)
1937
+ Interpolating the numerical solution associated with the
1938
+ key 'sol_T1' by default...
1939
+ Performing cubic spline interpolation by default...
1940
+ Resulting interpolation will be associated with the key
1941
+ 'cubic spline-interp-sol_T1' by default.
1942
+ Interpolation completed and associated with the key
1943
+ 'cubic spline-interp-sol_T1' (if this key already referred
1944
+ to a former interpolation, such an interpolation was
1945
+ erased).
1946
+
1947
+ TESTS::
1948
+
1949
+ sage: interp = c.interpolate(solution_key='my solution')
1950
+ Traceback (most recent call last):
1951
+ ...
1952
+ ValueError: no existing key 'my solution' referring to any
1953
+ numerical solution
1954
+ sage: interp = c.interpolate(solution_key='sol_T1', # needs scipy
1955
+ ....: method='my method')
1956
+ Traceback (most recent call last):
1957
+ ...
1958
+ ValueError: no available method of interpolation referred to
1959
+ as 'my method'
1960
+ """
1961
+ if solution_key is None:
1962
+ if 'odeint' in self._solutions:
1963
+ solution_key = 'odeint'
1964
+ else:
1965
+ solution_key = next(iter(self._solutions)) # will raise
1966
+ # error if self._solutions empty
1967
+ if verbose:
1968
+ print("Interpolating the numerical solution " +
1969
+ "associated with the key " +
1970
+ "'{}' ".format(solution_key) +
1971
+ "by default...")
1972
+ elif solution_key not in self._solutions:
1973
+ raise ValueError("no existing key " +
1974
+ "'{}' ".format(solution_key) +
1975
+ "referring to any numerical solution")
1976
+
1977
+ if method is None:
1978
+ method = 'cubic spline'
1979
+ if verbose:
1980
+ print("Performing cubic spline interpolation by "
1981
+ "default...")
1982
+
1983
+ if interpolation_key is None:
1984
+ interpolation_key = "{}-interp-".format(method)
1985
+ interpolation_key += "{}".format(solution_key)
1986
+ if verbose:
1987
+ print("Resulting interpolation will be associated " +
1988
+ "with the key '{}' ".format(interpolation_key) +
1989
+ "by default.")
1990
+
1991
+ if method == 'cubic spline':
1992
+ self._interpolations[interpolation_key] = []
1993
+ dim = self.codomain().dim()
1994
+ if not isinstance(self._solutions[solution_key][0], tuple):
1995
+ for i in range(dim):
1996
+ coordinate_curve = []
1997
+ for point in self._solutions[solution_key]:
1998
+ coordinate_curve += [[point[0], point[i+1]]]
1999
+ self._interpolations[interpolation_key] += [Spline(coordinate_curve)]
2000
+ else: # case multi charts
2001
+ j = 0
2002
+ for chart, sol in self._solutions[solution_key]:
2003
+ interp_chart = []
2004
+ for i in range(dim):
2005
+ coordinate_curve = []
2006
+ for point in sol:
2007
+ coordinate_curve += [[point[0], point[i + 1]]]
2008
+ interp_chart += [Spline(coordinate_curve)]
2009
+ self._interpolations[interpolation_key] += [(chart, interp_chart)]
2010
+ self._interpolations[interpolation_key+"_chart_"+str(j)] = interp_chart
2011
+ j += 1
2012
+ else:
2013
+ raise ValueError("no available method of interpolation " +
2014
+ "referred to as '{}'".format(method))
2015
+
2016
+ if verbose:
2017
+ print("Interpolation completed and associated with the " +
2018
+ "key '{}' ".format(interpolation_key) +
2019
+ "(if this key already referred to a former " +
2020
+ "interpolation, such an interpolation was erased).")
2021
+
2022
+ return self._interpolations[interpolation_key]
2023
+
2024
+ def interpolation(self, interpolation_key=None, verbose=False):
2025
+ r"""
2026
+ Return the interpolation object associated with the given key.
2027
+
2028
+ INPUT:
2029
+
2030
+ - ``interpolation_key`` -- (default: ``None``) key which the
2031
+ requested interpolation is associated to; a default
2032
+ value is chosen if none is provided
2033
+ - ``verbose`` -- boolean (default: ``False``); prints information about
2034
+ the interpolation object returned
2035
+
2036
+ OUTPUT: requested interpolation object
2037
+
2038
+ EXAMPLES:
2039
+
2040
+ Requesting an interpolation object previously computed::
2041
+
2042
+ sage: M = Manifold(3, 'M')
2043
+ sage: X.<x1,x2,x3> = M.chart()
2044
+ sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T')
2045
+ sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2)
2046
+ sage: D = X.symbolic_velocities()
2047
+ sage: eqns = [q*B/m*D[1], -q*B/m*D[0], 0]
2048
+ sage: p = M.point((0,0,0), name='p')
2049
+ sage: Tp = M.tangent_space(p)
2050
+ sage: v = Tp((1,0,1))
2051
+ sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c')
2052
+
2053
+ sage: # needs scipy
2054
+ sage: sol = c.solve(method='odeint',
2055
+ ....: solution_key='sol_T1',
2056
+ ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1})
2057
+ sage: interp = c.interpolate(method='cubic spline',
2058
+ ....: solution_key='sol_T1',
2059
+ ....: interpolation_key='interp_T1')
2060
+ sage: default_interp = c.interpolation(verbose=True)
2061
+ Returning the interpolation associated with the key
2062
+ 'interp_T1' by default...
2063
+ sage: default_interp == interp
2064
+ True
2065
+ sage: interp_mute = c.interpolation()
2066
+ sage: interp_mute == interp
2067
+ True
2068
+
2069
+ TESTS::
2070
+
2071
+ sage: c.interpolation(interpolation_key='my interp')
2072
+ Traceback (most recent call last):
2073
+ ...
2074
+ ValueError: no existing key 'my interp' referring to any
2075
+ interpolation
2076
+ """
2077
+
2078
+ if interpolation_key is None:
2079
+ if 'cubic spline' in self._interpolations:
2080
+ interpolation_key = 'cubic spline'
2081
+ else:
2082
+ interpolation_key = next(iter(self._interpolations)) # will
2083
+ # raise error if self._interpolations empty
2084
+ if verbose:
2085
+ print("Returning the interpolation associated with " +
2086
+ "the key '{}' ".format(interpolation_key) +
2087
+ "by default...")
2088
+ elif interpolation_key not in self._interpolations:
2089
+ raise ValueError("no existing key " +
2090
+ "'{}' ".format(interpolation_key) +
2091
+ "referring to any interpolation")
2092
+
2093
+ return self._interpolations[interpolation_key]
2094
+
2095
+ def __call__(self, t, interpolation_key=None,
2096
+ verbose=False):
2097
+ r"""
2098
+ Return the image of the curve for the given value of the curve
2099
+ parameter, using the chosen interpolation.
2100
+
2101
+ INPUT:
2102
+
2103
+ - ``t`` -- curve parameter value at which the curve is evaluated
2104
+ - ``interpolation_key`` -- (default: ``None``) key which the
2105
+ interpolation requested to compute the point is associated to;
2106
+ a default value is chosen if none is provided
2107
+ - ``verbose`` -- boolean (default: ``False``); prints information about
2108
+ the interpolation used
2109
+
2110
+ OUTPUT:
2111
+
2112
+ - :class:`~sage.manifolds.point.ManifoldPoint` on a
2113
+ manifold (codomain) with numerical coordinates
2114
+
2115
+ TESTS::
2116
+
2117
+ sage: M = Manifold(3, 'M')
2118
+ sage: X.<x1,x2,x3> = M.chart()
2119
+ sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T')
2120
+ sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2)
2121
+ sage: D = X.symbolic_velocities()
2122
+ sage: eqns = [q*B/m*D[1], -q*B/m*D[0], 0]
2123
+ sage: p = M.point((0,0,0), name='p')
2124
+ sage: Tp = M.tangent_space(p)
2125
+ sage: v = Tp((1,0,1))
2126
+ sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c')
2127
+
2128
+ sage: # needs scipy
2129
+ sage: sol = c.solve(method='odeint',
2130
+ ....: solution_key='sol_T1',
2131
+ ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1})
2132
+ sage: interp = c.interpolate(method='cubic spline',
2133
+ ....: solution_key='sol_T1',
2134
+ ....: interpolation_key='interp_T1')
2135
+ sage: c(1.1, interpolation_key='my interp')
2136
+ Traceback (most recent call last):
2137
+ ...
2138
+ ValueError: no existing key 'my interp' referring to any
2139
+ interpolation
2140
+ sage: p = c(1.1, verbose=True)
2141
+ Evaluating point coordinates from the interpolation
2142
+ associated with the key 'interp_T1' by default...
2143
+ sage: p.coordinates() # abs tol 1e-12
2144
+ (1.060743337877276, -0.21538352256822146, 1.1)
2145
+ """
2146
+
2147
+ if interpolation_key is None:
2148
+ if 'cubic spline' in self._interpolations:
2149
+ interpolation_key = 'cubic spline'
2150
+ else:
2151
+ # will raise error if self._interpolations empty
2152
+ interpolation_key = next(iter(self._interpolations))
2153
+ if verbose:
2154
+ print("Evaluating point coordinates from the " +
2155
+ "interpolation associated with the key " +
2156
+ "'{}' by default...".format(interpolation_key))
2157
+ elif interpolation_key not in self._interpolations:
2158
+ raise ValueError("no existing key " +
2159
+ "'{}' ".format(interpolation_key) +
2160
+ "referring to any interpolation")
2161
+
2162
+ interpolation = self._interpolations[interpolation_key]
2163
+
2164
+ if not isinstance(interpolation[0], Spline):
2165
+ # partial test, in case future interpolation objects do not
2166
+ # contain lists of instances of the Spline class
2167
+ raise TypeError("unexpected type of interpolation object")
2168
+
2169
+ interpolated_coordinates = [coord_curve_spline(t)
2170
+ for coord_curve_spline in interpolation]
2171
+ return self.codomain().point(coords=interpolated_coordinates,
2172
+ chart=self._chart)
2173
+
2174
+ def tangent_vector_eval_at(self, t,
2175
+ interpolation_key=None, verbose=False):
2176
+ r"""
2177
+ Return the vector tangent to ``self`` at the given curve
2178
+ parameter with components evaluated from the given
2179
+ interpolation.
2180
+
2181
+ INPUT:
2182
+
2183
+ - ``t`` -- curve parameter value at which the tangent vector is
2184
+ evaluated
2185
+ - ``interpolation_key`` -- (default: ``None``) key which the
2186
+ interpolation requested to compute the tangent vector is
2187
+ associated to; a default value is chosen if none is provided
2188
+ - ``verbose`` -- boolean (default: ``False``); prints information about
2189
+ the interpolation used
2190
+
2191
+ OUTPUT:
2192
+
2193
+ - :class:`~sage.manifolds.differentiable.tangent_vector.TangentVector`
2194
+ tangent vector with numerical components
2195
+
2196
+ EXAMPLES:
2197
+
2198
+ Evaluating a vector tangent to the curve::
2199
+
2200
+ sage: M = Manifold(3, 'M')
2201
+ sage: X.<x1,x2,x3> = M.chart()
2202
+ sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T')
2203
+ sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2)
2204
+ sage: D = X.symbolic_velocities()
2205
+ sage: eqns = [q*B/m*D[1], -q*B/m*D[0], 0]
2206
+ sage: p = M.point((0,0,0), name='p')
2207
+ sage: Tp = M.tangent_space(p)
2208
+ sage: v = Tp((1,0,1))
2209
+ sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c')
2210
+
2211
+ sage: # needs scipy
2212
+ sage: sol = c.solve(method='odeint',
2213
+ ....: solution_key='sol_T1',
2214
+ ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1})
2215
+ sage: interp = c.interpolate(method='cubic spline',
2216
+ ....: solution_key='sol_T1',
2217
+ ....: interpolation_key='interp_T1')
2218
+ sage: tg_vec = c.tangent_vector_eval_at(1.22, verbose=True)
2219
+ Evaluating tangent vector components from the interpolation
2220
+ associated with the key 'interp_T1' by default...
2221
+ sage: tg_vec
2222
+ Tangent vector at Point on the 3-dimensional differentiable
2223
+ manifold M
2224
+ sage: tg_vec[:] # abs tol 1e-12
2225
+ [0.7392640422917979, -0.6734182509826023, 1.0]
2226
+ sage: tg_vec_mute = c.tangent_vector_eval_at(1.22,
2227
+ ....: interpolation_key='interp_T1')
2228
+ sage: tg_vec_mute == tg_vec
2229
+ True
2230
+
2231
+ TESTS::
2232
+
2233
+ sage: tg_vec = c.tangent_vector_eval_at(1.22,
2234
+ ....: interpolation_key='my interp')
2235
+ Traceback (most recent call last):
2236
+ ...
2237
+ ValueError: no existing key 'my interp' referring to any
2238
+ interpolation
2239
+ """
2240
+ if interpolation_key is None:
2241
+ if 'cubic spline' in self._interpolations:
2242
+ interpolation_key = 'cubic spline'
2243
+ else:
2244
+ # will raise error if self._interpolations empty
2245
+ interpolation_key = next(iter(self._interpolations))
2246
+ if verbose:
2247
+ print("Evaluating tangent vector components from the " +
2248
+ "interpolation associated with the key " +
2249
+ "'{}' by default...".format(interpolation_key))
2250
+ elif interpolation_key not in self._interpolations:
2251
+ raise ValueError("no existing key " +
2252
+ "'{}' ".format(interpolation_key) +
2253
+ "referring to any interpolation")
2254
+
2255
+ interpolation = self._interpolations[interpolation_key]
2256
+
2257
+ if not isinstance(interpolation[0], Spline):
2258
+ # partial test, in case future interpolation objects do not
2259
+ # contain lists of instances of the Spline class
2260
+ raise TypeError("unexpected type of interpolation object")
2261
+
2262
+ interpolated_coordinates = [coordinate_curve_spline(t)
2263
+ for coordinate_curve_spline in interpolation]
2264
+ M = self.codomain()
2265
+ p = M.point(interpolated_coordinates, chart=self._chart, name=None)
2266
+ Tp = M.tangent_space(p)
2267
+
2268
+ # by default, order=1 in method 'derivative' of a class Spline
2269
+ evaluated_tgt_vec_comp = [coord_curve_spline.derivative(t)
2270
+ for coord_curve_spline in interpolation]
2271
+ basis = self._chart.frame().at(p)
2272
+ return Tp(evaluated_tgt_vec_comp, basis=basis)
2273
+
2274
+ @options(thickness=1, plot_points=75, aspect_ratio='automatic',
2275
+ plot_points_tangent=10, width_tangent=1, scale=1)
2276
+ def plot_integrated(self, chart=None, ambient_coords=None,
2277
+ mapping=None, prange=None, interpolation_key=None,
2278
+ include_end_point=(True, True),
2279
+ end_point_offset=(0.001, 0.001), verbose=False, color='red',
2280
+ style='-', label_axes=True, display_tangent=False,
2281
+ color_tangent='blue', across_charts=False, **kwds):
2282
+ r"""
2283
+ Plot the 2D or 3D projection of ``self`` onto the space of the
2284
+ chosen two or three ambient coordinates, based on the
2285
+ interpolation of a numerical solution previously computed.
2286
+
2287
+ .. SEEALSO::
2288
+
2289
+ :class:`~sage.manifolds.differentiable.curve.DifferentiableCurve.plot`
2290
+ for complete information about the input.
2291
+
2292
+ ADDITIONAL INPUT:
2293
+
2294
+ - ``interpolation_key`` -- (default: ``None``) key associated to
2295
+ the interpolation object used for the plot; a default value
2296
+ is chosen if none is provided
2297
+ - ``verbose`` -- boolean (default: ``False``); prints information about
2298
+ the interpolation object used and the plotting in progress
2299
+ - ``display_tangent`` -- boolean (default: ``False``); determines whether
2300
+ some tangent vectors should also be plotted
2301
+ - ``color_tangent`` -- (default: ``blue``) color of the tangent
2302
+ vectors when these are plotted
2303
+ - ``plot_points_tangent`` -- (default: 10) number of tangent
2304
+ vectors to display when these are plotted
2305
+ - ``width_tangent`` -- (default: 1) sets the width of the arrows
2306
+ representing the tangent vectors
2307
+ - ``scale`` -- (default: 1) scale applied to the tangent vectors
2308
+ before displaying them
2309
+
2310
+ EXAMPLES:
2311
+
2312
+ Trajectory of a particle of unit mass and unit charge in an
2313
+ unit, axial, uniform, stationary magnetic field::
2314
+
2315
+ sage: M = Manifold(3, 'M')
2316
+ sage: X.<x1,x2,x3> = M.chart()
2317
+ sage: var('t')
2318
+ t
2319
+ sage: D = X.symbolic_velocities()
2320
+ sage: eqns = [D[1], -D[0], 0]
2321
+ sage: p = M.point((0,0,0), name='p')
2322
+ sage: Tp = M.tangent_space(p)
2323
+ sage: v = Tp((1,0,1))
2324
+ sage: c = M.integrated_curve(eqns, D, (t,0,6), v, name='c')
2325
+
2326
+ sage: # needs scipy
2327
+ sage: sol = c.solve()
2328
+ sage: interp = c.interpolate()
2329
+ sage: c_plot_2d = c.plot_integrated(ambient_coords=[x1, x2],
2330
+ ....: thickness=2.5,
2331
+ ....: display_tangent=True, plot_points=200,
2332
+ ....: plot_points_tangent=10, scale=0.5,
2333
+ ....: color='blue', color_tangent='red',
2334
+ ....: verbose=True)
2335
+ Plotting from the interpolation associated with the key
2336
+ 'cubic spline-interp-odeint' by default...
2337
+ A tiny final offset equal to 0.000301507537688442 was
2338
+ introduced for the last point in order to safely compute it
2339
+ from the interpolation.
2340
+ sage: c_plot_2d
2341
+ Graphics object consisting of 11 graphics primitives
2342
+
2343
+ .. PLOT::
2344
+
2345
+ M = Manifold(3, 'M')
2346
+ X = M.chart('x1 x2 x3'); x1, x2, x3 = X[:]
2347
+ D = X.symbolic_velocities()
2348
+ eqns = [D[1], -D[0], 0]
2349
+ p = M.point((0,0,0), name='p')
2350
+ Tp = M.tangent_space(p)
2351
+ v = Tp((1,0,1))
2352
+ t = var('t')
2353
+ c = M.integrated_curve(eqns, D, (t, 0, 6), v, name='c')
2354
+ sol = c.solve()
2355
+ interp = c.interpolate()
2356
+ c_plot_2d_1 = c.plot_integrated(ambient_coords=[x1, x2],
2357
+ thickness=2.5,
2358
+ display_tangent=True, plot_points=200,
2359
+ plot_points_tangent=10, scale=0.5,
2360
+ color='blue', color_tangent='red')
2361
+ sphinx_plot(c_plot_2d_1)
2362
+ """
2363
+ from sage.manifolds.chart import RealChart
2364
+
2365
+ #
2366
+ # Get the @options plot_points from kwds
2367
+ #
2368
+ plot_points = kwds.pop('plot_points')
2369
+
2370
+ #
2371
+ # Interpolation to use
2372
+ #
2373
+ if interpolation_key is None:
2374
+ if 'cubic spline' in self._interpolations:
2375
+ interpolation_key = 'cubic spline'
2376
+ else:
2377
+ if across_charts:
2378
+ for key in self._interpolations:
2379
+ if key[-8:-1] != '_chart_': # check if not a subplot
2380
+ interpolation_key = key
2381
+ break
2382
+ else:
2383
+ raise ValueError("Did you forget to "
2384
+ "integrate or interpolate the result?")
2385
+ else:
2386
+ interpolation_key = next(iter(self._interpolations))
2387
+ # will raise error if self._interpolations empty
2388
+
2389
+ if verbose:
2390
+ print("Plotting from the interpolation associated " +
2391
+ "with the key '{}' ".format(interpolation_key) +
2392
+ "by default...")
2393
+ elif interpolation_key not in self._interpolations:
2394
+ raise ValueError("no existing key '{}' ".format(interpolation_key)
2395
+ + "referring to any interpolation")
2396
+
2397
+ interpolation = self._interpolations[interpolation_key]
2398
+
2399
+ if across_charts:
2400
+ len_tot = sum(len(interp[1][0]) for interp in interpolation)
2401
+ if isinstance(color, list):
2402
+ color = color * (len(interpolation) // 3 + 1)
2403
+ else:
2404
+ color = color * len(interpolation)
2405
+ res = 0
2406
+ for i in range(len(interpolation)):
2407
+ nb_pts = int(float(plot_points)*len(interpolation[i][1][0])/len_tot)
2408
+ self._chart = interpolation[i][0]
2409
+ res += self.plot_integrated(chart=chart, ambient_coords=ambient_coords,
2410
+ mapping=mapping, prange=prange,
2411
+ interpolation_key=interpolation_key+"_chart_"+str(i),
2412
+ include_end_point=include_end_point,
2413
+ end_point_offset=end_point_offset,
2414
+ verbose=verbose, color=color[i],
2415
+ style=style, label_axes=False,
2416
+ display_tangent=display_tangent,
2417
+ color_tangent=color_tangent,
2418
+ across_charts=False,
2419
+ plot_points=nb_pts, **kwds)
2420
+
2421
+ return res
2422
+
2423
+ #
2424
+ # Get the remaining @options from kwds
2425
+ #
2426
+ thickness = kwds.pop('thickness')
2427
+ aspect_ratio = kwds.pop('aspect_ratio')
2428
+
2429
+ #
2430
+ # The mapping, if present, and the chart with respect to which the curve
2431
+ # is plotted
2432
+ #
2433
+ if mapping is None:
2434
+ i0 = self.codomain().start_index()
2435
+ if chart is None:
2436
+ chart = self._chart
2437
+ else:
2438
+ if not isinstance(chart, RealChart):
2439
+ raise TypeError("{} is not a real chart".format(chart))
2440
+ mapping = self.codomain().identity_map()
2441
+ else:
2442
+ i0 = mapping.codomain().start_index()
2443
+ if chart is None:
2444
+ chart = mapping.codomain().default_chart()
2445
+ elif not isinstance(chart, RealChart):
2446
+ raise TypeError("{} is not a real chart".format(chart))
2447
+
2448
+ #
2449
+ # Coordinates of the above chart with respect to which the curve is
2450
+ # plotted
2451
+ #
2452
+ if ambient_coords is None:
2453
+ ambient_coords = chart[:] # all chart coordinates are used
2454
+ n_pc = len(ambient_coords)
2455
+ if n_pc != 2 and n_pc != 3:
2456
+ raise ValueError("the number of coordinates involved in " +
2457
+ "the plot must be either 2 or 3, " +
2458
+ "not {}".format(n_pc))
2459
+
2460
+ # From now on, 'pc' will denote coordinates in terms of which
2461
+ # the curve is plotted (i.e. the "ambient coordinates"), while
2462
+ # 'coord' will denote coordinates on self.domain(); of course,
2463
+ # when, for instance, the mapping is the identity map, these may
2464
+ # be the same.
2465
+
2466
+ # indices of plot coordinates
2467
+ # will raise an error if ambient_coords are not associated with chart
2468
+ ind_pc = [chart[:].index(pc) + i0 for pc in ambient_coords]
2469
+
2470
+ #
2471
+ # Maximal parameter range for the plot of the chosen
2472
+ # interpolation
2473
+ #
2474
+ # these two lines are the only general way to get the maximal
2475
+ # parameter range since, at this point, there is no clue about
2476
+ # the solution from which 'interpolation' was build, and it would
2477
+ # be an obvious error to declare param_min=self.domain().lower_bound()
2478
+ # for instance, since this might be an expression
2479
+ param_min = interpolation[0][0][0]
2480
+ param_max = interpolation[0][-1][0]
2481
+
2482
+ if prange is None:
2483
+ prange = (param_min, param_max)
2484
+ elif not isinstance(prange, (tuple, list)):
2485
+ raise TypeError("{} is neither ".format(prange) +
2486
+ "a tuple nor a list")
2487
+ elif len(prange) != 2:
2488
+ raise ValueError("the argument prange must be a " +
2489
+ "tuple/list of 2 elements")
2490
+ else:
2491
+ p = prange # 'p' declared only for the line below to be shorter
2492
+ if p[0] < param_min or p[0] > param_max or p[1] < param_min or p[1] > param_max:
2493
+ raise ValueError("parameter range should be a " +
2494
+ "subinterval of the curve domain " +
2495
+ "({})".format(self.domain()))
2496
+
2497
+ tmin = numerical_approx(prange[0])
2498
+ tmax = numerical_approx(prange[1])
2499
+
2500
+ if not include_end_point[0]:
2501
+ tmin += numerical_approx(end_point_offset[0])
2502
+
2503
+ if not include_end_point[1]:
2504
+ tmax -= numerical_approx(end_point_offset[1])
2505
+
2506
+ if mapping is None:
2507
+ if not isinstance(interpolation[0], Spline):
2508
+ # partial test in case future interpolation objects do not
2509
+ # contain lists of instances of the Spline class
2510
+ raise TypeError("unexpected type of interpolation object")
2511
+
2512
+ #
2513
+ # List of points for the plot curve
2514
+ #
2515
+ plot_curve = []
2516
+ dt = (tmax - tmin) / (plot_points - 1)
2517
+ t = tmin
2518
+
2519
+ for k in range(plot_points):
2520
+ if k == 0 and t < param_min:
2521
+ # This might happen for the first point (i.e. k = 0)
2522
+ # when prange[0], and hence tmin, should equal param_min;
2523
+ # but mere numerical rounding coming from having taken
2524
+ # tmin = numerical_approx(prange[0]) might
2525
+ # raise errors from trying to evaluate the
2526
+ # interpolation at a time smaller than
2527
+ # self.domain.lower_bound(). Hence the line below
2528
+ # that adds 1% of the step to compute even more
2529
+ # safely the first point
2530
+ t = param_min + 0.01*dt
2531
+ if verbose:
2532
+ print("A tiny initial offset equal to " +
2533
+ "{} ".format(0.01*dt) +
2534
+ "was introduced for the first point " +
2535
+ "only, in order to safely compute " +
2536
+ "it from the interpolation.")
2537
+
2538
+ if k == plot_points-1 and t > param_max:
2539
+ # This might happen for the last point
2540
+ # (i.e. k = plot_points-1) when prange[1], and hence
2541
+ # tmax, should equal param_max; but mere numerical
2542
+ # rounding coming from having taken
2543
+ # tmax = numerical_approx(prange[1) might raise errors
2544
+ # from trying to evaluate the interpolation at a time
2545
+ # greater than self.domain.upper_bound().
2546
+ # Hence the line below that subtract 1% of the
2547
+ # step to compute even more safely the last point
2548
+ t = param_max - 0.01*dt
2549
+ if verbose:
2550
+ print("A tiny final offset equal to " +
2551
+ "{} ".format(0.01*dt) +
2552
+ "was introduced for the last point " +
2553
+ "in order to safely compute " +
2554
+ "it from the interpolation.")
2555
+
2556
+ plot_curve.append([interpolation[j-i0](t) for j in ind_pc])
2557
+
2558
+ if k == 0 and t > tmin:
2559
+ # in case an initial offset was earlier added to
2560
+ # 'tmin' in order to avoid errors, it is now needed
2561
+ # to cancel this offset for the next steps
2562
+ t = tmin
2563
+ t += dt
2564
+
2565
+ if display_tangent:
2566
+ from sage.plot.arrow import arrow2d
2567
+ from sage.plot.graphics import Graphics
2568
+ from sage.plot.plot3d.shapes import arrow3d
2569
+
2570
+ scale = kwds.pop('scale')
2571
+ plot_points_tangent = kwds.pop('plot_points_tangent')
2572
+ width_tangent = kwds.pop('width_tangent')
2573
+
2574
+ plot_vectors = Graphics()
2575
+ dt = (tmax - tmin) / (plot_points_tangent - 1)
2576
+ t = tmin
2577
+
2578
+ for k in range(plot_points_tangent):
2579
+ if k == 0 and t < param_min:
2580
+ # This might happen for the first point
2581
+ # (i.e. k = 0) when prange[0], and hence tmin
2582
+ # should equal param_min; but mere numerical
2583
+ # rounding coming from having taken
2584
+ # tmin = numerical_approx(prange[0]) might
2585
+ # raise errors from trying to evaluate the
2586
+ # interpolation at a time smaller than
2587
+ # self.domain.lower_bound().
2588
+ # Hence the line below that add 1% of the step
2589
+ # to compute even more safely the first point.
2590
+ t = param_min + 0.01*dt
2591
+ if verbose:
2592
+ print("A tiny initial offset equal to " +
2593
+ "{} ".format(0.01*dt) +
2594
+ "was introduced for the first point " +
2595
+ "only, in order to safely compute " +
2596
+ "it from the interpolation.")
2597
+
2598
+ if k == plot_points_tangent - 1 and t > param_max:
2599
+ # This might happen for the last point
2600
+ # (i.e. k = plot_points_tangent-1) when
2601
+ # prange[1], and hence tmax, should equal
2602
+ # param_max; but mere numerical rounding coming from
2603
+ # having taken tmax = numerical_approx(prange[1)
2604
+ # might raise errors from trying to evaluate the
2605
+ # interpolation at a time greater than
2606
+ # self.domain.upper_bound(). Hence the line below
2607
+ # that subtracts 1% of the step to compute even
2608
+ # more safely the last point.
2609
+ t = param_max - 0.01*dt
2610
+ if verbose:
2611
+ print("A tiny final offset equal to " +
2612
+ "{} ".format(0.01*dt) +
2613
+ "was introduced for the last point " +
2614
+ "in order to safely compute " +
2615
+ "it from the interpolation.")
2616
+
2617
+ # interpolated ambient coordinates:
2618
+ xp = [interpolation[j-i0](t) for j in ind_pc]
2619
+
2620
+ # tangent vector ambiant components evaluated
2621
+ # from the interpolation:
2622
+ vec = [coordinate_curve_spline.derivative(t)
2623
+ for coordinate_curve_spline in interpolation]
2624
+
2625
+ coord_tail = xp
2626
+ coord_head = [xp[j] + scale*vec[j]
2627
+ for j in range(len(xp))]
2628
+
2629
+ if coord_head != coord_tail:
2630
+ if n_pc == 2:
2631
+ plot_vectors += arrow2d(tailpoint=coord_tail,
2632
+ headpoint=coord_head,
2633
+ color=color_tangent,
2634
+ width=width_tangent)
2635
+ else:
2636
+ plot_vectors += arrow3d(coord_tail,
2637
+ coord_head,
2638
+ color=color_tangent,
2639
+ width=width_tangent)
2640
+
2641
+ if k == 0 and t > tmin:
2642
+ # in case an initial offset was earlier added
2643
+ # to 'tmin' in order to avoid errors, it is now
2644
+ # needed to cancel this offset for the next steps
2645
+ t = tmin
2646
+ t += dt
2647
+
2648
+ return plot_vectors + DifferentiableCurve._graphics(self,
2649
+ plot_curve, ambient_coords,
2650
+ thickness=thickness,
2651
+ aspect_ratio=aspect_ratio,
2652
+ color=color,
2653
+ style=style,
2654
+ label_axes=label_axes)
2655
+
2656
+ return DifferentiableCurve._graphics(self, plot_curve,
2657
+ ambient_coords, thickness=thickness,
2658
+ aspect_ratio=aspect_ratio, color=color,
2659
+ style=style, label_axes=label_axes)
2660
+ else:
2661
+ #
2662
+ # The coordinate expressions of the mapping and the
2663
+ # coordinates involved
2664
+ #
2665
+ for chart_pair in mapping._coord_expression:
2666
+ subs = (chart_pair[0]._subcharts, chart_pair[1]._subcharts)
2667
+ # 'subs' declared only for the line below to be shorter
2668
+ if self._chart in subs[0] and chart in subs[1]:
2669
+ transf = {}
2670
+ required_coords = set()
2671
+ for pc in ambient_coords:
2672
+ jpc = chart[:].index(pc)
2673
+ AUX = mapping._coord_expression[chart_pair]
2674
+ # 'AUX' used only for the lines of source code
2675
+ # to be shorter
2676
+ transf[pc] = AUX.expr()[jpc]
2677
+ AUX2 = transf[pc].variables() # idem
2678
+ required_coords = required_coords.union(AUX2)
2679
+ break
2680
+ else:
2681
+ raise ValueError("no expression has been found for " +
2682
+ "{} in terms of {}".format(self,chart))
2683
+
2684
+ # fastf is the fast version of a substitution + numerical evaluation
2685
+ # using fast_callable.
2686
+ fastf = [fast_callable(transf[chart[i]], vars=tuple(self._chart[:]))
2687
+ for i in ind_pc]
2688
+
2689
+ if not isinstance(interpolation[0], Spline):
2690
+ # partial test, in case future interpolation objects do not
2691
+ # contain lists of instances of the Spline class
2692
+ raise TypeError("unexpected type of interpolation object")
2693
+
2694
+ #
2695
+ # List of points for the plot curve
2696
+ #
2697
+ plot_curve = []
2698
+ dt = (tmax - tmin) / (plot_points - 1)
2699
+ t = tmin
2700
+ required_coords_values = {}
2701
+
2702
+ for k in range(plot_points):
2703
+ if k == 0 and t < param_min:
2704
+ # This might happen for the first point (i.e. k = 0)
2705
+ # when prange[0], and hence tmin, should equal param_min;
2706
+ # but mere numerical rounding coming from having taken
2707
+ # tmin = numerical_approx(prange[0]) might
2708
+ # raise errors from trying to evaluate the
2709
+ # interpolation at a time smaller than
2710
+ # self.domain.lower_bound(). Hence the line below that adds
2711
+ # 1% of the step to compute even more safely the first point
2712
+ t = param_min + 0.01*dt
2713
+ if verbose:
2714
+ print("A tiny initial offset equal to " +
2715
+ "{} ".format(0.01*dt) +
2716
+ "was introduced for the first point " +
2717
+ "only, in order to safely compute " +
2718
+ "it from the interpolation.")
2719
+
2720
+ if k == plot_points - 1 and t > param_max:
2721
+ # This might happen for the last point (i.e. k = plot_points-1)
2722
+ # when prange[1], and hence tmax, should equal
2723
+ # param_max; but mere numerical rounding coming from
2724
+ # having taken tmax = numerical_approx(prange[1)
2725
+ # might raise errors from trying to evaluate the
2726
+ # interpolation at a time greater than
2727
+ # self.domain.upper_bound(). Hence the line below that
2728
+ # subtracts 1% of the step to compute even more safely
2729
+ # the last point.
2730
+ t = param_max - 0.01*dt
2731
+ if verbose:
2732
+ print("A tiny final offset equal to " +
2733
+ "{} ".format(0.01*dt) +
2734
+ "was introduced for the last point " +
2735
+ "in order to safely compute " +
2736
+ "it from the interpolation.")
2737
+
2738
+ # list of coordinates, argument of fastf, the fast diff_map
2739
+ arg = [inter(t) for inter in interpolation]
2740
+ # evaluation of fastf
2741
+ xp = [fastf[j](*arg) for j in range(len(ambient_coords))]
2742
+ plot_curve.append(xp)
2743
+
2744
+ if k == 0 and t > tmin:
2745
+ # in case an initial offset was earlier added to
2746
+ # 'tmin' in order to avoid errors, it is now needed
2747
+ # to cancel this offset for the next steps
2748
+ t = tmin
2749
+
2750
+ t += dt
2751
+
2752
+ if display_tangent:
2753
+ from sage.plot.arrow import arrow2d
2754
+ from sage.plot.graphics import Graphics
2755
+ from sage.plot.plot3d.shapes import arrow3d
2756
+
2757
+ scale = kwds.pop('scale')
2758
+ plot_points_tangent = kwds.pop('plot_points_tangent')
2759
+ width_tangent = kwds.pop('width_tangent')
2760
+
2761
+ plot_vectors = Graphics()
2762
+ dt = (tmax - tmin) / (plot_points_tangent - 1)
2763
+ t = tmin
2764
+ Dcoord_Dt = {}
2765
+
2766
+ Dpc_Dcoord = {}
2767
+ for pc in ambient_coords:
2768
+ Dpc_Dcoord[pc] = {}
2769
+ for coord in transf[pc].variables():
2770
+ Dpc_Dcoord[pc][coord] = transf[pc].derivative(coord)
2771
+
2772
+ for k in range(plot_points_tangent):
2773
+ if k == 0 and t < param_min:
2774
+ # This might happen for the first point (i.e. k = 0)
2775
+ # when prange[0], and hence tmin, should equal param_min;
2776
+ # but mere numerical rounding coming from having taken
2777
+ # tmin = numerical_approx(prange[0]) might
2778
+ # raise errors from trying to evaluate the
2779
+ # interpolation at a time smaller than
2780
+ # self.domain.lower_bound(). Hence the line below
2781
+ # that adds 1% of the step to compute even more
2782
+ # safely the first point
2783
+ t = param_min + 0.01*dt
2784
+ if verbose:
2785
+ print("A tiny initial offset equal to " +
2786
+ "{} ".format(0.01*dt) +
2787
+ "was introduced for the first point " +
2788
+ "only, in order to safely compute " +
2789
+ "it from the interpolation.")
2790
+
2791
+ if k == plot_points_tangent - 1 and t > param_max:
2792
+ # This might happen for the last point
2793
+ # (i.e. k = plot_points_tangent-1) when
2794
+ # when prange[1], and hence tmax, should equal
2795
+ # param_max; but mere numerical rounding coming from
2796
+ # having taken tmax = numerical_approx(prange[1)
2797
+ # might raise errors from trying to evaluate the
2798
+ # interpolation at a time greater than
2799
+ # self.domain.upper_bound(). Hence the line below
2800
+ # that subtracts 1% of the step to compute even
2801
+ # more safely the last point
2802
+ t = param_max - 0.01*dt
2803
+ if verbose:
2804
+ print("A tiny final offset equal to " +
2805
+ "{} ".format(0.01*dt) +
2806
+ "was introduced for the last point " +
2807
+ "in order to safely compute " +
2808
+ "it from the interpolation.")
2809
+
2810
+ for coord in required_coords:
2811
+ i = self._chart[:].index(coord)
2812
+ AUX = interpolation[i] # 'AUX' only used
2813
+ # for the lines below to be shorter
2814
+ required_coords_values[coord] = AUX(t)
2815
+ Dcoord_Dt[coord] = AUX.derivative(t)
2816
+
2817
+ xp = []
2818
+ pushed_vec = []
2819
+ for j in ind_pc:
2820
+ pc = chart[j]
2821
+ AUX = transf[pc]
2822
+ AUX = AUX.substitute(required_coords_values)
2823
+ # 'AUX' only used for the lines of code to
2824
+ # be shorter
2825
+ xp += [numerical_approx(AUX)]
2826
+
2827
+ pushed_comp = 0
2828
+ for coord in transf[pc].variables():
2829
+ D = Dpc_Dcoord[pc][coord]
2830
+ D = D.substitute(required_coords_values)
2831
+ D = numerical_approx(D)
2832
+ pushed_comp += Dcoord_Dt[coord] * D
2833
+
2834
+ pushed_vec += [pushed_comp]
2835
+
2836
+ coord_tail = xp
2837
+ coord_head = [val + scale*pushed_vec[j]
2838
+ for j, val in enumerate(xp)]
2839
+
2840
+ if coord_head != coord_tail:
2841
+ if n_pc == 2:
2842
+ plot_vectors += arrow2d(tailpoint=coord_tail,
2843
+ headpoint=coord_head,
2844
+ color=color_tangent,
2845
+ width=width_tangent)
2846
+ else:
2847
+ plot_vectors += arrow3d(coord_tail,
2848
+ coord_head,
2849
+ color=color_tangent,
2850
+ width=width_tangent)
2851
+
2852
+ if k == 0 and t > tmin:
2853
+ # in case an initial offset was earlier added to
2854
+ # 'tmin' in order to avoid errors, it is now needed
2855
+ # to cancel this offset for the next steps
2856
+ t = tmin
2857
+
2858
+ t += dt
2859
+ return plot_vectors + DifferentiableCurve._graphics(self,
2860
+ plot_curve, ambient_coords,
2861
+ thickness=thickness,
2862
+ aspect_ratio=aspect_ratio,
2863
+ color=color,
2864
+ style=style,
2865
+ label_axes=label_axes)
2866
+ return DifferentiableCurve._graphics(self, plot_curve,
2867
+ ambient_coords, thickness=thickness,
2868
+ aspect_ratio=aspect_ratio, color=color,
2869
+ style=style, label_axes=label_axes)
2870
+
2871
+
2872
+ class IntegratedAutoparallelCurve(IntegratedCurve):
2873
+ r"""
2874
+ Autoparallel curve on the manifold with respect to a given
2875
+ affine connection.
2876
+
2877
+ INPUT:
2878
+
2879
+ - ``parent`` --
2880
+ :class:`~sage.manifolds.differentiable.manifold_homset.IntegratedAutoparallelCurveSet`
2881
+ the set of curves `\mathrm{Hom_{autoparallel}}(I, M)` to which the
2882
+ curve belongs
2883
+ - ``affine_connection`` --
2884
+ :class:`~sage.manifolds.differentiable.affine_connection.AffineConnection`
2885
+ affine connection with respect to which the curve is autoparallel
2886
+ - ``curve_parameter`` -- symbolic expression to be used as the
2887
+ parameter of the curve (the equations defining an instance of
2888
+ IntegratedAutoparallelCurve are such that ``t`` will actually be
2889
+ an affine parameter of the curve)
2890
+ - ``initial_tangent_vector`` --
2891
+ :class:`~sage.manifolds.differentiable.tangent_vector.TangentVector`
2892
+ initial tangent vector of the curve
2893
+ - ``chart`` -- (default: ``None``) chart on the manifold in terms of
2894
+ which the equations are expressed; if ``None`` the default chart
2895
+ of the manifold is assumed
2896
+ - ``name`` -- (default: ``None``) string; symbol given to the curve
2897
+ - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to
2898
+ denote the curve; if none is provided, ``name`` will be used
2899
+
2900
+ EXAMPLES:
2901
+
2902
+ Autoparallel curves associated with the Mercator projection of the
2903
+ unit 2-sphere `\mathbb{S}^{2}`.
2904
+
2905
+ .. SEEALSO::
2906
+
2907
+ https://idontgetoutmuch.wordpress.com/2016/11/24/mercator-a-connection-with-torsion/
2908
+ for more details about Mercator projection.
2909
+
2910
+ On the Mercator projection, the lines of longitude all appear
2911
+ vertical and then all parallel with respect to each other.
2912
+ Likewise, all the lines of latitude appear horizontal and parallel
2913
+ with respect to each other.
2914
+ These curves may be recovered as autoparallel curves of a certain
2915
+ connection `\nabla` to be made explicit.
2916
+
2917
+ Start with declaring the standard polar coordinates
2918
+ `(\theta, \phi)` on `\mathbb{S}^{2}` and the
2919
+ corresponding coordinate frame `(e_{\theta}, e_{\phi})`::
2920
+
2921
+ sage: S2 = Manifold(2, 'S^2', start_index=1)
2922
+ sage: polar.<th,ph>=S2.chart()
2923
+ sage: epolar = polar.frame()
2924
+
2925
+ Normalizing `e_{\phi}` provides an orthonormal basis::
2926
+
2927
+ sage: ch_basis = S2.automorphism_field()
2928
+ sage: ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th)
2929
+ sage: epolar_ON = epolar.new_frame(ch_basis,'epolar_ON')
2930
+
2931
+ Denote `(\hat{e}_{\theta}, \hat{e}_{\phi})` such an orthonormal frame
2932
+ field. In any point, the vector field `\hat{e}_{\theta}` is
2933
+ normalized and tangent to the line of longitude through the point.
2934
+ Likewise, `\hat{e}_{\phi}` is normalized and tangent to the
2935
+ line of latitude.
2936
+
2937
+ Now, set an affine connection with respect to such fields that are
2938
+ parallelly transported in all directions, that is:
2939
+ `\nabla \hat{e}_{\theta} = \nabla \hat{e}_{\phi} = 0`.
2940
+ This is equivalent to setting all the connection coefficients to
2941
+ zero with respect to this frame::
2942
+
2943
+ sage: nab = S2.affine_connection('nab')
2944
+ sage: nab.set_coef(frame=epolar_ON)[:]
2945
+ [[[0, 0], [0, 0]], [[0, 0], [0, 0]]]
2946
+
2947
+ This connection is such that two vectors are parallel if their
2948
+ angles to a given meridian are the same.
2949
+ Check that this connection is compatible with the Euclidean
2950
+ metric tensor `g` induced on `\mathbb{S}^{2}`::
2951
+
2952
+ sage: g = S2.metric('g')
2953
+ sage: g[1,1], g[2,2] = 1, (sin(th))^2
2954
+ sage: nab(g)[:]
2955
+ [[[0, 0], [0, 0]], [[0, 0], [0, 0]]]
2956
+
2957
+ Yet, this connection is not the Levi-Civita connection, which
2958
+ implies that it has non-vanishing torsion::
2959
+
2960
+ sage: nab.torsion()[:]
2961
+ [[[0, 0], [0, 0]], [[0, cos(th)/sin(th)], [-cos(th)/sin(th), 0]]]
2962
+
2963
+ Set generic initial conditions for the autoparallel curves to
2964
+ compute::
2965
+
2966
+ sage: [th0, ph0, v_th0, v_ph0] = var('th0 ph0 v_th0 v_ph0')
2967
+ sage: p = S2.point((th0, ph0), name='p')
2968
+ sage: Tp = S2.tangent_space(p)
2969
+ sage: v = Tp((v_th0, v_ph0), basis=epolar_ON.at(p))
2970
+
2971
+ Note here that the components ``(v_th0, v_ph0)`` of the initial
2972
+ tangent vector ``v`` refer to the basis
2973
+ ``epolar_ON`` `= (\hat{e}_{\theta}, \hat{e}_{\phi})`
2974
+ and not the coordinate basis ``epolar`` `= (e_{\theta}, e_{\phi})`.
2975
+ This is merely to help picture the aspect of the tangent vector in
2976
+ the usual embedding of `\mathbb{S}^{2}` in
2977
+ `\mathbb{R}^{3}` thanks to using an orthonormal frame,
2978
+ since providing the components with respect to the coordinate basis
2979
+ would require multiplying the second component (i.e. the `\phi`
2980
+ component) in order to picture the vector in the same way.
2981
+ This subtlety will need to be taken into account later when the
2982
+ numerical curve will be compared to the analytical solution.
2983
+
2984
+ Now, declare the corresponding integrated autoparallel curve and display
2985
+ the differential system it satisfies::
2986
+
2987
+ sage: [t, tmin, tmax] = var('t tmin tmax')
2988
+ sage: c = S2.integrated_autoparallel_curve(nab, (t, tmin, tmax),
2989
+ ....: v, chart=polar, name='c')
2990
+ sage: sys = c.system(verbose=True)
2991
+ Autoparallel curve c in the 2-dimensional differentiable
2992
+ manifold S^2 equipped with Affine connection nab on the
2993
+ 2-dimensional differentiable manifold S^2, and integrated over
2994
+ the Real interval (tmin, tmax) as a solution to the following
2995
+ equations, written with respect to Chart (S^2, (th, ph)):
2996
+ <BLANKLINE>
2997
+ Initial point: Point p on the 2-dimensional differentiable
2998
+ manifold S^2 with coordinates [th0, ph0] with respect to
2999
+ Chart (S^2, (th, ph))
3000
+ Initial tangent vector: Tangent vector at Point p on the
3001
+ 2-dimensional differentiable manifold S^2 with
3002
+ components [v_th0, v_ph0/sin(th0)] with respect to Chart (S^2, (th, ph))
3003
+ <BLANKLINE>
3004
+ d(th)/dt = Dth
3005
+ d(ph)/dt = Dph
3006
+ d(Dth)/dt = 0
3007
+ d(Dph)/dt = -Dph*Dth*cos(th)/sin(th)
3008
+ <BLANKLINE>
3009
+
3010
+ Set a dictionary providing the parameter range and the initial
3011
+ conditions for a line of latitude and a line of longitude::
3012
+
3013
+ sage: dict_params={'latit':{tmin:0,tmax:3,th0:pi/4,ph0:0.1,v_th0:0,v_ph0:1},
3014
+ ....: 'longi':{tmin:0,tmax:3,th0:0.1,ph0:0.1,v_th0:1,v_ph0:0}}
3015
+
3016
+ Declare the Mercator coordinates `(\xi, \zeta)` and the
3017
+ corresponding coordinate change from the polar coordinates::
3018
+
3019
+ sage: mercator.<xi,ze> = S2.chart(r'xi:(-oo,oo):\xi ze:(0,2*pi):\zeta')
3020
+ sage: polar.transition_map(mercator, (log(tan(th/2)), ph))
3021
+ Change of coordinates from Chart (S^2, (th, ph)) to Chart
3022
+ (S^2, (xi, ze))
3023
+
3024
+ Ask for the identity map in terms of these charts in order to add
3025
+ this coordinate change to its dictionary of expressions. This is
3026
+ required to plot the curve with respect to the Mercator chart::
3027
+
3028
+ sage: identity = S2.identity_map()
3029
+ sage: identity.coord_functions(polar, mercator)
3030
+ Coordinate functions (log(sin(1/2*th)/cos(1/2*th)), ph) on the
3031
+ Chart (S^2, (th, ph))
3032
+
3033
+ Solve, interpolate and prepare the plot for the solutions
3034
+ corresponding to the two initial conditions previously set::
3035
+
3036
+ sage: # needs scipy sage.plot
3037
+ sage: graph2D_mercator = Graphics()
3038
+ sage: for key in dict_params:
3039
+ ....: sol = c.solve(solution_key='sol-'+key,
3040
+ ....: parameters_values=dict_params[key])
3041
+ ....: interp = c.interpolate(solution_key='sol-'+key,
3042
+ ....: interpolation_key='interp-'+key)
3043
+ ....: graph2D_mercator+=c.plot_integrated(interpolation_key='interp-'+key,
3044
+ ....: chart=mercator, thickness=2)
3045
+
3046
+ Prepare a grid of Mercator coordinates lines, and plot the curves
3047
+ over it::
3048
+
3049
+ sage: # needs scipy sage.plot
3050
+ sage: graph2D_mercator_coords=mercator.plot(chart=mercator,
3051
+ ....: number_values=8,color='yellow')
3052
+ sage: graph2D_mercator + graph2D_mercator_coords
3053
+ Graphics object consisting of 18 graphics primitives
3054
+
3055
+ .. PLOT::
3056
+
3057
+ S2 = Manifold(2, 'S^2', start_index=1)
3058
+ polar = S2.chart('th ph'); th, ph = polar[:]
3059
+ epolar = polar.frame()
3060
+ ch_basis = S2.automorphism_field()
3061
+ ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th)
3062
+ epolar_ON = epolar.new_frame(ch_basis, 'epolar_ON')
3063
+ nab = S2.affine_connection('nab')
3064
+ _ = nab.set_coef(frame=epolar_ON)
3065
+ t,tmin,tmax,th0,ph0,v_th0,v_ph0 = var('t tmin tmax th0 ph0 v_th0 v_ph0')
3066
+ p = S2.point((th0, ph0), name='p')
3067
+ Tp = S2.tangent_space(p)
3068
+ v = Tp((v_th0, v_ph0), basis=epolar_ON.at(p))
3069
+ c = S2.integrated_autoparallel_curve(nab, (t, tmin, tmax), v,
3070
+ chart=polar, name='c')
3071
+ dict_params={'latit':{tmin:0,tmax:3,th0:pi/4,ph0:0.1,v_th0:0,v_ph0:1},
3072
+ 'longi':{tmin:0,tmax:3,th0:0.1,ph0:0.1,v_th0:1,v_ph0:0}}
3073
+ mercator = S2.chart(r'xi:(-oo,oo):\xi ze:(0,2*pi):\zeta')
3074
+ xi,ze = var('xi ze')
3075
+ _ = polar.transition_map(mercator, (log(tan(th/2)), ph))
3076
+ identity = S2.identity_map()
3077
+ identity.coord_functions(polar, mercator)
3078
+ graph2D_mercator = Graphics()
3079
+ for key in dict_params:
3080
+ sol = c.solve(solution_key='sol-'+key,
3081
+ parameters_values=dict_params[key])
3082
+ interp = c.interpolate(solution_key='sol-'+key,
3083
+ interpolation_key='interp-'+key)
3084
+ graph2D_mercator += c.plot_integrated(interpolation_key='interp-'+key,
3085
+ chart=mercator, thickness=2)
3086
+ graph2D_mercator_coords = mercator.plot(chart=mercator,
3087
+ number_values=8, color='yellow')
3088
+ sphinx_plot(graph2D_mercator + graph2D_mercator_coords)
3089
+
3090
+ The resulting curves are horizontal and vertical as expected.
3091
+ It is easier to check that these are latitude and longitude lines
3092
+ respectively when plotting them on `\mathbb{S}^{2}`.
3093
+ To do so, use `\mathbb{R}^{3}` as the codomain of the standard
3094
+ map embedding `(\mathbb{S}^{2}, (\theta, \phi))` in the
3095
+ 3-dimensional Euclidean space::
3096
+
3097
+ sage: R3 = Manifold(3, 'R3', start_index=1)
3098
+ sage: cart.<X,Y,Z> = R3.chart()
3099
+ sage: euclid_embedding = S2.diff_map(R3,
3100
+ ....: {(polar, cart):[sin(th)*cos(ph),sin(th)*sin(ph),cos(th)]})
3101
+
3102
+ Plot the resulting curves on the grid of polar coordinates lines on
3103
+ `\mathbb{S}^{2}`::
3104
+
3105
+ sage: # needs sage.plot
3106
+ sage: graph3D_embedded_curves = Graphics()
3107
+ sage: for key in dict_params:
3108
+ ....: graph3D_embedded_curves += c.plot_integrated(interpolation_key='interp-'+key,
3109
+ ....: mapping=euclid_embedding, thickness=5,
3110
+ ....: display_tangent=True, scale=0.4, width_tangent=0.5)
3111
+ sage: graph3D_embedded_polar_coords = polar.plot(chart=cart,
3112
+ ....: mapping=euclid_embedding,
3113
+ ....: number_values=15, color='yellow')
3114
+ sage: graph3D_embedded_curves + graph3D_embedded_polar_coords
3115
+ Graphics3d Object
3116
+
3117
+ .. PLOT::
3118
+
3119
+ S2 = Manifold(2, 'S^2', start_index=1)
3120
+ polar = S2.chart('th ph'); th, ph = polar[:]
3121
+ epolar = polar.frame()
3122
+ ch_basis = S2.automorphism_field()
3123
+ ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th)
3124
+ epolar_ON = epolar.new_frame(ch_basis, 'epolar_ON')
3125
+ nab = S2.affine_connection('nab')
3126
+ _ = nab.set_coef(frame=epolar_ON)
3127
+ t,tmin,tmax,th0,ph0,v_th0,v_ph0 = var('t tmin tmax th0 ph0 v_th0 v_ph0')
3128
+ p = S2.point((th0, ph0), name='p')
3129
+ Tp = S2.tangent_space(p)
3130
+ v = Tp((v_th0, v_ph0), basis=epolar_ON.at(p))
3131
+ c = S2.integrated_autoparallel_curve(nab, (t, tmin, tmax), v,
3132
+ chart=polar, name='c')
3133
+ dict_params = {'latit':{tmin:0,tmax:3,th0:pi/4,ph0:0.1,v_th0:0,v_ph0:1},
3134
+ 'longi':{tmin:0,tmax:3,th0:0.1,ph0:0.1,v_th0:1,v_ph0:0}}
3135
+ R3 = Manifold(3, 'R3', start_index=1)
3136
+ cart = R3.chart('X Y Z'); X, Y, Z = cart[:]
3137
+ euclid_embedding = S2.diff_map(R3,
3138
+ {(polar, cart): [sin(th)*cos(ph),sin(th)*sin(ph),cos(th)]})
3139
+ graph3D_embedded_curves = Graphics()
3140
+ for key in dict_params:
3141
+ sol = c.solve(solution_key='sol-'+key,
3142
+ parameters_values=dict_params[key])
3143
+ interp = c.interpolate(solution_key='sol-'+key,
3144
+ interpolation_key='interp-'+key)
3145
+ graph3D_embedded_curves += c.plot_integrated(interpolation_key='interp-'+key,
3146
+ mapping=euclid_embedding, thickness=5,
3147
+ display_tangent=True, scale=0.4, width_tangent=0.5)
3148
+ graph3D_embedded_polar_coords = polar.plot(chart=cart,
3149
+ mapping=euclid_embedding,
3150
+ number_values=15, color='yellow')
3151
+ graph = graph3D_embedded_curves+graph3D_embedded_polar_coords
3152
+ sphinx_plot(graph)
3153
+
3154
+ Finally, one may plot a general autoparallel curve with respect to
3155
+ `\nabla` that is neither a line of latitude or longitude.
3156
+ The vectors tangent to such a curve make an angle different from 0
3157
+ or `\pi/2` with the lines of latitude and longitude.
3158
+ Then, compute a curve such that both components of its initial
3159
+ tangent vectors are nonzero::
3160
+
3161
+ sage: # needs scipy
3162
+ sage: sol = c.solve(solution_key='sol-angle',
3163
+ ....: parameters_values={tmin:0,tmax:2,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:8})
3164
+ sage: interp = c.interpolate(solution_key='sol-angle',
3165
+ ....: interpolation_key='interp-angle')
3166
+
3167
+ Plot the resulting curve in the Mercator plane.
3168
+ This generates a straight line, as expected::
3169
+
3170
+ sage: c.plot_integrated(interpolation_key='interp-angle', # needs scipy sage.plot
3171
+ ....: chart=mercator, thickness=1, display_tangent=True,
3172
+ ....: scale=0.2, width_tangent=0.2)
3173
+ Graphics object consisting of 11 graphics primitives
3174
+
3175
+ .. PLOT::
3176
+
3177
+ S2 = Manifold(2, 'S^2', start_index=1)
3178
+ polar = S2.chart('th ph'); th, ph = polar[:]
3179
+ epolar = polar.frame()
3180
+ ch_basis = S2.automorphism_field()
3181
+ ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th)
3182
+ epolar_ON = epolar.new_frame(ch_basis, 'epolar_ON')
3183
+ nab = S2.affine_connection('nab')
3184
+ _ = nab.set_coef(frame=epolar_ON)
3185
+ t,tmin,tmax,th0,ph0,v_th0,v_ph0 = var('t tmin tmax th0 ph0 v_th0 v_ph0')
3186
+ p = S2.point((th0, ph0), name='p')
3187
+ Tp = S2.tangent_space(p)
3188
+ v = Tp((v_th0, v_ph0), basis=epolar_ON.at(p))
3189
+ c = S2.integrated_autoparallel_curve(nab, (t, tmin, tmax), v,
3190
+ chart=polar, name='c')
3191
+ mercator = S2.chart(r'xi:(-oo,oo):\xi ze:(0,2*pi):\zeta')
3192
+ xi, ze = mercator[:]
3193
+ trans_map = polar.transition_map(mercator, (log(tan(th/2)), ph))
3194
+ identity = S2.identity_map()
3195
+ _ = identity.coord_functions(polar, mercator)
3196
+ sol = c.solve(solution_key='sol-angle',
3197
+ parameters_values={tmin:0,tmax:2,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:8})
3198
+ interp = c.interpolate(solution_key='sol-angle',
3199
+ interpolation_key='interp-angle')
3200
+ graph2D_mercator_angle_curve=c.plot_integrated(
3201
+ interpolation_key='interp-angle',
3202
+ chart=mercator, thickness=1, display_tangent=True,
3203
+ scale=0.2, width_tangent=0.2)
3204
+ sphinx_plot(graph2D_mercator_angle_curve)
3205
+
3206
+ One may eventually plot such a curve on `\mathbb{S}^{2}`::
3207
+
3208
+ sage: # needs scipy sage.plot
3209
+ sage: graph3D_embedded_angle_curve=c.plot_integrated(interpolation_key='interp-angle',
3210
+ ....: mapping=euclid_embedding, thickness=5,
3211
+ ....: display_tangent=True, scale=0.1, width_tangent=0.5)
3212
+ sage: graph3D_embedded_angle_curve + graph3D_embedded_polar_coords
3213
+ Graphics3d Object
3214
+
3215
+ .. PLOT::
3216
+
3217
+ S2 = Manifold(2, 'S^2', start_index=1)
3218
+ polar = S2.chart('th ph'); th, ph = polar[:]
3219
+ epolar = polar.frame()
3220
+ ch_basis = S2.automorphism_field()
3221
+ ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th)
3222
+ epolar_ON = epolar.new_frame(ch_basis, 'epolar_ON')
3223
+ nab = S2.affine_connection('nab')
3224
+ _ = nab.set_coef(frame=epolar_ON)
3225
+ t,tmin,tmax,th0,ph0,v_th0,v_ph0 = var('t tmin tmax th0 ph0 v_th0 v_ph0')
3226
+ p = S2.point((th0, ph0), name='p')
3227
+ Tp = S2.tangent_space(p)
3228
+ v = Tp((v_th0, v_ph0), basis=epolar_ON.at(p))
3229
+ c = S2.integrated_autoparallel_curve(nab, (t, tmin, tmax), v,
3230
+ chart=polar, name='c')
3231
+ R3 = Manifold(3, 'R3', start_index=1)
3232
+ cart = R3.chart('X Y Z')
3233
+ euclid_embedding = S2.diff_map(R3,
3234
+ {(polar, cart):[sin(th)*cos(ph),sin(th)*sin(ph),cos(th)]})
3235
+ sol = c.solve(solution_key='sol-angle',
3236
+ parameters_values={tmin:0,tmax:2,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:8})
3237
+ interp = c.interpolate(solution_key='sol-angle',
3238
+ interpolation_key='interp-angle')
3239
+ graph3D_embedded_angle_curve = c.plot_integrated(interpolation_key='interp-angle',
3240
+ mapping=euclid_embedding, thickness=5, display_tangent=True,
3241
+ scale=0.1, width_tangent=0.5)
3242
+ graph3D_embedded_polar_coords = polar.plot(chart=cart,
3243
+ mapping=euclid_embedding, number_values=15, color='yellow')
3244
+ graph = graph3D_embedded_angle_curve + graph3D_embedded_polar_coords
3245
+ sphinx_plot(graph)
3246
+
3247
+ All the curves presented are loxodromes, and the differential system
3248
+ defining them (displayed above) may be solved analytically,
3249
+ providing the following expressions:
3250
+
3251
+ .. MATH::
3252
+
3253
+ \begin{aligned}
3254
+ \theta(t) &= \theta_{0} + \dot{\theta}_{0} (t - t_{0}), \\
3255
+ \phi(t) &= \phi_{0} - \frac{1}{\tan \alpha} \left(
3256
+ \ln \tan \frac{\theta_{0} + \dot{\theta}_{0} (t - t_{0})}{2} -
3257
+ \ln \tan \frac{\theta_{0}}{2} \right),
3258
+ \end{aligned}
3259
+
3260
+ where `\alpha` is the angle between the curve and any latitude
3261
+ line it crosses; then, one finds
3262
+ `\tan \alpha = - \dot{\theta}_{0} / (\dot{\phi}_{0} \sin \theta_{0})`
3263
+ (then `\tan \alpha \leq 0` when the initial tangent vector
3264
+ points towards the southeast).
3265
+
3266
+ In order to use these expressions to compare with the result
3267
+ provided by the numerical integration, remember that the components
3268
+ ``(v_th0, v_ph0)`` of the initial
3269
+ tangent vector ``v`` refer to the basis
3270
+ ``epolar_ON`` `= (\hat{e}_{\theta}, \hat{e}_{\phi})` and not the
3271
+ coordinate basis
3272
+ ``epolar`` `= (e_{\theta}, e_{\phi})`.
3273
+ Therefore, the following relations hold:
3274
+ ``v_ph0`` `= \dot{\phi}_{0} \sin \theta_{0}` (and not merely
3275
+ `\dot{\phi}_{0}`), while ``v_th0`` clearly is `\dot{\theta}_{0}`.
3276
+
3277
+ With this in mind, plot an analytical curve to compare with a
3278
+ numerical solution::
3279
+
3280
+ sage: # needs scipy sage.plot
3281
+ sage: graph2D_mercator_angle_curve = c.plot_integrated(interpolation_key='interp-angle',
3282
+ ....: chart=mercator, thickness=1)
3283
+ sage: expr_ph = ph0+v_ph0/v_th0*(ln(tan((v_th0*t+th0)/2))-ln(tan(th0/2)))
3284
+ sage: c_loxo = S2.curve({polar:[th0+v_th0*t, expr_ph]}, (t,0,2),
3285
+ ....: name='c_loxo')
3286
+
3287
+ Ask for the expression of the loxodrome in terms of the Mercator
3288
+ chart in order to add it to its dictionary of expressions.
3289
+ It is a particularly long expression, and there is no particular
3290
+ need to display it, which is why it may simply be affected to an
3291
+ arbitrary variable ``expr_mercator``, which will never be used
3292
+ again.
3293
+ But adding the expression to the dictionary is required to plot the
3294
+ curve with respect to the Mercator chart::
3295
+
3296
+ sage: expr_mercator = c_loxo.expression(chart2=mercator) # needs scipy sage.plot
3297
+
3298
+ Plot the curves (for clarity, set a 2 degrees shift in the initial
3299
+ value of `\theta_{0}` so that the curves do not overlap)::
3300
+
3301
+ sage: # needs scipy sage.plot
3302
+ sage: graph2D_mercator_loxo = c_loxo.plot(chart=mercator,
3303
+ ....: parameters={th0:pi/4+2*pi/180, ph0:0.1, v_th0:1, v_ph0:8},
3304
+ ....: thickness=1, color='blue')
3305
+ sage: graph2D_mercator_angle_curve + graph2D_mercator_loxo
3306
+ Graphics object consisting of 2 graphics primitives
3307
+
3308
+ .. PLOT::
3309
+
3310
+ S2 = Manifold(2, 'S^2', start_index=1)
3311
+ polar = S2.chart('th ph'); th, ph = polar[:]
3312
+ epolar = polar.frame()
3313
+ ch_basis = S2.automorphism_field()
3314
+ ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th)
3315
+ epolar_ON = epolar.new_frame(ch_basis, 'epolar_ON')
3316
+ nab = S2.affine_connection('nab')
3317
+ _ = nab.set_coef(frame=epolar_ON)
3318
+ t, tmin, tmax, th0, ph0 = var('t tmin tmax th0 ph0')
3319
+ v_th0, v_ph0, alpha = var('v_th0 v_ph0 alpha')
3320
+ p = S2.point((th0, ph0), name='p')
3321
+ Tp = S2.tangent_space(p)
3322
+ v = Tp((v_th0, v_ph0), basis=epolar_ON.at(p))
3323
+ c = S2.integrated_autoparallel_curve(nab, (t, tmin, tmax), v,
3324
+ chart=polar, name='c')
3325
+ mercator = S2.chart(r'xi:(-oo,oo):\xi ze:(0,2*pi):\zeta')
3326
+ xi, ze = mercator[:]
3327
+ trans_map = polar.transition_map(mercator, (log(tan(th/2)), ph))
3328
+ identity = S2.identity_map()
3329
+ _ = identity.coord_functions(polar, mercator)
3330
+ sol = c.solve(solution_key='sol-angle',
3331
+ parameters_values={tmin:0,tmax:2,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:8})
3332
+ interp = c.interpolate(solution_key='sol-angle',
3333
+ interpolation_key='interp-angle')
3334
+ graph2D_mercator_angle_curve = c.plot_integrated(interpolation_key='interp-angle',
3335
+ chart=mercator, thickness=1)
3336
+ expr_ph = ph0+v_ph0/v_th0*(ln(tan((v_th0*t+th0)/2))-ln(tan(th0/2)))
3337
+ c_loxo = S2.curve({polar: [th0+v_th0*t, expr_ph]}, (t,0,2), name='c_loxo')
3338
+ expr = c_loxo.expression(chart2=mercator)
3339
+ graph2D_mercator_loxo = c_loxo.plot(chart=mercator,
3340
+ parameters={th0:pi/4+2*pi/180, ph0:0.1, v_th0:1, v_ph0:8},
3341
+ thickness=1, color='blue')
3342
+ sphinx_plot(graph2D_mercator_angle_curve+graph2D_mercator_loxo)
3343
+
3344
+ Both curves do have the same aspect.
3345
+ One may eventually compare these curves on `\mathbb{S}^{2}`::
3346
+
3347
+ sage: # needs scipy sage.plot
3348
+ sage: graph3D_embedded_angle_curve=c.plot_integrated(interpolation_key='interp-angle',
3349
+ ....: mapping=euclid_embedding, thickness=3)
3350
+ sage: graph3D_embedded_loxo = c_loxo.plot(mapping=euclid_embedding,
3351
+ ....: parameters={th0:pi/4+2*pi/180, ph0:0.1, v_th0:1, v_ph0:8},
3352
+ ....: thickness=3, color = 'blue')
3353
+ sage: (graph3D_embedded_angle_curve + graph3D_embedded_loxo
3354
+ ....: + graph3D_embedded_polar_coords)
3355
+ Graphics3d Object
3356
+
3357
+ .. PLOT::
3358
+
3359
+ S2 = Manifold(2, 'S^2', start_index=1)
3360
+ polar = S2.chart('th ph'); th, ph = polar[:]
3361
+ epolar = polar.frame()
3362
+ ch_basis = S2.automorphism_field()
3363
+ ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th)
3364
+ epolar_ON = epolar.new_frame(ch_basis, 'epolar_ON')
3365
+ nab = S2.affine_connection('nab')
3366
+ _ = nab.set_coef(frame=epolar_ON)
3367
+ t, tmin, tmax, th0, ph0 = var('t tmin tmax th0 ph0')
3368
+ v_th0, v_ph0, alpha = var('v_th0 v_ph0 alpha')
3369
+ p = S2.point((th0, ph0), name='p')
3370
+ Tp = S2.tangent_space(p)
3371
+ v = Tp((v_th0, v_ph0), basis=epolar_ON.at(p))
3372
+ c = S2.integrated_autoparallel_curve(nab, (t, tmin, tmax), v,
3373
+ chart=polar, name='c')
3374
+ R3 = Manifold(3, 'R3', start_index=1)
3375
+ cart = R3.chart('X Y Z')
3376
+ euclid_embedding = S2.diff_map(R3,
3377
+ {(polar, cart):[sin(th)*cos(ph),sin(th)*sin(ph),cos(th)]})
3378
+ sol = c.solve(solution_key='sol-angle',
3379
+ parameters_values={tmin:0,tmax:2,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:8})
3380
+ interp = c.interpolate(solution_key='sol-angle',
3381
+ interpolation_key='interp-angle')
3382
+ graph3D_embedded_angle_curve = c.plot_integrated(interpolation_key='interp-angle',
3383
+ mapping=euclid_embedding, thickness=3)
3384
+ expr_ph = ph0+v_ph0/v_th0*(ln(tan((v_th0*t+th0)/2))-ln(tan(th0/2)))
3385
+ c_loxo = S2.curve({polar: [th0+v_th0*t, expr_ph]}, (t,0,2), name='c_loxo')
3386
+ graph3D_embedded_loxo = c_loxo.plot(mapping=euclid_embedding,
3387
+ parameters={th0:pi/4+2*pi/180, ph0:0.1, v_th0:1, v_ph0:8},
3388
+ thickness=3, color='blue')
3389
+ graph3D_embedded_polar_coords = polar.plot(chart=cart,
3390
+ mapping=euclid_embedding, number_values=15, color='yellow')
3391
+ graph = graph3D_embedded_angle_curve + graph3D_embedded_loxo
3392
+ graph += graph3D_embedded_polar_coords
3393
+ sphinx_plot(graph)
3394
+ """
3395
+
3396
+ def __init__(self, parent, affine_connection, curve_parameter,
3397
+ initial_tangent_vector, chart=None, name=None,
3398
+ latex_name=None, verbose=False, across_charts=False):
3399
+ r"""
3400
+ Construct an autoparallel curve with respect to the given affine
3401
+ connection with the given initial tangent vector.
3402
+
3403
+ TESTS::
3404
+
3405
+ sage: M = Manifold(3, 'M')
3406
+ sage: X.<x1,x2,x3> = M.chart()
3407
+ sage: [t, A, B] = var('t A B')
3408
+ sage: nab = M.affine_connection('nabla', r'\nabla')
3409
+ sage: nab[X.frame(),0,0,1],nab[X.frame(),2,1,2]=A*x1^2,B*x2*x3
3410
+ sage: p = M.point((0,0,0), name='p')
3411
+ sage: Tp = M.tangent_space(p)
3412
+ sage: v = Tp((1,0,1))
3413
+ sage: c = M.integrated_autoparallel_curve(nab, (t, 0, 5), v,
3414
+ ....: name='c') ; c
3415
+ Integrated autoparallel curve c in the 3-dimensional
3416
+ differentiable manifold M
3417
+ sage: TestSuite(c).run()
3418
+ """
3419
+
3420
+ # setting the chart to gain access to the coordinate functions
3421
+ if chart is None:
3422
+ chart = parent.codomain().default_chart()
3423
+
3424
+ velocities = chart.symbolic_velocities()
3425
+
3426
+ dim = parent.codomain().dim()
3427
+ i0 = parent.codomain().start_index()
3428
+
3429
+ self._across_charts = across_charts
3430
+ if not across_charts:
3431
+
3432
+ equations_rhs = []
3433
+
3434
+ gamma = affine_connection.coef(frame=chart.frame())
3435
+
3436
+ for rho in range(dim):
3437
+ rhs = 0
3438
+ for mu in range(dim):
3439
+ for nu in range(dim):
3440
+ vMUvNU = velocities[mu] * velocities[nu]
3441
+ gammaRHO_mu_nu = gamma[[rho+i0, mu+i0, nu+i0]].expr(chart=chart)
3442
+ # line above is the expression of the scalar
3443
+ # field 'gamma[[rho+i0, mu+i0, nu+i0]]' in terms
3444
+ # of 'chart' (here, in any point of the manifold,
3445
+ # the scalar field 'gamma[[rho+i0, mu+i0, nu+i0]]'
3446
+ # provides the coefficient [rho+i0, mu+i0, nu+i0]
3447
+ # of the affine connection with respect to frame
3448
+ # 'chart.frame()')
3449
+ rhs -= gammaRHO_mu_nu * vMUvNU
3450
+ # 'vMUvNU' and 'gammaRHO_mu_nu' only used for the
3451
+ # line above to be shorter
3452
+ equations_rhs += [rhs.simplify_full()]
3453
+ else:
3454
+ equations_rhs = {} # Dict of all equation in all top_charts
3455
+ for chart in parent.codomain().top_charts():
3456
+ velocities = chart.symbolic_velocities()
3457
+ equations_rhs_chart = [] # Equation in one chart
3458
+ gamma = affine_connection.coef(frame=chart.frame())
3459
+ for rho in range(dim):
3460
+ rhs = 0
3461
+ for mu in range(dim):
3462
+ for nu in range(dim):
3463
+ vMUvNU = velocities[mu] * velocities[nu]
3464
+ gammaRHO_mu_nu = gamma[
3465
+ [rho + i0, mu + i0, nu + i0]].expr(chart=chart)
3466
+ rhs -= gammaRHO_mu_nu * vMUvNU
3467
+ equations_rhs_chart += [rhs.simplify_full()]
3468
+ equations_rhs[chart] = equations_rhs_chart
3469
+
3470
+ IntegratedCurve.__init__(self, parent, equations_rhs,
3471
+ velocities, curve_parameter,
3472
+ initial_tangent_vector, chart=chart,
3473
+ name=name, latex_name=latex_name,
3474
+ verbose=verbose, across_charts=across_charts)
3475
+
3476
+ self._affine_connection = affine_connection
3477
+
3478
+ def _repr_(self):
3479
+ r"""
3480
+ Return a string representation of ``self``.
3481
+
3482
+ TESTS::
3483
+
3484
+ sage: M = Manifold(3, 'M')
3485
+ sage: X.<x1,x2,x3> = M.chart()
3486
+ sage: [t, A, B] = var('t A B')
3487
+ sage: nab = M.affine_connection('nabla', r'\nabla')
3488
+ sage: nab[X.frame(),0,0,1],nab[X.frame(),2,1,2]=A*x1^2,B*x2*x3
3489
+ sage: p = M.point((0,0,0), name='p')
3490
+ sage: Tp = M.tangent_space(p)
3491
+ sage: v = Tp((1,0,1))
3492
+ sage: c = M.integrated_autoparallel_curve(nab, (t,0,5), v); c
3493
+ Integrated autoparallel curve in the 3-dimensional
3494
+ differentiable manifold M
3495
+ sage: c = M.integrated_autoparallel_curve(nab, (t, 0, 5), v,
3496
+ ....: name='c') ; c
3497
+ Integrated autoparallel curve c in the 3-dimensional
3498
+ differentiable manifold M
3499
+ """
3500
+
3501
+ description = "Integrated autoparallel curve "
3502
+ if self._name is not None:
3503
+ description += self._name + " "
3504
+ description += "in the {}".format(self._codomain)
3505
+ return description
3506
+
3507
+ def __reduce__(self):
3508
+ r"""
3509
+ Reduction function for the pickle protocole.
3510
+
3511
+ TESTS::
3512
+
3513
+ sage: M = Manifold(3, 'M')
3514
+ sage: X.<x1,x2,x3> = M.chart()
3515
+ sage: [t, A, B] = var('t A B')
3516
+ sage: nab = M.affine_connection('nabla', r'\nabla')
3517
+ sage: nab[X.frame(),0,0,1],nab[X.frame(),2,1,2]=A*x1^2,B*x2*x3
3518
+ sage: p = M.point((0,0,0), name='p')
3519
+ sage: Tp = M.tangent_space(p)
3520
+ sage: v = Tp((1,0,1))
3521
+ sage: c = M.integrated_autoparallel_curve(nab, (t, 0, 5), v,
3522
+ ....: name='c')
3523
+ sage: c.__reduce__()
3524
+ (<class 'sage.manifolds.differentiable.manifold_homset.IntegratedAutoparallelCurveSet_with_category.element_class'>,
3525
+ (Set of Morphisms from Real interval (0, 5) to
3526
+ 3-dimensional differentiable manifold M in Category of homsets of
3527
+ topological spaces which actually are integrated autoparallel
3528
+ curves with respect to a certain affine connection,
3529
+ Affine connection nabla on the 3-dimensional
3530
+ differentiable manifold M,
3531
+ t,
3532
+ Tangent vector at Point p on the 3-dimensional
3533
+ differentiable manifold M,
3534
+ Chart (M, (x1, x2, x3)),
3535
+ 'c',
3536
+ 'c',
3537
+ False,
3538
+ False))
3539
+
3540
+ Test of pickling::
3541
+
3542
+ sage: loads(dumps(c))
3543
+ Integrated autoparallel curve c in the 3-dimensional differentiable manifold M
3544
+ """
3545
+
3546
+ return (type(self), (self.parent(), self._affine_connection,
3547
+ self._curve_parameter, self._initial_tangent_vector,
3548
+ self._chart, self._name, self._latex_name, False,
3549
+ self._across_charts))
3550
+
3551
+ def system(self, verbose=False):
3552
+ r"""
3553
+ Provide a detailed description of the system defining the
3554
+ autoparallel curve and returns the system defining it: chart,
3555
+ equations and initial conditions.
3556
+
3557
+ INPUT:
3558
+
3559
+ - ``verbose`` -- boolean (default: ``False``); prints a detailed
3560
+ description of the curve
3561
+
3562
+ OUTPUT:
3563
+
3564
+ - list containing the
3565
+
3566
+ * the equations
3567
+ * the initial conditions
3568
+ * the chart
3569
+
3570
+ EXAMPLES:
3571
+
3572
+ System defining an autoparallel curve::
3573
+
3574
+ sage: M = Manifold(3, 'M')
3575
+ sage: X.<x1,x2,x3> = M.chart()
3576
+ sage: [t, A, B] = var('t A B')
3577
+ sage: nab = M.affine_connection('nabla', r'\nabla')
3578
+ sage: nab[X.frame(),0,0,1],nab[X.frame(),2,1,2]=A*x1^2,B*x2*x3
3579
+ sage: p = M.point((0,0,0), name='p')
3580
+ sage: Tp = M.tangent_space(p)
3581
+ sage: v = Tp((1,0,1))
3582
+ sage: c = M.integrated_autoparallel_curve(nab, (t, 0, 5), v)
3583
+ sage: sys = c.system(verbose=True)
3584
+ Autoparallel curve in the 3-dimensional differentiable
3585
+ manifold M equipped with Affine connection nabla on the
3586
+ 3-dimensional differentiable manifold M, and integrated
3587
+ over the Real interval (0, 5) as a solution to the
3588
+ following equations, written with respect to
3589
+ Chart (M, (x1, x2, x3)):
3590
+ <BLANKLINE>
3591
+ Initial point: Point p on the 3-dimensional differentiable
3592
+ manifold M with coordinates [0, 0, 0] with respect to
3593
+ Chart (M, (x1, x2, x3))
3594
+ Initial tangent vector: Tangent vector at Point p on the
3595
+ 3-dimensional differentiable manifold M with
3596
+ components [1, 0, 1] with respect to Chart (M, (x1, x2, x3))
3597
+ <BLANKLINE>
3598
+ d(x1)/dt = Dx1
3599
+ d(x2)/dt = Dx2
3600
+ d(x3)/dt = Dx3
3601
+ d(Dx1)/dt = -A*Dx1*Dx2*x1^2
3602
+ d(Dx2)/dt = 0
3603
+ d(Dx3)/dt = -B*Dx2*Dx3*x2*x3
3604
+ <BLANKLINE>
3605
+ sage: sys_bis = c.system()
3606
+ sage: sys_bis == sys
3607
+ True
3608
+ """
3609
+
3610
+ v0 = self._initial_tangent_vector
3611
+ chart = self._chart
3612
+
3613
+ if verbose:
3614
+ initial_tgt_space = v0.parent()
3615
+ initial_pt = initial_tgt_space.base_point() # retrieves
3616
+ # the initial point as the base point of the tangent space
3617
+ # to which initial tangent vector belongs
3618
+ initial_pt_coords = list(initial_pt.coordinates(chart))
3619
+ # previous line converts to list since would otherwise be a
3620
+ # tuple ; will raise error if coordinates in chart are not
3621
+ # known
3622
+
3623
+ initial_coord_basis = chart.frame().at(initial_pt)
3624
+ initial_tgt_vec_comps = v0[initial_coord_basis,:] # will
3625
+ # raise error if components in coordinate basis are not
3626
+ # known
3627
+
3628
+ description = "Autoparallel curve "
3629
+ if self._name is not None:
3630
+ description += self._name + " "
3631
+ description += "in the {} ".format(self.codomain())
3632
+ description += "equipped with "
3633
+ description += "{}, ".format(self._affine_connection)
3634
+ description += "and integrated over the "
3635
+ description += "{} ".format(self.domain())
3636
+ description += "as a solution to the following equations, "
3637
+ description += "written with respect to "
3638
+ description += "{}:\n\n".format(chart)
3639
+
3640
+ description += "Initial point: {} ".format(initial_pt)
3641
+ description += "with coordinates "
3642
+ description += "{} ".format(initial_pt_coords)
3643
+ description += "with respect to {}\n".format(chart)
3644
+
3645
+ description += "Initial tangent vector: {} ".format(v0)
3646
+ description += "with components "
3647
+ description += "{}".format(initial_tgt_vec_comps)
3648
+ description += " with respect to {}\n\n".format(chart)
3649
+
3650
+ for coord_func,velocity in zip(chart[:],self._velocities):
3651
+ description += "d({})/d{} = {}\n".format(coord_func,
3652
+ self._curve_parameter,
3653
+ velocity)
3654
+
3655
+ for velocity,eqn in zip(self._velocities,self._equations_rhs):
3656
+ description += "d({})/d{} = {}\n".format(velocity,
3657
+ self._curve_parameter,
3658
+ eqn)
3659
+
3660
+ print(description)
3661
+
3662
+ return [self._equations_rhs, v0, chart]
3663
+
3664
+
3665
+ class IntegratedGeodesic(IntegratedAutoparallelCurve):
3666
+ r"""
3667
+ Geodesic on the manifold with respect to a given metric.
3668
+
3669
+ INPUT:
3670
+
3671
+ - ``parent`` --
3672
+ :class:`~sage.manifolds.differentiable.manifold_homset.IntegratedGeodesicSet`
3673
+ the set of curves `\mathrm{Hom_{geodesic}}(I, M)` to which the
3674
+ curve belongs
3675
+ - ``metric`` --
3676
+ :class:`~sage.manifolds.differentiable.metric.PseudoRiemannianMetric`
3677
+ metric with respect to which the curve is a geodesic
3678
+ - ``curve_parameter`` -- symbolic expression to be used as the
3679
+ parameter of the curve (the equations defining an instance of
3680
+ IntegratedGeodesic are such that ``t`` will actually be an affine
3681
+ parameter of the curve);
3682
+ - ``initial_tangent_vector`` --
3683
+ :class:`~sage.manifolds.differentiable.tangent_vector.TangentVector`
3684
+ initial tangent vector of the curve
3685
+ - ``chart`` -- (default: ``None``) chart on the manifold in terms of
3686
+ which the equations are expressed; if ``None`` the default chart
3687
+ of the manifold is assumed
3688
+ - ``name`` -- (default: ``None``) string; symbol given to the curve
3689
+ - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote
3690
+ the curve; if none is provided, ``name`` will be used
3691
+
3692
+ EXAMPLES:
3693
+
3694
+ Geodesics of the unit 2-sphere `\mathbb{S}^{2}`.
3695
+ Start with declaring the standard polar coordinates
3696
+ `(\theta, \phi)` on `\mathbb{S}^{2}` and the
3697
+ corresponding coordinate frame `(e_{\theta}, e_{\phi})`::
3698
+
3699
+ sage: S2 = Manifold(2, 'S^2', structure='Riemannian', start_index=1)
3700
+ sage: polar.<th,ph>=S2.chart('th ph')
3701
+ sage: epolar = polar.frame()
3702
+
3703
+ Set the standard round metric::
3704
+
3705
+ sage: g = S2.metric()
3706
+ sage: g[1,1], g[2,2] = 1, (sin(th))^2
3707
+
3708
+ Set generic initial conditions for the geodesics to compute::
3709
+
3710
+ sage: [th0, ph0, v_th0, v_ph0] = var('th0 ph0 v_th0 v_ph0')
3711
+ sage: p = S2.point((th0, ph0), name='p')
3712
+ sage: Tp = S2.tangent_space(p)
3713
+ sage: v = Tp((v_th0, v_ph0), basis=epolar.at(p))
3714
+
3715
+ Declare the corresponding integrated geodesic and display the
3716
+ differential system it satisfies::
3717
+
3718
+ sage: [t, tmin, tmax] = var('t tmin tmax')
3719
+ sage: c = S2.integrated_geodesic(g, (t, tmin, tmax), v,
3720
+ ....: chart=polar, name='c')
3721
+ sage: sys = c.system(verbose=True)
3722
+ Geodesic c in the 2-dimensional Riemannian manifold S^2
3723
+ equipped with Riemannian metric g on the 2-dimensional
3724
+ Riemannian manifold S^2, and integrated over the Real
3725
+ interval (tmin, tmax) as a solution to the following geodesic
3726
+ equations, written with respect to Chart (S^2, (th, ph)):
3727
+ <BLANKLINE>
3728
+ Initial point: Point p on the 2-dimensional Riemannian
3729
+ manifold S^2 with coordinates [th0, ph0] with respect to
3730
+ Chart (S^2, (th, ph))
3731
+ Initial tangent vector: Tangent vector at Point p on the
3732
+ 2-dimensional Riemannian manifold S^2 with
3733
+ components [v_th0, v_ph0] with respect to Chart (S^2, (th, ph))
3734
+ <BLANKLINE>
3735
+ d(th)/dt = Dth
3736
+ d(ph)/dt = Dph
3737
+ d(Dth)/dt = Dph^2*cos(th)*sin(th)
3738
+ d(Dph)/dt = -2*Dph*Dth*cos(th)/sin(th)
3739
+ <BLANKLINE>
3740
+
3741
+ Set a dictionary providing the parameter range and the initial
3742
+ conditions for various geodesics::
3743
+
3744
+ sage: dict_params={'equat':{tmin:0,tmax:3,th0:pi/2,ph0:0.1,v_th0:0,v_ph0:1},
3745
+ ....: 'longi':{tmin:0,tmax:3,th0:0.1,ph0:0.1,v_th0:1,v_ph0:0},
3746
+ ....: 'angle':{tmin:0,tmax:3,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:1}}
3747
+
3748
+ Use `\mathbb{R}^{3}` as the codomain of the standard map
3749
+ embedding `(\mathbb{S}^{2}, (\theta, \phi))` in the
3750
+ 3-dimensional Euclidean space::
3751
+
3752
+ sage: R3 = Manifold(3, 'R3', start_index=1)
3753
+ sage: cart.<X,Y,Z> = R3.chart()
3754
+ sage: euclid_embedding = S2.diff_map(R3,
3755
+ ....: {(polar, cart):[sin(th)*cos(ph),sin(th)*sin(ph),cos(th)]})
3756
+
3757
+ Solve, interpolate and prepare the plot for the solutions
3758
+ corresponding to the three initial conditions previously set::
3759
+
3760
+ sage: # needs scipy sage.plot
3761
+ sage: graph3D_embedded_geods = Graphics()
3762
+ sage: for key in dict_params:
3763
+ ....: sol = c.solve(solution_key='sol-'+key,
3764
+ ....: parameters_values=dict_params[key])
3765
+ ....: interp = c.interpolate(solution_key='sol-'+key,
3766
+ ....: interpolation_key='interp-'+key)
3767
+ ....: graph3D_embedded_geods += c.plot_integrated(interpolation_key='interp-'+key,
3768
+ ....: mapping=euclid_embedding, thickness=5,
3769
+ ....: display_tangent=True, scale=0.3,
3770
+ ....: width_tangent=0.5)
3771
+
3772
+ Plot the resulting geodesics on the grid of polar coordinates lines
3773
+ on `\mathbb{S}^{2}` and check that these are great circles::
3774
+
3775
+ sage: # needs scipy sage.plot
3776
+ sage: graph3D_embedded_polar_coords = polar.plot(chart=cart,
3777
+ ....: mapping=euclid_embedding,
3778
+ ....: number_values=15, color='yellow')
3779
+ sage: graph3D_embedded_geods + graph3D_embedded_polar_coords
3780
+ Graphics3d Object
3781
+
3782
+ .. PLOT::
3783
+
3784
+ S2 = Manifold(2, 'S^2', structure='Riemannian', start_index=1)
3785
+ polar = S2.chart('th ph'); th, ph = polar[:]
3786
+ epolar = polar.frame()
3787
+ g = S2.metric()
3788
+ g[1,1], g[2,2] = 1, (sin(th))**2
3789
+ t,tmin,tmax,th0,ph0,v_th0,v_ph0 = var('t tmin tmax th0 ph0 v_th0 v_ph0')
3790
+ p = S2.point((th0, ph0), name='p')
3791
+ Tp = S2.tangent_space(p)
3792
+ v = Tp((v_th0, v_ph0), basis=epolar.at(p))
3793
+ c = S2.integrated_geodesic(g, (t, tmin, tmax), v, chart=polar,
3794
+ name='c')
3795
+ dict_params={'equat':{tmin:0,tmax:3,th0:pi/2,ph0:0.1,v_th0:0,v_ph0:1},
3796
+ 'longi':{tmin:0,tmax:3,th0:0.1,ph0:0.1,v_th0:1,v_ph0:0},
3797
+ 'angle':{tmin:0,tmax:3,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:1}}
3798
+ R3 = Manifold(3, 'R3', start_index=1)
3799
+ cart = R3.chart('X Y Z')
3800
+ euclid_embedding = S2.diff_map(R3,
3801
+ {(polar, cart): [sin(th)*cos(ph),sin(th)*sin(ph),cos(th)]})
3802
+ graph3D_embedded_geods = Graphics()
3803
+ for key in dict_params:
3804
+ sol = c.solve(solution_key='sol-'+key,
3805
+ parameters_values=dict_params[key])
3806
+ interp = c.interpolate(solution_key='sol-'+key,
3807
+ interpolation_key='interp-'+key)
3808
+ graph3D_embedded_geods += c.plot_integrated(interpolation_key='interp-'+key,
3809
+ mapping=euclid_embedding, thickness=5,
3810
+ display_tangent=True, scale=0.3,
3811
+ width_tangent=0.5)
3812
+ graph3D_embedded_polar_coords = polar.plot(chart=cart,
3813
+ mapping=euclid_embedding,
3814
+ number_values=15, color='yellow')
3815
+ graph = graph3D_embedded_geods + graph3D_embedded_polar_coords
3816
+ sphinx_plot(graph)
3817
+ """
3818
+
3819
+ def __init__(self, parent, metric, curve_parameter,
3820
+ initial_tangent_vector, chart=None, name=None,
3821
+ latex_name=None, verbose=False, across_charts=False):
3822
+ r"""
3823
+ Construct a geodesic curve with respect to the given metric with the
3824
+ given initial tangent vector.
3825
+
3826
+ TESTS::
3827
+
3828
+ sage: S2 = Manifold(2, 'S^2', structure='Riemannian')
3829
+ sage: X.<theta,phi> = S2.chart()
3830
+ sage: t, A = var('t A')
3831
+ sage: g = S2.metric()
3832
+ sage: g[0,0] = A
3833
+ sage: g[1,1] = A*sin(theta)^2
3834
+ sage: p = S2.point((pi/2,0), name='p')
3835
+ sage: Tp = S2.tangent_space(p)
3836
+ sage: v = Tp((1/sqrt(2),1/sqrt(2)))
3837
+ sage: c = S2.integrated_geodesic(g, (t,0,pi), v, name='c'); c
3838
+ Integrated geodesic c in the 2-dimensional Riemannian
3839
+ manifold S^2
3840
+ sage: TestSuite(c).run()
3841
+ """
3842
+
3843
+ affine_connection = metric.connection()
3844
+
3845
+ IntegratedAutoparallelCurve.__init__(self, parent,
3846
+ affine_connection, curve_parameter,
3847
+ initial_tangent_vector, chart=chart,
3848
+ name=name, latex_name=latex_name,
3849
+ verbose=verbose, across_charts=across_charts)
3850
+
3851
+ self._metric = metric
3852
+ self._across_charts = across_charts
3853
+
3854
+ def _repr_(self):
3855
+ r"""
3856
+ Return a string representation of ``self``.
3857
+
3858
+ TESTS::
3859
+
3860
+ sage: S2 = Manifold(2, 'S^2', structure='Riemannian')
3861
+ sage: X.<theta,phi> = S2.chart()
3862
+ sage: t, A = var('t A')
3863
+ sage: g = S2.metric()
3864
+ sage: g[0,0] = A
3865
+ sage: g[1,1] = A*sin(theta)^2
3866
+ sage: p = S2.point((pi/2,0), name='p')
3867
+ sage: Tp = S2.tangent_space(p)
3868
+ sage: v = Tp((1/sqrt(2),1/sqrt(2)))
3869
+ sage: c = S2.integrated_geodesic(g, (t, 0, pi), v) ; c
3870
+ Integrated geodesic in the 2-dimensional Riemannian
3871
+ manifold S^2
3872
+ sage: c = S2.integrated_geodesic(g, (t,0,pi), v, name='c'); c
3873
+ Integrated geodesic c in the 2-dimensional Riemannian
3874
+ manifold S^2
3875
+ """
3876
+
3877
+ description = "Integrated geodesic "
3878
+ if self._name is not None:
3879
+ description += self._name + " "
3880
+ description += "in the {}".format(self._codomain)
3881
+ return description
3882
+
3883
+ def __reduce__(self):
3884
+ r"""
3885
+ Reduction function for the pickle protocole.
3886
+
3887
+ TESTS::
3888
+
3889
+ sage: S2 = Manifold(2, 'S^2', structure='Riemannian')
3890
+ sage: X.<theta,phi> = S2.chart()
3891
+ sage: t, A = var('t A')
3892
+ sage: g = S2.metric()
3893
+ sage: g[0,0] = A
3894
+ sage: g[1,1] = A*sin(theta)^2
3895
+ sage: p = S2.point((pi/2,0), name='p')
3896
+ sage: Tp = S2.tangent_space(p)
3897
+ sage: v = Tp((1/sqrt(2),1/sqrt(2)))
3898
+ sage: c = S2.integrated_geodesic(g, (t, 0, pi), v, name='c')
3899
+ sage: c.__reduce__()
3900
+ (<...IntegratedGeodesicSet_with_category.element_class'>,
3901
+ (Set of Morphisms from Real interval (0, pi) to
3902
+ 2-dimensional Riemannian manifold S^2 in Category of homsets of
3903
+ topological spaces which actually are integrated geodesics with
3904
+ respect to a certain metric,
3905
+ Riemannian metric g on the 2-dimensional Riemannian
3906
+ manifold S^2,
3907
+ t,
3908
+ Tangent vector at Point p on the 2-dimensional
3909
+ Riemannian manifold S^2,
3910
+ Chart (S^2, (theta, phi)),
3911
+ 'c',
3912
+ 'c',
3913
+ False,
3914
+ False))
3915
+
3916
+ Test of pickling::
3917
+
3918
+ sage: loads(dumps(c))
3919
+ Integrated geodesic c in the 2-dimensional Riemannian manifold S^2
3920
+ """
3921
+
3922
+ return (type(self), (self.parent(), self._metric,
3923
+ self._curve_parameter, self._initial_tangent_vector,
3924
+ self._chart, self._name, self._latex_name, False,
3925
+ self._across_charts))
3926
+
3927
+ def system(self, verbose=False):
3928
+ r"""
3929
+ Return the system defining the geodesic: chart, equations and
3930
+ initial conditions.
3931
+
3932
+ INPUT:
3933
+
3934
+ - ``verbose`` -- boolean (default: ``False``); prints a detailed
3935
+ description of the curve
3936
+
3937
+ OUTPUT: list containing
3938
+
3939
+ * the equations
3940
+ * the initial equations
3941
+ * the chart
3942
+
3943
+ EXAMPLES:
3944
+
3945
+ System defining a geodesic::
3946
+
3947
+ sage: S2 = Manifold(2, 'S^2',structure='Riemannian')
3948
+ sage: X.<theta,phi> = S2.chart()
3949
+ sage: t, A = var('t A')
3950
+ sage: g = S2.metric()
3951
+ sage: g[0,0] = A
3952
+ sage: g[1,1] = A*sin(theta)^2
3953
+ sage: p = S2.point((pi/2,0), name='p')
3954
+ sage: Tp = S2.tangent_space(p)
3955
+ sage: v = Tp((1/sqrt(2),1/sqrt(2)))
3956
+ sage: c = S2.integrated_geodesic(g, (t, 0, pi), v, name='c')
3957
+ sage: sys = c.system(verbose=True)
3958
+ Geodesic c in the 2-dimensional Riemannian manifold S^2
3959
+ equipped with Riemannian metric g on the 2-dimensional
3960
+ Riemannian manifold S^2, and integrated over the Real
3961
+ interval (0, pi) as a solution to the following geodesic
3962
+ equations, written with respect to Chart (S^2, (theta, phi)):
3963
+ <BLANKLINE>
3964
+ Initial point: Point p on the 2-dimensional Riemannian
3965
+ manifold S^2 with coordinates [1/2*pi, 0] with respect to
3966
+ Chart (S^2, (theta, phi))
3967
+ Initial tangent vector: Tangent vector at Point p on the
3968
+ 2-dimensional Riemannian manifold S^2 with
3969
+ components [1/2*sqrt(2), 1/2*sqrt(2)] with respect to
3970
+ Chart (S^2, (theta, phi))
3971
+ <BLANKLINE>
3972
+ d(theta)/dt = Dtheta
3973
+ d(phi)/dt = Dphi
3974
+ d(Dtheta)/dt = Dphi^2*cos(theta)*sin(theta)
3975
+ d(Dphi)/dt = -2*Dphi*Dtheta*cos(theta)/sin(theta)
3976
+ <BLANKLINE>
3977
+ sage: sys_bis = c.system()
3978
+ sage: sys_bis == sys
3979
+ True
3980
+ """
3981
+
3982
+ v0 = self._initial_tangent_vector
3983
+ chart = self._chart
3984
+
3985
+ if verbose:
3986
+ initial_tgt_space = v0.parent()
3987
+ initial_pt = initial_tgt_space.base_point()
3988
+ # retrieves the initial point as the base point of the
3989
+ # tangent space to which initial tangent vector belongs
3990
+
3991
+ initial_pt_coords = list(initial_pt.coordinates(chart))
3992
+ # previous line converts to list since would otherwise be
3993
+ # a tuple ; will raise error if coordinates in chart are
3994
+ # not known
3995
+
3996
+ initial_coord_basis = chart.frame().at(initial_pt)
3997
+ initial_tgt_vec_comps = v0[initial_coord_basis,:]
3998
+ # will raise error if components in coordinate basis are
3999
+ # not known
4000
+
4001
+ description = "Geodesic "
4002
+ if self._name is not None:
4003
+ description += self._name + " "
4004
+ description += "in the {} ".format(self.codomain())
4005
+ description += "equipped with "
4006
+ description += "{}, ".format(self._metric)
4007
+ description += "and integrated over the "
4008
+ description += "{} ".format(self.domain())
4009
+ description += "as a solution to the following "
4010
+ description += "geodesic equations, written with respect to "
4011
+ description += "{}:\n\n".format(chart)
4012
+
4013
+ description += "Initial point: {} ".format(initial_pt)
4014
+ description += "with coordinates "
4015
+ description += "{} ".format(initial_pt_coords)
4016
+ description += "with respect to {}\n".format(chart)
4017
+
4018
+ description += "Initial tangent vector: {} ".format(v0)
4019
+ description += "with components "
4020
+ description += "{}".format(initial_tgt_vec_comps)
4021
+ description += " with respect to {}\n\n".format(chart)
4022
+
4023
+ for coord_func,velocity in zip(chart[:],self._velocities):
4024
+ description += "d({})/d{} = {}\n".format(coord_func,
4025
+ self._curve_parameter,
4026
+ velocity)
4027
+
4028
+ for velocity,eqn in zip(self._velocities,self._equations_rhs):
4029
+ description += "d({})/d{} = {}\n".format(velocity,
4030
+ self._curve_parameter,
4031
+ eqn)
4032
+
4033
+ print(description)
4034
+
4035
+ return [self._equations_rhs, v0, chart]