Answer for Problem 11
Using the FFT algorithms from Press, the time evolution of a plucked string was evaluated for a string that was fixed at the ends and one that was not. The below charts represent the data which came out of the program listed below.
#ifndef FUNCTION_H #define FUNCTION_H
#include <functional> #include <vector> #include <iostream> #include <time.h> #include <math.h>
using namespace std;
// // The following group of classes were an attempt to develop a way to call functions on functions // class Function : public unary_function<double,double> { public: Function() {}; virtual ~Function() { //do garbage collection on tempFunctions //crashes program? for (vector<Function*>::iterator iter = tempFunctions.begin(); iter != tempFunctions.end(); iter++) delete *iter; }; virtual double operator()(double x){return x;}; virtual Function& operator^(Function& f); virtual Function& operator*(Function& f); virtual Function& operator+(Function& f); virtual Function& operator-(Function& f); virtual Function& operator()(Function& f);
virtual Function& operator^(double x); virtual Function& operator*(double x); virtual Function& operator+(double x); virtual Function& operator-(double x);
virtual Function& operator-(); virtual Function& getDerivative() { return *this;}; protected: vector<Function*> tempFunctions; };
class PowFunc : public Function { public: PowFunc(Function&f1, Function& f2) : a(f1), b(f2) {} virtual double operator() ( double x) { return pow(a(x),b(x));}; virtual Function& getDerivative();
protected: Function& a; Function& b; };
Function& Function::operator^(Function& f) { PowFunc* a; tempFunctions.insert(tempFunctions.end(), (a = new PowFunc(*this, f))); return *a; };
Function& PowFunc::getDerivative() { cerr << "can not evaluate f(x)^g(x). Need to develop numerical approximation" <<endl; return a*b; };
class AdditionFunc : public Function { public: AdditionFunc(Function&f1, Function& f2) : a(f1), b(f2) {} virtual double operator() ( double x) { return (a(x)+b(x));}; virtual Function& getDerivative(); protected: Function& a; Function& b; };
Function& Function::operator+(Function& f) { AdditionFunc* a; tempFunctions.insert(tempFunctions.end(), (a = new AdditionFunc(*this, f))); return *a; };
Function& AdditionFunc::getDerivative() { return (a.getDerivative() + b.getDerivative()); };
class SubtractionFunc : public Function { public: SubtractionFunc(Function&f1, Function& f2) : a(f1), b(f2) {} virtual double operator() ( double x) { return (a(x)-b(x));}; virtual Function& getDerivative();
protected: Function& a; Function& b; };
Function& Function::operator-(Function& f) { SubtractionFunc* s; tempFunctions.insert(tempFunctions.end(), (s = new SubtractionFunc(*this, f))); return *s; };
Function& SubtractionFunc::getDerivative() { return (a.getDerivative() - b.getDerivative()); };
class MultiplyFunc : public Function { public: MultiplyFunc(Function& f1, Function& f2) :a(f1), b(f2) {}; virtual double operator() ( double x) { return a(x)*b(x);}; virtual Function& getDerivative();
protected: Function& a; Function& b; };
Function& Function::operator*(Function& f) { MultiplyFunc * m; tempFunctions.insert(tempFunctions.end(), (m = new MultiplyFunc(*this, f))); return *m; };
Function& MultiplyFunc::getDerivative() { return (a.getDerivative()*b + a*b.getDerivative()); };
class CompositionFunc : public Function { public: CompositionFunc(Function& f1, Function& f2) :a(f1), b(f2) {}; virtual double operator() ( double x) { return a(b(x));}; virtual Function& getDerivative(); protected: Function& a; Function& b; };
Function& Function::operator()(Function& f) { CompositionFunc* c; tempFunctions.insert(tempFunctions.end(), (c = new CompositionFunc(*this, f))); return *c; };
Function& CompositionFunc::getDerivative() { return (a.getDerivative()(b)*b.getDerivative()); };
class ConstFunc : public Function { public: ConstFunc(double x) : _x(x) {}; virtual double operator()(double x){return _x;}; protected: double _x; };
class IdentityFunction : public ConstFunc { public: IdentityFunction() : ConstFunc(1) {}; };
class VectorFunc : public Function { public: VectorFunc(vector<double> &list) : v(list) {}; virtual double operator()(double n) { return v[(int)n]; }; protected: vector<double> & v; };
Function& Function::operator^(double x) { ConstFunc* c; tempFunctions.insert(tempFunctions.end(), (c = new ConstFunc(x))); return ((*this)^(*c)); } Function& Function::operator*(double x) { ConstFunc* c; tempFunctions.insert(tempFunctions.end(), (c = new ConstFunc(x))); return ((*this)*(*c)); }; Function& Function::operator+(double x) { ConstFunc* c; tempFunctions.insert(tempFunctions.end(), (c = new ConstFunc(x))); return ((*this)+(*c)); } ; Function& Function::operator-(double x) { ConstFunc* c; tempFunctions.insert(tempFunctions.end(), (c = new ConstFunc(x))); return ((*this)-(*c)); } ; Function& Function::operator-() { ConstFunc* c; tempFunctions.insert(tempFunctions.end(), (c = new ConstFunc(0))); return ((*c) - (*this)); };
#define functor(NAME, FUNC) \ class NAME : public Function \ {\ public: \ virtual Function& operator()(Function& x) { return Function::operator ()(x);}; \ virtual double operator()(double x) { return FUNC(x);}; \ };
#define FunctionObject(FUNCTION) \ functor(FunctionObject##FUNCTION, FUNCTION) \ FunctionObject##FUNCTION FUNCTION##F;
#define FunctionObjectSpec(NAME, FUNCTION, OBJECT) \ functor(NAME,FUNCTION) \ NAME OBJECT;\
#define FunctionObjectSpecPtr(NAME, FUNCTION, OBJECT) \ functor(NAME,FUNCTION) \ NAME* OBJECT;\
#define FunctionObject_(NAME, FUNCTION) \ functor(NAME,FUNCTION) \ NAME FUNCTION##F;
FunctionObjectSpec(ExponentialFunc, exp, exponentialFunc);
FunctionObjectSpec(CosFunc, cos, cosFunc)
FunctionObjectSpec(SinFunc, sin, sinFunc);
class SumFunc : public Function { public: enum Type {Int, Dub}; SumFunc(int lower, int upper, int increment = 1) : a(lower), b(upper), inc(increment), type(Int) {}; SumFunc(double lower, double upper, double increment = 1) : a(lower), b(upper), inc(increment), type(Dub){}; virtual double operator()(double x) { return ((b-a)/inc)*x; }; virtual Function& operator()(Function& f) { double val = 0; for (double n = a;n<=b;n+=inc) val += f(n); ConstFunc *c = new ConstFunc(val); tempFunctions.insert(tempFunctions.end(), c); return *c; };
protected: double a, b, inc; Type type; };
#endif
#include "Function.h" //#include <math.h> #include <vector>
const double pi = acos(-1); //From numerical recipies
void four11( vector<double>& data, const int isign) { //vector<complex<double> > ret(vals.size()); //#include <cmath> //#include "nr.h" //using namespace std; //void NR: :four1(Vec_io_dp &data, const int isign) // Replaces data [0. .2*nn-1] by its discrete Fourier transform, if isign is input as 1; or replaces // data [0. .2*nn-1] by nn times its inverse discrete Fourier transform, if isign is input as -1. // data is a complex array of length nn stored as a real array of length 2*nn. nn MUST be an // integer power of 2 (this is not checked for!) { int n , mmax, m, j, istep, i; double wtemp, wr, wpr, wpi, wi, theta, tempr, tempi;
int nn=data .size( )/2; n=nn << 1; j=1; for (i=1; i<n; i+=2) { if (j>i) { swap (data [j-1], data [i-1]); swap (data [j], data [i]) ; } m=nn; while (m>=2 && j>m) { j-=m; m>>=1; } j+=m; } mmax=2; while (n>mmax) { istep=mmax << 1; theta=isign*(6.28318530717959/mmax); wtemp=sin (0.5*theta); wpr = -2.0*wtemp*wtemp; wpi = sin(theta); wr=1.0; wi=0.0; for (m=1; m<mmax; m+=2) { for (i=m; i<n; i+=istep) { j=i+mmax; tempr=wr*data [j-1] - wi*data [j]; tempi=wr*data [j] +wi*data [j-1]; data [j-1]=data [i-1] - tempr; data [j] = data [i] - tempi; data [i-1] += tempr; data [i] += tempi; } wr=(wtemp=wr)*wpr-wi*wpi+wr; wi=wi*wpr+wtemp*wpi+wi; } mmax=istep; } } }
void four11real(vector<double>& data, const int isign) { int i, i1, i2, i3, i4; double c1=0.5, c2, h1r, h1i, h2r, h2i, wr, wi, wpr, wpi, wtemp, theta;
int n=data. size ( ); theta=pi/(double)(n>>1); if (isign == 1) { c2 = -0.5; four11 (data,1); } else { c2=0.5; theta = -theta; } wtemp=sin (0.5*theta); wpr = -2.0*wtemp*wtemp; wpi =sin (theta); wr=1.0+wpr; wi=wpi; for (i=1; i<(n>>2) ; i++) { i2=1+(i1=i+i); i4=1+(i3=n-i1); h1r=c1*(data[i1] +data[i3] ); h1i=c1*(data [i2] - data [i4] ); h2r= -c2* (data [i2] +data [i4]); h2i=c2* (data [i1] -data [i3] ); data [i1]=h1r+wr*h2r-wi*h2i; data [i2] =h1i+wr*h2i+wi*h2r; data [i3] =h1r-wr*h2r+wi*h2i; data[i4] = -h1i+wr*h2i+wi*h2r; wr= (wtemp+wr)*wpr-wi*wpi+wr; wi=wi*wpr+wtemp*wpi+wi; } if (isign == 1) { data [0] = (h1r=data [0]) +data [1]; data [1] = h1r-data [1]; } else { data [0] =c1* ( ( h1r=data [0]) +data [1]); data [1] =c1* (h1r-data [1] ); four11(data, -1); } }
void four11sin(vector<double>& y) { int j; double sum, y1, y2, theta, wi=0.0, wr=1.0, wpi, wpr, wtemp; int n=y. size ( ); theta=3.141592653589793238/(double)(n); wtemp=sin (0.5*theta); wpr=-2.0*wtemp*wtemp; wpi=sin (theta); y [0] = 0.0; for (j=1; j<(n>>1)+1; j++) { wr=(wtemp=wr) *wpr-wi* wpi+wr; wi=wi*wpr+wtemp*wpi+wi; y1=wi*(y[j]+y [n-j]); y2=0.5* (y[j] - y[n-j]); y [j] =y1+y2; y [n-j]=y1-y2; } four11real(y,1); y [0]*=0.5; sum=y [1] =0.0; for (j=0; j<n-1; j+=2) { sum += y [j]; y [j]=y [j+1]; y [j+1]=sum; } }
int main(int argc, char* argv[]) { double n = pow(2,9); //number of Fourier modes must be power of two
double xInc = 0.01; //the position increment on which to evaluate the string for time evolution double x0 = 0.3; //distance in meters plucked double k = 1000; //tension in the string 1/meters^2 double L = 1; //length of the string in meters double v = 300; //speed of the wave in meters/seconds double totalTime = pi/v; //seconds to evaluate the wave for double timeInc = totalTime/25; //the time increments used to generate the data (seconds) double x; //counter to evaluate initial pluck double t; //counter to evaluate time evolution int i;
Function self; FunctionObject(exp); Function& y = expF(((self-x0)^2)*(-k)); //function represting initial spatial disturbance due to pluck
//setup vector containing initial position of the string vector<double> vals(2*n); vector<double> vals2(2*n);
cout << "# The sample values of the string at the initial pluck is given by\n"; for (x=0,i=0;x<=L;x+=L/n) { vals[i*2] = y(x); vals[i*2+1] = 0; cout << x << "\t" << vals[2*i] << "\t" << vals[2*i+1] << endl; i++; }; vals2 = vals;
four11(vals, 1); //perform fourier transform on the values to get our coefficients an, bn
cout << "\n# The fourier spectrum is given by\n"; for (i=0;i<n;i++) cout << i << "\t" << vals[2*i] << "\t" << vals[2*i+1] << endl;
cout << "\n# The time evolved plot is given by\n"; for (t=0;t<=totalTime;t+=timeInc) { for (x=0;x<=L;x+=xInc) { double sum =0; for (i=0;i<n/2;i++) sum += (vals[i*2]*cos(2*i*pi*x/L) + vals[i*2+1]*sin(2*i*pi*x/L))*cos(2*i*v*t/L); cout << t << "\t" << x << "\t" << sum << endl; } }
vals = vals2; four11sin(vals); cout << "\n# The fourier sin spectrum is given by\n"; for (i=0;i<n;i++) cout << i << "\t" << vals2[2*i] << "\t" << vals[2*i+1] << endl;
cout << "\n# The time evolved plot with the string held at both ends (fourier sin) is given by\n"; for (t=0;t<=totalTime;t+=timeInc) { for (x=0;x<=L;x+=xInc) { double sum =0; for (i=0;i<n;i++) //sum += (vals[i*2]*cos(2*i*pi*x/L) + vals[i*2+1]*sin(2*i*pi*x/L))*cos(2*i*v*t/L); sum += (vals[i]*sin(i*pi*x/L))*cos(2*i*v*t/L); //sine transform does not have a coefficients
cout << t << "\t" << x << "\t" << sum << endl; } } return 0; }