.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "api/gallery/classical/lagrangian/plot_01_double_pendulum.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_api_gallery_classical_lagrangian_plot_01_double_pendulum.py: The double pendulum and deterministic chaos ================================================= :class:`~physicskit.classical.systems.lagrangian.DoublePendulum` is two point masses :math:`m_1, m_2` on massless rigid rods of length :math:`l_1, l_2`, the second hanging from the first, with generalized coordinates :math:`q = (\theta_1, \theta_2)` measured from the downward vertical: .. math:: x_1 = l_1\sin\theta_1, \quad y_1 = -l_1\cos\theta_1, \qquad x_2 = x_1 + l_2\sin\theta_2, \quad y_2 = y_1 - l_2\cos\theta_2 . Its Lagrangian :math:`L = T - V` is built symbolically from these positions, .. math:: T = \tfrac12 m_1(\dot x_1^2+\dot y_1^2) + \tfrac12 m_2(\dot x_2^2+\dot y_2^2), \qquad V = m_1 g y_1 + m_2 g y_2 , and :class:`~physicskit.classical.utils.symbolic.LagrangianEngine` derives the Euler-Lagrange equations of motion, :math:`\frac{d}{dt}\frac{\partial L}{\partial \dot q_i} - \frac{\partial L}{\partial q_i} = 0`, automatically. Because the resulting mass matrix :math:`M(\theta_1, \theta_2)` depends on the configuration (it is *not* separable), the system is integrated with the general (non-separable-capable) implicit-midpoint symplectic integrator. This example shows that two trajectories launched 0.01 rad apart track each other briefly and then diverge completely: deterministic chaos, not integration error (energy stays conserved throughout; see ``tests/test_conservation.py``). See :doc:`/api/gallery/classical/visualizers/plot_01_side_by_side_animation` for an animated side-by-side view of this same system. .. GENERATED FROM PYTHON SOURCE LINES 42-47 .. code-block:: Python import matplotlib.pyplot as plt import numpy as np from physicskit.classical.systems.lagrangian import DoublePendulum .. GENERATED FROM PYTHON SOURCE LINES 48-50 Trajectory and phase portrait --------------------------------- .. GENERATED FROM PYTHON SOURCE LINES 50-68 .. code-block:: Python pendulum = DoublePendulum(theta0=[2.0, 1.0], thetadot0=[0.5, -0.3]) result = pendulum.integrate((0, 3.0), dt=3e-5, method="implicit_midpoint") tip = np.array([pendulum.positions(q) for q in result.q]) fig1, axes = plt.subplots(1, 2, figsize=(9, 4.2)) axes[0].plot(tip[:, 2], tip[:, 3], color="steelblue", lw=0.4) axes[0].set_title("Second bob's path (x2, y2)") axes[0].set_aspect("equal") axes[0].set_xlabel("x") axes[0].set_ylabel("y") axes[1].plot(result.q[:, 0], result.p[:, 0], color="firebrick", lw=0.4) axes[1].set_title(r"Phase portrait: $(\theta_1, \dot\theta_1)$") axes[1].set_xlabel(r"$\theta_1$") axes[1].set_ylabel(r"$\dot\theta_1$") fig1.tight_layout() .. image-sg:: /api/gallery/classical/lagrangian/images/sphx_glr_plot_01_double_pendulum_001.png :alt: Second bob's path (x2, y2), Phase portrait: $(\theta_1, \dot\theta_1)$ :srcset: /api/gallery/classical/lagrangian/images/sphx_glr_plot_01_double_pendulum_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 69-75 Sensitivity to initial conditions -------------------------------------- Two trajectories launched a hundredth of a radian apart track each other briefly, then diverge completely -- the defining signature of deterministic chaos (first analyzed rigorously for the 3-body problem by Poincare). .. GENERATED FROM PYTHON SOURCE LINES 75-93 .. code-block:: Python p1 = DoublePendulum(theta0=[2.0, 1.0], thetadot0=[0.5, -0.3]) p2 = DoublePendulum(theta0=[2.01, 1.0], thetadot0=[0.5, -0.3]) r1 = p1.integrate((0, 3.0), dt=3e-5, method="implicit_midpoint") r2 = p2.integrate((0, 3.0), dt=3e-5, method="implicit_midpoint") drift1 = np.max(np.abs(r1.energy - r1.energy[0])) / abs(r1.energy[0]) print(f"energy drift over 1e5 steps: {drift1:.3e} (chaos below is real dynamics, not numerical error)") fig2, ax = plt.subplots(figsize=(7, 4)) ax.plot(r1.t, r1.q[:, 0], color="steelblue", lw=0.8, label=r"$\theta_1(0)=2.00$") ax.plot(r2.t, r2.q[:, 0], color="firebrick", lw=0.8, label=r"$\theta_1(0)=2.01$") ax.set_xlabel("t") ax.set_ylabel(r"$\theta_1(t)$") ax.set_title("Trajectories that start 0.01 rad apart") ax.legend() .. image-sg:: /api/gallery/classical/lagrangian/images/sphx_glr_plot_01_double_pendulum_002.png :alt: Trajectories that start 0.01 rad apart :srcset: /api/gallery/classical/lagrangian/images/sphx_glr_plot_01_double_pendulum_002.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none energy drift over 1e5 steps: 1.791e-07 (chaos below is real dynamics, not numerical error) .. GENERATED FROM PYTHON SOURCE LINES 94-99 Because ``implicit_midpoint`` exactly conserves quadratic invariants of *any* ODE and is symplectic for canonical Hamiltonians in general, the divergence above is the real chaotic dynamics, not integration error. See also :doc:`plot_02_bead_on_rotating_hoop` and :doc:`plot_03_coupled_oscillators`, built the same symbolic way. .. GENERATED FROM PYTHON SOURCE LINES 99-101 .. code-block:: Python plt.show() .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 1.791 seconds) .. _sphx_glr_download_api_gallery_classical_lagrangian_plot_01_double_pendulum.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_01_double_pendulum.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_01_double_pendulum.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_01_double_pendulum.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_