Sorry it took me a while to get to this. But effectively, we can model it as a bunch of mass and springs where the first mass is forced to move at a constant speed. Then, we can take the limit as the number of springs gets larger and closer together as the answer to your question. I coded a simulation for this in python and I will share the result and my code below. Feel free to comment on questions or advice on how to make my simulation better as I am always looking for ways to improve.
Some analytical comments: we can see that about halfway through the gif all the dots become close to each other as they were at the start, I believe this is because once they all have velocities around the same as the leading one, it just turns into a simple mass and spring problem, although it may be hard to see in this animation it is most like each mass is is simple harmonic motion around its equilibrium point.
I will preface my code with some notes: I have often called x[0] the position last mass and x[-1] that of the first, same thing with velocity and acceleration, this is just because I am visualizing the picture you gave and am beginning the array of positions from the left; I tried to comment my code as best as possible which at times may seem excessive, but I do not know your level of education and I want to be as accommodating as possible; please do not ask me questions about the plotting part of the code, I genuinely do not know anything about it; there are probably better ways to animate it with a moving frame but, again, I do not know how to.
Here is the gif:
Here is the code:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
''' Setting values '''
k = 1 # spring constant
m = 1 # mass
N = 10 # number of masses we will use
l = 1 # equilibrium length in between each point
dt = .01 # time in between each step
v0 = 10 # initial speed the leading mass is being driven at
Nt = 1000 # total number of iterations the code will run for
''' Defining Functions '''
def get_a(x): # getting acceleration from position
backward_a = -1 * (k/m) * (x - (np.roll(x,1) + l)) # acceleration from mass behind
backward_a[0] = 0 # the last mass does not have a mass behind it
forward_a = -1 * (k/m) * (x - (np.roll(x,-1) - l)) # acceleration from mass in front
a = forward_a + backward_a # total acceleration
a[-1] = 0 # setting the first mass to move at constant speed
return a
def mod_v(v_old,a): # modifying velocity
delta_v = a * dt
v_new = v_old + delta_v
return v_new
def mod_x(x_old,v): # modifying position
delta_x = v * dt
x_new = x_old + delta_x
return x_new
# I could have used a singular function for both position and velocity
# but I feel as though this improves readability.
''' Getting data '''
position = np.zeros((Nt+1,N))
# 2d array to represent positions of all the masses at different times
x = np.arange(0,N,l) # setting initial position
v = np.zeros(N) # setting initial velocity
v[-1] = v0 # making the firsts
position[0] = x # inputting it into data
for i in range(Nt):
x = mod_x(x,v) # getting new position
position[i+1] = x # inputting data
a = get_a(x) # getting a from x
v = mod_v(v,a) # updating v from a
''' plotting data '''
WaveData = position
x=np.zeros(N)
def plot_background():
line.set_data([], [])
return line,
def animate(i):
line.set_data(WaveData[i],x,)
return line,
fig = plt.figure()
ax = plt.axes(xlim=(-1, N*l + v0 * dt * Nt), ylim=(-0.2, 0.2),)
line, = ax.plot([], [], 'ro', lw=2)
anim = FuncAnimation(fig, animate, init_func=plot_background,
frames=Nt+1, interval=10, blit=True)
anim.save(r'animated_function.gif', fps=60)#, extra_args=['-vcodec', 'libx264'])
plt.show()