#!/bin/sh """:" exec python $0 ${1+"$@"} """ # ---------------------- HelloWavesExt.py ---------------------- # # This program demonstrates how to make an animation of the # wave equation. The user can specify the initial # conditions by dragging the graph. # # This is a slightly extended version of the HelloWaves.py script, # also containing a short description of the numerical model used # to simulate the motion of a guitar string. from Tkinter import * # The Tk package import Pmw # The Python MegaWidget package import math # import the sin-function master = Tk() # build Tk-environment npoints = 15 # use 10 points on each curve def mouseDrag(event): global idx u[idx] = g.yaxis_invtransform(event.y) g.element_configure("string", ydata = tuple(u)) def mouseDown(event): global idx g.element_bind("string", "<Motion>", mouseDrag) el = g.element_closest(event.x, event.y) idx = el["index"] def mouseUp(event): g.element_unbind("string", "<Motion>") init() def step(): global u, up, um, C # Description of the mathematical model used for simulating # waves on a guitar string: # # Waves on a string are described (as many other phenomena # like light and radio waves) by the wave equation # # u_tt = u_xx # # i.e. the second derivative in time of a function u(x,t) equals # the second derivative in space (sometimes you see the wave velocity # in here as well, but we have now scaled it away). # # The physical interpretation of u in the current context is the # displacement of the string from its equilibrium position. # # The equation u_tt = u_xx is to be solved for 0<x<1.5 in this example. # At time t=0 we need to specify the shape of the string (this can # be done interactively!) and the velocity of the string, which is # naturally set to zero. # # The mathematical model outlined so far is then discretized by # a numerical method, here the finite difference method. This means # that the second-order derivatives are replaced by finite differences # according to the formula: # # f''(x[i]) = (f(x[i+1]) -2f(x[i]) +f(x[i-1]))/(dx*dx) # # where dx is the distance between x[i-1] and x[i], and x[i] and x[i+1]. # If we imagine that all f values are stored in an array f[i], we can # write the approximation like this: # # f''(x[i]) = (f[i+1] -2*f[i] + f[i-1])/(dx*dx) # # To apply such a formula to our present equation, we need to store # the computed values of u in an array, which for simplicity can be # written as u[i,j], where i is a counter for points in the x direction # and j is a counter for points in t direction. # Inserting the approximations to the second derivatives in the # equation u_tt = u_xx results in a recurrence relation # # (u[i,j+1] -2*u[i,j] + u[i,j-1])/(dt*dt) = # (u[i+1,j] -2*u[i,j] + u[i-1,j])/(dx*dx) # # This formula can be solved with respect to u[i,j+1], which # is the new value of u we compute (assuming that u at time levels # j and j-1 are already computed). # # u[i,j+1] = 2*u[i,j] - u[i,j-1] + C*C*(u[i+1,j] -2*u[i,j] + u[i-1,j]) # # with C*C = dt*dt/(dx*dx). # # It is only necessary to store three time levels: j+1, j, and j-1, # i.e., all previous levels can be thrown away. To this end, we introduce # three arrays u[i], up[i], and um[i], for u[i,j], u[i,j+1], and # u[i,j-1]. Moreover, we need a special value of u[i,-1] to get # started with the scheme (this value incorporates the start condition # that the velocity of the string is zero): # # um[i] = u[i] + 0.5*C*C*(u[i+1] - 2*u[i] + u[i-1]) # # The parameter C is important: C>1 will introduce numerical instability # and the displacement of the string will explode (try it!). # C=1 is a very special case where the numerical model (which is # usually only an approximation to the mathematical model) is exact # for any choice of dx=dt. However, if you make a wavy initial condition, # the exact solution obtained by C=1 is less attractive from a # visual point of view, so we have set C=0.8 here to introduce some # numerical errors that make the graph look nicer ;-) # # As u=0 always at the end points, we only update up (the new values) # at the interior x points 1...(npoints-1) # here is the magic line that finds new displacements of the # points along the string: for i in range(1, len(u)-1): up[i] = 2*u[i] -um[i] + C*C*(u[i+1] - 2*u[i] + u[i-1]) # shuffle arrays (to be ready for calling step() at the next time level): um = u[:] u = up[:] # update the graph: g.element_configure("string", ydata = tuple(u)) def run(): g.element_configure("string", symbol="") ntimesteps = 200 # the length of the animation for t in range(ntimesteps): step() master.after(20) # wait 0.02 second master.update_idletasks() # update screen g.element_configure("string", symbol="circle") def init(): global u, up, um, C C = 0.8 # make special value for um (incorporating du/dt=0 at t=0): um = u[:] # ensure that um is an array of correct length... for i in range(1, len(um)-1): um[i] = u[i] + 0.5*C*C*(u[i+1] - 2*u[i] + u[i-1]) up = u[:] def zero(): for i in range(len(u)): u[i] = 0 g.element_configure("string", ydata = tuple(u)) init() if not Pmw.Blt.haveblt(master): # Is Blt installed? print("BLT is not installed!") else: vector_x = [] # x coordinates of the grid points u = [] # string displacement at the x values # make a default graph for x in range(npoints+1): vector_x.append(x*0.1) if(x < 2.0*npoints/3): u.append(x/(2.0*npoints/3)) else: u.append(float(npoints-x)/(npoints/3)) init() g = Pmw.Blt.Graph(master) g.pack(expand=1, fill='both') curvename = 'string' g.line_create(curvename, xdata=tuple(vector_x), ydata=tuple(u), pixels=7, smooth='natural') # smooth the curve by splines g.element_bind("string", "<ButtonPress>", mouseDown) g.element_bind("string", "<ButtonRelease>", mouseUp ) g.yaxis_configure(min=-1, max=1) g.configure(title='Hello world of waves', width=250, height=200) buttons = Pmw.ButtonBox(master, labelpos='n', label_text='Options') buttons.pack(fill='x', expand=0, padx=10, pady=10) buttons.add('Simulate', command=run) buttons.add('Step', command=step) buttons.add('Zero', command=zero) buttons.add('Quit', command=master.quit) master.mainloop()