passagemath-symbolics 10.8.1a1__cp311-cp311-macosx_13_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_symbolics/.dylibs/libgmp.10.dylib +0 -0
- passagemath_symbolics/__init__.py +3 -0
- passagemath_symbolics-10.8.1a1.dist-info/METADATA +186 -0
- passagemath_symbolics-10.8.1a1.dist-info/RECORD +182 -0
- passagemath_symbolics-10.8.1a1.dist-info/WHEEL +6 -0
- passagemath_symbolics-10.8.1a1.dist-info/top_level.txt +3 -0
- sage/all__sagemath_symbolics.py +17 -0
- sage/calculus/all.py +14 -0
- sage/calculus/calculus.py +2838 -0
- sage/calculus/desolvers.py +1864 -0
- sage/calculus/predefined.py +51 -0
- sage/calculus/tests.py +225 -0
- sage/calculus/var.cpython-311-darwin.so +0 -0
- sage/calculus/var.pyx +401 -0
- sage/dynamics/all__sagemath_symbolics.py +6 -0
- sage/dynamics/complex_dynamics/all.py +5 -0
- sage/dynamics/complex_dynamics/mandel_julia.py +765 -0
- sage/dynamics/complex_dynamics/mandel_julia_helper.cpython-311-darwin.so +0 -0
- sage/dynamics/complex_dynamics/mandel_julia_helper.pyx +1034 -0
- sage/ext/all__sagemath_symbolics.py +1 -0
- sage/ext_data/kenzo/CP2.txt +45 -0
- sage/ext_data/kenzo/CP3.txt +349 -0
- sage/ext_data/kenzo/CP4.txt +4774 -0
- sage/ext_data/kenzo/README.txt +49 -0
- sage/ext_data/kenzo/S4.txt +20 -0
- sage/ext_data/magma/latex/latex.m +1021 -0
- sage/ext_data/magma/latex/latex.spec +1 -0
- sage/ext_data/magma/sage/basic.m +356 -0
- sage/ext_data/magma/sage/sage.spec +1 -0
- sage/ext_data/magma/spec +9 -0
- sage/geometry/all__sagemath_symbolics.py +8 -0
- sage/geometry/hyperbolic_space/all.py +5 -0
- sage/geometry/hyperbolic_space/hyperbolic_coercion.py +755 -0
- sage/geometry/hyperbolic_space/hyperbolic_constants.py +5 -0
- sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +2419 -0
- sage/geometry/hyperbolic_space/hyperbolic_interface.py +206 -0
- sage/geometry/hyperbolic_space/hyperbolic_isometry.py +1083 -0
- sage/geometry/hyperbolic_space/hyperbolic_model.py +1502 -0
- sage/geometry/hyperbolic_space/hyperbolic_point.py +621 -0
- sage/geometry/riemannian_manifolds/all.py +7 -0
- sage/geometry/riemannian_manifolds/parametrized_surface3d.py +1632 -0
- sage/geometry/riemannian_manifolds/surface3d_generators.py +461 -0
- sage/interfaces/all__sagemath_symbolics.py +1 -0
- sage/interfaces/magma.py +2991 -0
- sage/interfaces/magma_free.py +90 -0
- sage/interfaces/maple.py +1402 -0
- sage/interfaces/mathematica.py +1345 -0
- sage/interfaces/mathics.py +1312 -0
- sage/interfaces/sympy.py +1398 -0
- sage/interfaces/sympy_wrapper.py +197 -0
- sage/interfaces/tides.py +938 -0
- sage/libs/all__sagemath_symbolics.py +6 -0
- sage/manifolds/all.py +7 -0
- sage/manifolds/calculus_method.py +553 -0
- sage/manifolds/catalog.py +437 -0
- sage/manifolds/chart.py +4010 -0
- sage/manifolds/chart_func.py +3416 -0
- sage/manifolds/continuous_map.py +2183 -0
- sage/manifolds/continuous_map_image.py +155 -0
- sage/manifolds/differentiable/affine_connection.py +2475 -0
- sage/manifolds/differentiable/all.py +1 -0
- sage/manifolds/differentiable/automorphismfield.py +1383 -0
- sage/manifolds/differentiable/automorphismfield_group.py +604 -0
- sage/manifolds/differentiable/bundle_connection.py +1445 -0
- sage/manifolds/differentiable/characteristic_cohomology_class.py +1840 -0
- sage/manifolds/differentiable/chart.py +1241 -0
- sage/manifolds/differentiable/curve.py +1028 -0
- sage/manifolds/differentiable/de_rham_cohomology.py +541 -0
- sage/manifolds/differentiable/degenerate.py +559 -0
- sage/manifolds/differentiable/degenerate_submanifold.py +1668 -0
- sage/manifolds/differentiable/diff_form.py +1660 -0
- sage/manifolds/differentiable/diff_form_module.py +1062 -0
- sage/manifolds/differentiable/diff_map.py +1315 -0
- sage/manifolds/differentiable/differentiable_submanifold.py +291 -0
- sage/manifolds/differentiable/examples/all.py +1 -0
- sage/manifolds/differentiable/examples/euclidean.py +2517 -0
- sage/manifolds/differentiable/examples/real_line.py +897 -0
- sage/manifolds/differentiable/examples/sphere.py +1186 -0
- sage/manifolds/differentiable/examples/symplectic_space.py +187 -0
- sage/manifolds/differentiable/examples/symplectic_space_test.py +40 -0
- sage/manifolds/differentiable/integrated_curve.py +4035 -0
- sage/manifolds/differentiable/levi_civita_connection.py +841 -0
- sage/manifolds/differentiable/manifold.py +4254 -0
- sage/manifolds/differentiable/manifold_homset.py +1826 -0
- sage/manifolds/differentiable/metric.py +3032 -0
- sage/manifolds/differentiable/mixed_form.py +1507 -0
- sage/manifolds/differentiable/mixed_form_algebra.py +559 -0
- sage/manifolds/differentiable/multivector_module.py +800 -0
- sage/manifolds/differentiable/multivectorfield.py +1522 -0
- sage/manifolds/differentiable/poisson_tensor.py +268 -0
- sage/manifolds/differentiable/pseudo_riemannian.py +755 -0
- sage/manifolds/differentiable/pseudo_riemannian_submanifold.py +1839 -0
- sage/manifolds/differentiable/scalarfield.py +1343 -0
- sage/manifolds/differentiable/scalarfield_algebra.py +472 -0
- sage/manifolds/differentiable/symplectic_form.py +912 -0
- sage/manifolds/differentiable/symplectic_form_test.py +220 -0
- sage/manifolds/differentiable/tangent_space.py +412 -0
- sage/manifolds/differentiable/tangent_vector.py +616 -0
- sage/manifolds/differentiable/tensorfield.py +4665 -0
- sage/manifolds/differentiable/tensorfield_module.py +963 -0
- sage/manifolds/differentiable/tensorfield_paral.py +2450 -0
- sage/manifolds/differentiable/tensorfield_paral_test.py +16 -0
- sage/manifolds/differentiable/vector_bundle.py +1725 -0
- sage/manifolds/differentiable/vectorfield.py +1717 -0
- sage/manifolds/differentiable/vectorfield_module.py +2445 -0
- sage/manifolds/differentiable/vectorframe.py +1832 -0
- sage/manifolds/family.py +270 -0
- sage/manifolds/local_frame.py +1490 -0
- sage/manifolds/manifold.py +3090 -0
- sage/manifolds/manifold_homset.py +452 -0
- sage/manifolds/operators.py +359 -0
- sage/manifolds/point.py +994 -0
- sage/manifolds/scalarfield.py +3718 -0
- sage/manifolds/scalarfield_algebra.py +629 -0
- sage/manifolds/section.py +3111 -0
- sage/manifolds/section_module.py +831 -0
- sage/manifolds/structure.py +229 -0
- sage/manifolds/subset.py +2721 -0
- sage/manifolds/subsets/all.py +1 -0
- sage/manifolds/subsets/closure.py +131 -0
- sage/manifolds/subsets/pullback.py +883 -0
- sage/manifolds/topological_submanifold.py +891 -0
- sage/manifolds/trivialization.py +733 -0
- sage/manifolds/utilities.py +1348 -0
- sage/manifolds/vector_bundle.py +1347 -0
- sage/manifolds/vector_bundle_fiber.py +332 -0
- sage/manifolds/vector_bundle_fiber_element.py +111 -0
- sage/matrix/all__sagemath_symbolics.py +1 -0
- sage/matrix/matrix_symbolic_dense.cpython-311-darwin.so +0 -0
- sage/matrix/matrix_symbolic_dense.pxd +6 -0
- sage/matrix/matrix_symbolic_dense.pyx +1030 -0
- sage/matrix/matrix_symbolic_sparse.cpython-311-darwin.so +0 -0
- sage/matrix/matrix_symbolic_sparse.pxd +6 -0
- sage/matrix/matrix_symbolic_sparse.pyx +1038 -0
- sage/modules/all__sagemath_symbolics.py +1 -0
- sage/modules/vector_callable_symbolic_dense.py +105 -0
- sage/modules/vector_symbolic_dense.py +116 -0
- sage/modules/vector_symbolic_sparse.py +118 -0
- sage/rings/all__sagemath_symbolics.py +4 -0
- sage/rings/asymptotic/all.py +6 -0
- sage/rings/asymptotic/asymptotic_expansion_generators.py +1485 -0
- sage/rings/asymptotic/asymptotic_ring.py +4858 -0
- sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +4106 -0
- sage/rings/asymptotic/growth_group.py +5373 -0
- sage/rings/asymptotic/growth_group_cartesian.py +1400 -0
- sage/rings/asymptotic/term_monoid.py +5205 -0
- sage/rings/function_field/all__sagemath_symbolics.py +2 -0
- sage/rings/polynomial/all__sagemath_symbolics.py +1 -0
- sage/symbolic/all.py +15 -0
- sage/symbolic/assumptions.py +987 -0
- sage/symbolic/benchmark.py +93 -0
- sage/symbolic/callable.py +456 -0
- sage/symbolic/callable.pyi +66 -0
- sage/symbolic/comparison_impl.pyi +38 -0
- sage/symbolic/complexity_measures.py +35 -0
- sage/symbolic/constants.py +1286 -0
- sage/symbolic/constants_c_impl.pyi +10 -0
- sage/symbolic/expression_conversion_algebraic.py +310 -0
- sage/symbolic/expression_conversion_sympy.py +317 -0
- sage/symbolic/expression_conversions.py +1727 -0
- sage/symbolic/function_factory.py +355 -0
- sage/symbolic/function_factory.pyi +41 -0
- sage/symbolic/getitem_impl.pyi +24 -0
- sage/symbolic/integration/all.py +1 -0
- sage/symbolic/integration/external.py +271 -0
- sage/symbolic/integration/integral.py +1075 -0
- sage/symbolic/maxima_wrapper.py +162 -0
- sage/symbolic/operators.py +267 -0
- sage/symbolic/operators.pyi +61 -0
- sage/symbolic/pynac_constant_impl.pyi +13 -0
- sage/symbolic/pynac_function_impl.pyi +8 -0
- sage/symbolic/random_tests.py +461 -0
- sage/symbolic/relation.py +2062 -0
- sage/symbolic/ring.cpython-311-darwin.so +0 -0
- sage/symbolic/ring.pxd +5 -0
- sage/symbolic/ring.pyi +110 -0
- sage/symbolic/ring.pyx +1393 -0
- sage/symbolic/series_impl.pyi +10 -0
- sage/symbolic/subring.py +1025 -0
- sage/symbolic/symengine.py +19 -0
- sage/symbolic/tests.py +40 -0
- sage/symbolic/units.py +1468 -0
|
@@ -0,0 +1,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]
|