Simulation#
Plotting#
We’ll examine how to plot a simple mass-spring-damper system. We’ll first define the system
# System parameters
m = 1.0 # mass
c = 0.5 # damping coefficient
k = 2.0 # spring constant
# Define the system of ODEs
def mass_spring_damper(t, y):
x, v = y
dxdt = v
dvdt = (-c * v - k * x) / m
return [dxdt, dvdt]
We then define our initial conditions, and the range we would like to simulate.
import numpy as np # For storing out solutions in np.arrays
# Initial conditions
y0 = [1.0, 0.0] # initial displacement and velocity
# Time span
t_end = 15
t_span = (0, t_end)
t_eval = np.linspace(*t_span, 300) # Increased to 300 for smoother animation
We can then use scipy to solve the initial value problem (IVP) and store the solutions in a pandas dataframe.
import pandas as pd
from scipy.integrate import solve_ivp
# Solve the ODE
sol = solve_ivp(mass_spring_damper, t_span, y0, t_eval=t_eval)
# Create a DataFrame
df = pd.DataFrame({'time': sol.t, 'displacement': sol.y[0]})
Using plotly we can animate the trajectory and display it in a Javascript player.
import plotly.graph_objects as go
animation_time = 10
# Create animation frames
frames = [
go.Frame(data=[
go.Scatter(x=df['time'][:i+1], y=df['displacement'][:i+1], mode='lines', name='trajectory'), # Line showing trajectory
go.Scatter(x=[df['time'][i]], y=[df['displacement'][i]], mode='markers', marker=dict(size=10, color='red'), name='current point') # Marker for current point
],
name=str(df['time'][i]))
for i in range(len(df))
]
# Create the figure
fig = go.Figure(
data=[
go.Scatter(x=[df['time'][0]], y=[df['displacement'][0]], mode='lines', name='trajectory'), # Initial empty line
go.Scatter(x=[df['time'][0]], y=[df['displacement'][0]], mode='markers', marker=dict(size=10, color='red'), name='current point') # Initial marker
],
layout=go.Layout(
xaxis=dict(range=t_span, autorange=False, title='Time'),
yaxis=dict(range=[-1,1], autorange=False, title='Displacement'),
title="Mass-Spring-Damper System Animation",
updatemenus=[dict(type="buttons",
buttons=[dict(label="Play",
method="animate",
args=[None, {"frame": {"duration": animation_time, "redraw": False},
"fromcurrent": True}])])]),
frames=frames
)
fig.show()