
.. DO NOT EDIT.
.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY.
.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE:
.. "_gallery/CatenaryCase/run_catenary.py"
.. LINE NUMBERS ARE GIVEN BELOW.

.. only:: html

    .. note::
        :class: sphx-glr-download-link-note

        :ref:`Go to the end <sphx_glr_download__gallery_CatenaryCase_run_catenary.py>`
        to download the full example code.

.. rst-class:: sphx-glr-example-title

.. _sphx_glr__gallery_CatenaryCase_run_catenary.py:


Catenary
========

This case simulates a rod hanging under its own weight, forming a catenary
curve. The rod is fixed at both ends and is allowed to settle into its
equilibrium position.

.. GENERATED FROM PYTHON SOURCE LINES 9-20

.. code-block:: Python


    from collections import defaultdict
    import numpy as np

    import elastica as ea

    from post_processing import (
        plot_video,
        plot_catenary,
    )








.. GENERATED FROM PYTHON SOURCE LINES 21-24

Simulation Setup
----------------
We define a simulator class that inherits from the necessary mixins.

.. GENERATED FROM PYTHON SOURCE LINES 24-35

.. code-block:: Python



    class CatenarySimulator(
        ea.BaseSystemCollection, ea.Constraints, ea.Forcing, ea.Damping, ea.CallBacks
    ):
        pass


    catenary_sim = CatenarySimulator()
    final_time = 30








.. GENERATED FROM PYTHON SOURCE LINES 36-39

Rod Setup
---------
We set up the rod parameters. This rod is affected by a gravity force.

.. GENERATED FROM PYTHON SOURCE LINES 39-81

.. code-block:: Python


    n_elem = 500
    time_step = 1e-4
    total_steps = int(final_time / time_step)
    rendering_fps = 20
    step_skip = int(1.0 / (rendering_fps * time_step))

    start = np.zeros((3,))
    direction = np.array([1.0, 0.0, 0.0])
    normal = np.array([0.0, 0.0, 1.0])
    binormal = np.cross(direction, normal)

    # catenary parameters
    base_length = 1.0
    base_radius = 0.01
    base_area = np.pi * (base_radius**2)
    volume = base_area * base_length
    mass = 0.2
    density = mass / volume
    E = 1e4
    poisson_ratio = 0.5
    shear_modulus = E / (poisson_ratio + 1.0)

    base_rod = ea.CosseratRod.straight_rod(
        n_elem,
        start,
        direction,
        normal,
        base_length,
        base_radius,
        density,
        youngs_modulus=E,
        shear_modulus=shear_modulus,
    )

    catenary_sim.append(base_rod)

    # Add gravity
    catenary_sim.add_forcing_to(base_rod).using(
        ea.GravityForces, acc_gravity=-9.80665 * normal
    )








.. GENERATED FROM PYTHON SOURCE LINES 82-83

Damping is added to the system to help it reach a steady state.

.. GENERATED FROM PYTHON SOURCE LINES 83-92

.. code-block:: Python


    # add damping
    damping_constant = 0.3
    catenary_sim.dampen(base_rod).using(
        ea.AnalyticalLinearDamper,
        damping_constant=damping_constant,
        time_step=time_step,
    )








.. GENERATED FROM PYTHON SOURCE LINES 93-96

Boundary Conditions
-------------------
We fix both ends of the rod using the `FixedConstraint`.

.. GENERATED FROM PYTHON SOURCE LINES 96-104

.. code-block:: Python


    # fix catenary ends
    catenary_sim.constrain(base_rod).using(
        ea.FixedConstraint,
        constrained_position_idx=(0, -1),
        constrained_director_idx=(0, -1),
    )








.. GENERATED FROM PYTHON SOURCE LINES 105-108

Callback
--------
We define a callback class to record the rod state during the simulation.

.. GENERATED FROM PYTHON SOURCE LINES 108-141

.. code-block:: Python



    # Add call backs
    class CatenaryCallBack(ea.CallBackBaseClass):
        """
        Call back function for continuum snake
        """

        def __init__(self, step_skip: int, callback_params: dict) -> None:
            super().__init__()
            self.every = step_skip
            self.callback_params = callback_params

        def make_callback(
            self, system: ea.typing.RodType, time: np.float64, current_step: int
        ) -> None:

            if current_step % self.every == 0:

                self.callback_params["time"].append(time)
                self.callback_params["step"].append(current_step)
                self.callback_params["position"].append(system.position_collection.copy())
                self.callback_params["radius"].append(system.radius.copy())
                self.callback_params["internal_force"].append(system.internal_forces.copy())

                return


    recorded_history: dict[str, list] = defaultdict(list)
    catenary_sim.collect_diagnostics(base_rod).using(
        CatenaryCallBack, step_skip=step_skip, callback_params=recorded_history
    )








.. GENERATED FROM PYTHON SOURCE LINES 142-145

Finalize and Run
----------------
We finalize the simulator, create the time-stepper, and run.

.. GENERATED FROM PYTHON SOURCE LINES 145-156

.. code-block:: Python


    catenary_sim.finalize()
    timestepper: ea.typing.StepperProtocol = ea.PositionVerlet()

    dt = final_time / total_steps
    time = 0.0
    for i in range(total_steps):
        time = timestepper.step(catenary_sim, time, dt)
    position = np.array(recorded_history["position"])
    b = np.min(position[-1][2])








.. GENERATED FROM PYTHON SOURCE LINES 157-161

Post-processing
---------------
Finally, we can save a video of the simulation and plot the final
shape of the catenary.

.. GENERATED FROM PYTHON SOURCE LINES 161-172

.. code-block:: Python


    # plotting the videos
    filename_video = "catenary.mp4"
    plot_video(
        recorded_history,
        video_name=filename_video,
        fps=rendering_fps,
        xlim=[0, base_length],
        ylim=[-0.5 * base_length, 0.5 * base_length],
    )





.. rst-class:: sphx-glr-script-out

 .. code-block:: none

    plot video
      0%|          | 0/600 [00:00<?, ?it/s]      1%|          | 4/600 [00:00<00:15, 39.01it/s]      2%|▏         | 11/600 [00:00<00:11, 52.97it/s]      3%|▎         | 18/600 [00:00<00:10, 56.58it/s]      4%|▍         | 25/600 [00:00<00:09, 58.67it/s]      5%|▌         | 31/600 [00:00<00:09, 58.18it/s]      6%|▌         | 37/600 [00:00<00:09, 57.59it/s]      7%|▋         | 44/600 [00:00<00:09, 58.96it/s]      8%|▊         | 50/600 [00:00<00:12, 43.60it/s]      9%|▉         | 55/600 [00:01<00:12, 43.08it/s]     10%|█         | 60/600 [00:01<00:12, 43.73it/s]     11%|█         | 65/600 [00:01<00:11, 45.19it/s]     12%|█▏        | 70/600 [00:01<00:12, 43.29it/s]     12%|█▎        | 75/600 [00:01<00:11, 45.01it/s]     14%|█▎        | 81/600 [00:01<00:10, 48.09it/s]     14%|█▍        | 87/600 [00:01<00:10, 48.75it/s]     15%|█▌        | 92/600 [00:01<00:10, 48.96it/s]     16%|█▋        | 98/600 [00:01<00:09, 50.42it/s]     17%|█▋        | 104/600 [00:02<00:10, 48.62it/s]     18%|█▊        | 110/600 [00:02<00:09, 49.55it/s]     19%|█▉        | 115/600 [00:02<00:10, 48.14it/s]     20%|██        | 121/600 [00:02<00:09, 49.40it/s]     21%|██        | 126/600 [00:02<00:09, 48.33it/s]     22%|██▏       | 131/600 [00:02<00:09, 48.31it/s]     23%|██▎       | 136/600 [00:02<00:09, 48.61it/s]     24%|██▎       | 141/600 [00:02<00:09, 48.76it/s]     24%|██▍       | 146/600 [00:02<00:09, 48.50it/s]     25%|██▌       | 152/600 [00:03<00:08, 49.83it/s]     26%|██▌       | 157/600 [00:03<00:08, 49.63it/s]     27%|██▋       | 163/600 [00:03<00:08, 52.44it/s]     28%|██▊       | 169/600 [00:03<00:08, 53.44it/s]     29%|██▉       | 175/600 [00:03<00:07, 54.69it/s]     30%|███       | 181/600 [00:03<00:07, 53.72it/s]     31%|███       | 187/600 [00:03<00:07, 54.23it/s]     32%|███▏      | 193/600 [00:03<00:07, 54.38it/s]     33%|███▎      | 199/600 [00:03<00:07, 52.00it/s]     34%|███▍      | 205/600 [00:04<00:07, 52.54it/s]     35%|███▌      | 211/600 [00:04<00:07, 51.39it/s]     36%|███▌      | 217/600 [00:04<00:07, 52.75it/s]     37%|███▋      | 223/600 [00:04<00:07, 53.31it/s]     38%|███▊      | 229/600 [00:04<00:06, 53.59it/s]     39%|███▉      | 235/600 [00:04<00:06, 54.02it/s]     40%|████      | 241/600 [00:04<00:06, 54.84it/s]     41%|████      | 247/600 [00:04<00:06, 54.58it/s]     42%|████▏     | 254/600 [00:04<00:06, 55.90it/s]     43%|████▎     | 260/600 [00:05<00:06, 56.27it/s]     44%|████▍     | 266/600 [00:05<00:05, 56.03it/s]     45%|████▌     | 272/600 [00:05<00:05, 56.04it/s]     46%|████▋     | 278/600 [00:05<00:05, 54.53it/s]     47%|████▋     | 284/600 [00:05<00:06, 49.34it/s]     48%|████▊     | 290/600 [00:05<00:06, 49.08it/s]     49%|████▉     | 296/600 [00:05<00:06, 49.99it/s]     50%|█████     | 302/600 [00:05<00:05, 50.22it/s]     51%|█████▏    | 308/600 [00:06<00:05, 51.15it/s]     52%|█████▏    | 314/600 [00:06<00:05, 52.03it/s]     53%|█████▎    | 320/600 [00:06<00:05, 52.80it/s]     54%|█████▍    | 326/600 [00:06<00:05, 53.18it/s]     55%|█████▌    | 332/600 [00:06<00:04, 54.29it/s]     56%|█████▋    | 338/600 [00:06<00:04, 55.06it/s]     57%|█████▋    | 344/600 [00:06<00:04, 56.00it/s]     58%|█████▊    | 350/600 [00:06<00:04, 55.33it/s]     59%|█████▉    | 356/600 [00:06<00:04, 56.11it/s]     60%|██████    | 362/600 [00:06<00:04, 56.98it/s]     61%|██████▏   | 368/600 [00:07<00:04, 56.87it/s]     62%|██████▏   | 374/600 [00:07<00:03, 56.74it/s]     63%|██████▎   | 380/600 [00:07<00:03, 57.03it/s]     64%|██████▍   | 386/600 [00:07<00:03, 55.85it/s]     65%|██████▌   | 392/600 [00:07<00:03, 56.31it/s]     66%|██████▋   | 398/600 [00:07<00:03, 56.21it/s]     67%|██████▋   | 404/600 [00:07<00:03, 56.43it/s]     68%|██████▊   | 410/600 [00:07<00:03, 57.27it/s]     69%|██████▉   | 416/600 [00:07<00:03, 57.18it/s]     70%|███████   | 422/600 [00:08<00:03, 56.26it/s]     71%|███████▏  | 428/600 [00:08<00:03, 57.06it/s]     72%|███████▏  | 434/600 [00:08<00:02, 55.66it/s]     73%|███████▎  | 440/600 [00:08<00:02, 54.79it/s]     74%|███████▍  | 446/600 [00:08<00:02, 54.36it/s]     75%|███████▌  | 452/600 [00:08<00:02, 55.11it/s]     76%|███████▋  | 458/600 [00:08<00:02, 54.21it/s]     77%|███████▋  | 464/600 [00:08<00:02, 53.32it/s]     78%|███████▊  | 470/600 [00:08<00:02, 52.29it/s]     79%|███████▉  | 476/600 [00:09<00:02, 53.02it/s]     80%|████████  | 482/600 [00:09<00:02, 53.89it/s]     81%|████████▏ | 488/600 [00:09<00:02, 54.86it/s]     82%|████████▏ | 494/600 [00:09<00:01, 55.19it/s]     83%|████████▎ | 500/600 [00:09<00:01, 54.27it/s]     84%|████████▍ | 506/600 [00:09<00:01, 53.55it/s]     85%|████████▌ | 512/600 [00:09<00:01, 55.02it/s]     86%|████████▋ | 518/600 [00:09<00:01, 54.06it/s]     87%|████████▋ | 524/600 [00:09<00:01, 53.72it/s]     88%|████████▊ | 530/600 [00:10<00:01, 53.13it/s]     89%|████████▉ | 536/600 [00:10<00:01, 53.28it/s]     90%|█████████ | 542/600 [00:10<00:01, 53.86it/s]     91%|█████████▏| 548/600 [00:10<00:00, 53.05it/s]     92%|█████████▏| 554/600 [00:10<00:00, 52.95it/s]     93%|█████████▎| 560/600 [00:10<00:00, 52.38it/s]     94%|█████████▍| 566/600 [00:10<00:00, 51.85it/s]     95%|█████████▌| 572/600 [00:10<00:00, 51.63it/s]     96%|█████████▋| 578/600 [00:10<00:00, 51.83it/s]     97%|█████████▋| 584/600 [00:11<00:00, 51.73it/s]     98%|█████████▊| 590/600 [00:11<00:00, 52.23it/s]     99%|█████████▉| 596/600 [00:11<00:00, 53.00it/s]    100%|██████████| 600/600 [00:11<00:00, 52.68it/s]




.. GENERATED FROM PYTHON SOURCE LINES 173-178

.. video:: ../../../examples/CatenaryCase/catenary.mp4
   :width: 720
   :autoplay:
   :muted:
   :loop:

.. GENERATED FROM PYTHON SOURCE LINES 180-181

plotting the catenary positions after simulation.

.. GENERATED FROM PYTHON SOURCE LINES 181-186

.. code-block:: Python

    plot_catenary(
        recorded_history,
        xlim=(0, base_length),
        ylim=(b, 0.0),
    )



.. image-sg:: /_gallery/CatenaryCase/images/sphx_glr_run_catenary_001.png
   :alt: Catenary Final Shape
   :srcset: /_gallery/CatenaryCase/images/sphx_glr_run_catenary_001.png
   :class: sphx-glr-single-img






.. rst-class:: sphx-glr-timing

   **Total running time of the script:** (0 minutes 47.772 seconds)


.. _sphx_glr_download__gallery_CatenaryCase_run_catenary.py:

.. only:: html

  .. container:: sphx-glr-footer sphx-glr-footer-example

    .. container:: sphx-glr-download sphx-glr-download-jupyter

      :download:`Download Jupyter notebook: run_catenary.ipynb <run_catenary.ipynb>`

    .. container:: sphx-glr-download sphx-glr-download-python

      :download:`Download Python source code: run_catenary.py <run_catenary.py>`

    .. container:: sphx-glr-download sphx-glr-download-zip

      :download:`Download zipped: run_catenary.zip <run_catenary.zip>`


.. only:: html

 .. rst-class:: sphx-glr-signature

    `Gallery generated by Sphinx-Gallery <https://sphinx-gallery.github.io>`_
