#!/usr/bin/env python
#fixed step euler and runge-kutta for harmonic oscillator
from __future__ import division
from numpy import *
from matplotlib import pyplot as plt
import pyximport
pyximport.install(setup_args={"include_dirs":get_include()})
from integration import *

'''
#moved the integration to cython
def euler_python(z0,n_steps,h):
	#python implementation of euler step
	z=array([z0]) #this will accumulate function values over time
	for i in range(n_steps-1):
		z_new = z[-1] + h*f(z[-1])
		z = vstack((z,z_new))
	return z
'''

def f(z):
	#we are simplifying a second order equation in y to a first order equation in z
	#we know that ddy = -y, so let z = (y,dy)
	#then dz = (dy,ddy) = (dy, -y) = (z[1],-z[0])
	return array([z[1],-z[0]])

def compare_fixed_h(ti,tf,h,z0):
	from time import time
	n_steps = int((tf-ti)/h)
	t = linspace(ti,tf,n_steps)
	es = empty((n_steps,2))
	rks = empty((n_steps,2))
	t0 = time()
	euler_cython(f,z0,es,n_steps,h)
	print "Euler took: %.3f s"%(time()-t0)
	t0 = time()
	runge_kutta_4_cython(f,z0,rks,n_steps,h)
	print "Runge Kutta took: %.3f s"%(time()-t0)

	plt.figure()
	plt.plot(t,es[...,0],color='b', label='euler')
	plt.plot(t,rks[...,0],color='g',label='runge kutta 4')
	plt.xlim([ti,tf])
	plt.ylim([-2,2])
	plt.xlabel('t')
	plt.ylable('y')
	plt.title('Euler vs. Runge Kutta for $\ddot{y}+y=0$, h=%.4f'%(h))
	plt.legend(loc='lower left')


def plot_over_h(ti,tf,z0,hs):
	#for this ode, we know the solution is y=cos(t)
	ale_euler = []
	ale_rks = []
	end_euler = []
	end_rks = []

	for h in hs:
		n_steps = int((tf-ti)/h)
		t = linspace(ti,tf,n_steps)
		es = empty((n_steps,2))
		rks = empty((n_steps,2))
		euler_cython(f,z0,es,n_steps,h)
		runge_kutta_4_cython(f,z0,rks,n_steps,h)
		
		ale_euler.append( sum(absolute(es[...,0]-cos(t)))/n_steps )
		ale_rks.append( sum(absolute(rks[...,0]-cos(t)))/n_steps )
		end_euler.append(absolute(es[-1]-array([cos(tf),-sin(tf)])) )
		end_rks.append(absolute(rks[-1]-array([cos(tf),-sin(tf)])) )

	#average local error
	plt.figure()
	plt.loglog(hs,ale_euler,color='b', label='euler')
	plt.loglog(hs,ale_rks,color='g',label='runge kutta 4')
	plt.xlim([0,hs[-1]])
	plt.xlabel('h')
	plt.ylabel('average local error')
	plt.grid(True)
	plt.title('Average local error of Euler vs. Runge Kutta for $\ddot{y}+y=0$')
	plt.legend(loc='upper left')

	#ending value
	plt.figure()
	plt.loglog(hs,[x[0] for x in end_euler],color='b', label='euler')
	plt.loglog(hs,[x[0] for x in end_rks],color='g',label='runge kutta 4')
	plt.xlim([0,hs[-1]])
	plt.xlabel('h')
	plt.ylabel('ending local error')
	plt.grid(True)
	plt.title('Ending error of Euler vs. Runge Kutta for $\ddot{y}+y=0$')
	plt.legend(loc='upper left')

	#ending slope
	plt.figure()
	plt.loglog(hs,[x[1] for x in end_euler],color='b', label='euler')
	plt.loglog(hs,[x[1] for x in end_rks],color='g',label='runge kutta 4')
	plt.xlim([0,hs[-1]])
	plt.xlabel('h')
	plt.ylabel('ending local derivative error')
	plt.grid(True)
	plt.title('Ending derivative error of Euler vs. Runge Kutta for $\ddot{y}+y=0$')
	plt.legend(loc='upper left')

if __name__ == '__main__':
	ti = 0.
	tf = 100*pi
	h = .005
	z0 = array([1,0]) #z = (y,dy)
	
	#compare_fixed_h(ti,tf,h,z0)
	
	hs = logspace(-4, -1, num=12)
	plot_over_h(ti,tf,z0,hs)

	plt.show()		
