123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 |
- """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()
|