.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "api/gallery/classical/chains/plot_02_fput_recurrence.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_chains_plot_02_fput_recurrence.py: The Fermi-Pasta-Ulam-Tsingou recurrence ============================================= :class:`~physicskit.classical.systems.chains.FPUTChain` is the classic Fermi-Pasta-Ulam-Tsingou :math:`\beta`-lattice: :math:`N` fixed-end masses :math:`m` coupled by springs with an added quartic non-linearity, so the inter-site stretch :math:`r_i = q_{i+1} - q_i` costs potential energy .. math:: V(r_i) = \frac{k}{2} r_i^2 + \frac{\beta}{4} r_i^4 , \qquad H = \sum_{i=1}^{N} \frac{p_i^2}{2m} + \sum_{i=0}^{N} V(r_i) , giving the equation of motion :math:`m\ddot q_i = k(q_{i+1} - 2q_i + q_{i-1}) + \beta\!\left[(q_{i+1}-q_i)^3 - (q_i - q_{i-1})^3\right]`. Here a single low-order normal mode (mode 1) is excited initially, with :math:`\beta = 1`. Fermi, Pasta, Ulam, and Tsingou expected the non-linear coupling to thermalize the system -- energy spreading evenly across all normal-mode energies :math:`E_k` (see :class:`~physicskit.classical.systems.chains.HarmonicChain`, :doc:`plot_01_harmonic_chain`, for the linear-mode decomposition). Instead the system shows the famous near-exact recurrence: energy drains out of mode 1 into a couple of other low modes (mostly mode 3, here) and then flows back, essentially returning to the original single-mode state -- a complete cycle, not thermalization. Contrast this with :class:`~physicskit.classical.systems.chains.HarmonicChain`'s perfectly rigid mode-separation (:math:`\beta = 0`, see :doc:`plot_01_harmonic_chain`): the non-linearity is what lets modes exchange energy at all, but not ergodically. .. GENERATED FROM PYTHON SOURCE LINES 36-70 .. code-block:: Python import matplotlib.pyplot as plt import numpy as np from physicskit.classical.systems.chains import FPUTChain from physicskit.classical.visualizers.modal_analysis import plot_modal_energy_bars chain = FPUTChain(n=32, beta=1.0, mode=1, amplitude=1.5) e0 = chain.energy() result = chain.integrate((0, 9500), dt=0.02, method="yoshida4") drift = abs(result.energy[-1] - e0) / abs(e0) print(f"relative energy drift over the full run: {drift:.3e}") modal_history = np.array([chain.modal_energies(q, p) for q, p in zip(result.q[::200], result.p[::200])]) t_sampled = result.t[::200] mode1_fraction = modal_history[:, 0] / e0 midpoint_idx = int(np.argmin(mode1_fraction)) print( f"mode-1 energy fraction: starts at {mode1_fraction[0]:.3f}, " f"dips to {mode1_fraction[midpoint_idx]:.3f} at t={t_sampled[midpoint_idx]:.0f}, " f"ends at {mode1_fraction[-1]:.3f}" ) fig1, ax = plt.subplots(figsize=(8, 4.5)) for k in range(5): ax.plot(t_sampled, modal_history[:, k], label=f"mode {k + 1}") ax.axvline(t_sampled[midpoint_idx], color="0.7", lw=0.8, ls="--") annotate_xy = (t_sampled[midpoint_idx], modal_history[midpoint_idx, 0]) ax.annotate("energy most spread out here", xy=annotate_xy, xytext=(0.55, 0.5), textcoords="axes fraction", arrowprops=dict(arrowstyle="->", color="0.4")) ax.set_xlabel("t") ax.set_ylabel(r"$E_k(t)$") ax.set_title("A complete recurrence cycle: energy leaves mode 1 and fully returns") ax.legend() fig1.tight_layout() .. image-sg:: /api/gallery/classical/chains/images/sphx_glr_plot_02_fput_recurrence_001.png :alt: A complete recurrence cycle: energy leaves mode 1 and fully returns :srcset: /api/gallery/classical/chains/images/sphx_glr_plot_02_fput_recurrence_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none relative energy drift over the full run: 1.549e-12 mode-1 energy fraction: starts at 0.992, dips to 0.630 at t=4952, ends at 0.992 .. GENERATED FROM PYTHON SOURCE LINES 71-73 Modal energy distribution: mid-cycle vs. after the recurrence -------------------------------------------------------------------- .. GENERATED FROM PYTHON SOURCE LINES 73-83 .. code-block:: Python fig2, axes2 = plt.subplots(1, 2, figsize=(11, 4)) plot_modal_energy_bars(chain, result.q[::200][midpoint_idx], result.p[::200][midpoint_idx], ax=axes2[0], color="firebrick") axes2[0].set_title(f"Most spread out, t={t_sampled[midpoint_idx]:.0f}") plot_modal_energy_bars(chain, result.q[-1], result.p[-1], ax=axes2[1], color="steelblue") axes2[1].set_title(f"Recurred, t={result.t[-1]:.0f} (energy drift = {drift:.1e})") fig2.suptitle("Modal energy distribution: mid-cycle vs. after the recurrence") fig2.tight_layout(rect=[0, 0, 1, 0.92]) plt.show() .. image-sg:: /api/gallery/classical/chains/images/sphx_glr_plot_02_fput_recurrence_002.png :alt: Modal energy distribution: mid-cycle vs. after the recurrence, Most spread out, t=4952, Recurred, t=9500 (energy drift = 1.5e-12) :srcset: /api/gallery/classical/chains/images/sphx_glr_plot_02_fput_recurrence_002.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 3.253 seconds) .. _sphx_glr_download_api_gallery_classical_chains_plot_02_fput_recurrence.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_02_fput_recurrence.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_02_fput_recurrence.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_02_fput_recurrence.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_