Coupled Spring Mass System#

This Question and answer is refered from the ME211 learning materials of Faculty of Engineering, University of Peradeniya.

Consider the coupled spring mass system:

image.png

Solve ODE#

Isolating each of the masses and applying Newton’s eaquations for each of them seperately we have:

\(\begin{align} m\ddot{x}_1&=-kx_1-k(x_1-x_2),\\ m\ddot{x}_2&=-kx_2-k(x_2-x_1) \end{align}\)

Which can be written as: \(\begin{align} M\ddot{X}+KX&=0 \end{align}\) where \(\begin{align} X=\begin{bmatrix}x_1\\x_2\end{bmatrix},\:\:\:\: M=\begin{bmatrix}m & 0\\0 & m\end{bmatrix},\:\:\:\: K=\begin{bmatrix}2k & -k\\ -k & 2k\end{bmatrix} \end{align}\)

This can also be written in the dynamic system form \(\begin{align} \dot{Y}&=AY \end{align}\) where \(\begin{align} Y=\begin{bmatrix}x_1\\x_2\\\dot{x}_1\\\dot{x}_2\end{bmatrix},\:\:\:\: A=\begin{bmatrix}0 & 0 & 1 &0\\ 0 & 0& 0 &1\\\frac{k}{m} & -\frac{2k}{m} & 0 &0 \\-\frac{2k}{m} & \frac{k}{m} & 0 &0 \end{bmatrix}. \end{align}\)

Import Modules#

[19]:
import numpy as np
from scipy.integrate import odeint
import plotly.graph_objects as go

Note

The functions create_line_trace, create_point_trace, create_arrow_trace, and and others were written in previous tutorials. Please include them in your notebook on top before starting to follow this tutorial. You can download it by clicking the Download icon on the Navigation Bar.

Define System Function#

[21]:
def system_dynamics(Y, t, k, m):
    A = np.array([[0, 0, 1, 0],
                  [0, 0, 0, 1],
                  [-2*k/m, k/m, 0, 0],
                  [k/m, -2*k/m, 0, 0]])
    dYdt = A@Y
    return dYdt

Constants and Conditions#

[22]:
# parameters and initial conditions
k = 1.0  # Spring constant
m = 1.0  # Mass
x1_0, x2_0 = 0.5, -0.5  # Initial positions
v1_0, v2_0 = 0.1, 0.1   # Initial velocities
Y0 = [x1_0, x2_0, v1_0, v2_0]
t = np.linspace(0, 10, 250)  # Time points

Solve ODE#

[23]:
# Solve ODE
solution = odeint(system_dynamics, Y0, t, args=(k, m))

# Extract positions and velocities
x1_vals, x2_vals = solution[:, 0], solution[:, 1]
v1_vals, v2_vals = solution[:, 2], solution[:, 3]

Plot the Solutions#

[24]:
# Plotting
fig = go.Figure()
fig.add_trace(go.Scatter(x=t, y=x1_vals, mode='lines', name='x1 (Position)'))
fig.add_trace(go.Scatter(x=t, y=x2_vals, mode='lines', name='x2 (Position)'))
fig.add_trace(go.Scatter(x=t, y=v1_vals, mode='lines', name='v1 (Velocity)', line=dict(dash='dash')))
fig.add_trace(go.Scatter(x=t, y=v2_vals, mode='lines', name='v2 (Velocity)', line=dict(dash='dash')))

fig.update_layout(title='Mass-Spring System Dynamics',
                  xaxis_title='Time (s)',
                  yaxis_title='Position / Velocity',
                  legend_title='Variable')

fig.show()

Animation#

[25]:
fig = go.Figure(
    data=[go.Scatter(x=[x1_vals[0]+0.5, x2_vals[0]-0.5], y=[0, 0], mode='markers', marker=dict(size=20, color=['red', 'blue']), name='Masses')],
    layout=go.Layout(
        xaxis=dict(range=[-1, 1], autorange=False),
        yaxis=dict(range=[-1, 1], autorange=False, scaleanchor="x", scaleratio=1),
        title="Animation of Two-Mass, Two-Spring System",
        updatemenus=[dict(
            type="buttons",
            buttons=[dict(label="Play",
                          method="animate",
                          args=[None, {"frame": {"duration": 50, "redraw": True}, "fromcurrent": True}]),
                    dict(label="Pause",
                         method="animate",
                         args=[[None], {"frame": {"duration": 0, "redraw": False}, "mode": "immediate"}])])]
    ),
    frames=[go.Frame(data=[go.Scatter(x=[x1_vals[k]+0.5, x2_vals[k]-0.5], y=[0, 0], mode='markers', marker=dict(size=20, color=['red', 'blue']))])
            for k in range(len(t))]
)

fig.show()

Energy#

[26]:
# Calculating energies
KE = 0.5 * m * (solution[:, 2]**2 + solution[:, 3]**2)  # Kinetic Energy: 0.5 * m * v^2
PE = 0.5 * k * (solution[:, 0]**2 + solution[:,1]**2 +(solution[:, 0] - solution[:, 1])**2)  # Potential Energy: 0.5 * k * x^2
TE = KE + PE  # Total Energy

# Plotting energies
energy_fig = go.Figure()
energy_fig.add_trace(go.Scatter(x=t, y=KE, mode='lines', name='Kinetic Energy'))
energy_fig.add_trace(go.Scatter(x=t, y=PE, mode='lines', name='Potential Energy'))
energy_fig.add_trace(go.Scatter(x=t, y=TE, mode='lines', name='Total Energy'))

energy_fig.update_layout(title='Energy of the Mass-Spring System',
                         xaxis_title='Time (s)',
                         yaxis_title='Energy',
                         legend_title='Type of Energy')

energy_fig.show()

Linear Momentum#

[27]:
P=m*solution[:,2]+m*solution[:,3]
fig = go.Figure()
fig.add_trace(go.Scatter(x=t, y=P, mode='lines',name='Linear Momentum'))
fig.update_layout(title='Linear Momentum Vs Time')
fig.show()