"""Simulate a discrete collection of oscillators We will use this as a model of a vibrating string and compare two methods of integration: Euler (red) and Runge-Kutta4 (green). For too large values of dt the simple Euler will diverge.""" # To model 'N' oscillators, we will use N+2 Points, numbered # 0, 1, 2, 3, ... N+1. Points 0 and N+1 are actually the boundaries. # We will keep them fixed, but adding them in as if they were # masses makes the programming easier. # Adapted from B.Martin (2009) http://www.kcvs.ca/martin by M.Musy from vedo import * #################################################### N = 400 # Number of coupled oscillators dt = 0.5 # Time step nsteps = 2000 # Number of steps in the simulation #################################################### # Initial positions #################################################### x = np.array(list(range(N + 2))) z = np.zeros(N + 2, float) y = np.zeros(N + 2, float) # y[p] is the position of particle p for p in x: # p is particle number along x axis y[p] = 100 * np.sin(p/15) * np.exp(-p/50) #################################################### # Initial velocities #################################################### v = np.zeros(N + 2, float) # or you can give one specific particle a kick: # v[40] = 50 #################################################### # Integrate forward #################################################### # Acceleration function for the simple harmonic oscillator def accel(y, v, t): a = np.zeros(N + 2, float) # acceleration of particles a[1 : N+1] = -(y[1 : N+1] - y[0:N]) - (y[1 : N+1] - y[2 : N+2]) return a def runge_kutta4(y, v, t, dt): # 4th Order Runge-Kutta yk1 = dt * v vk1 = dt * accel(y, v, t) yk2 = dt * (v + vk1 / 2) vk2 = dt * accel(y + yk1 / 2, v + vk1 / 2, t + dt / 2) yk3 = dt * (v + vk2 / 2) vk3 = dt * accel(y + yk2 / 2, v + vk2 / 2, t + dt / 2) yk4 = dt * (v + vk3) vk4 = dt * accel(y + yk3, v + vk3, t + dt) ynew = y + (yk1 + 2 * yk2 + 2 * yk3 + yk4) / 6 vnew = v + (vk1 + 2 * vk2 + 2 * vk3 + vk4) / 6 return ynew, vnew def euler(y, v, t, dt): # simple euler integrator vnew = v + accel(y, v, t) * dt ynew = y + vnew * dt + 1 / 2 * accel(y, vnew, t) * dt ** 2 return ynew, vnew positions_eu, positions_rk = [], [] y_eu, y_rk = np.array(y), np.array(y) v_eu, v_rk = np.array(v), np.array(v) t = 0 for i in progressbar(nsteps, c="b", title="integrating RK4 and Euler"): y_eu, v_eu = euler(y_eu, v_eu, t, dt) y_rk, v_rk = runge_kutta4(y_rk, v_rk, t, dt) t += dt positions_eu.append(y_eu) # store result of integration positions_rk.append(y_rk) #################################################### # Visualize the result #################################################### plt = Plotter(interactive=False, axes=2, size=(1400,1000)) line_eu = Line([0,0,0], [len(x)-1,0,0], res=len(x)).c("red5").lw(5) plt += line_eu line_rk = Line([0,0,0], [len(x)-1,0,0], res=len(x)).c("green5").lw(5) plt += line_rk # let's also add a fancy background image from wikipedia img = dataurl + "images/wave_wiki.png" plt += Image(img).alpha(0.8).scale(0.4).pos(0,-100,-1) plt += __doc__ plt.show(zoom=1.5) for i in progressbar(nsteps, title="visualize the result", c='y'): if i%10 != 0: continue y_eu = positions_eu[i] # retrieve the list of y positions at step i y_rk = positions_rk[i] pts = line_eu.points pts[:,1] = y_eu line_eu.points = pts pts = line_rk.points pts[:,1] = y_rk line_rk.points = pts plt.render() plt.interactive().close()