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;
}