def eulerstep(phi, qn, vn, Dt, M):
    """
    This function takes one timestep across a set of equations dq/dt = v, 
    dv/dt = -dphi(q)/dq using the Euler method, where q is a generalised 
    spatial coordinate, v the velocity, t is time and phi is the potential.
    
    Parameters    
        phi: A function defining the potential of the system.
        qn: The initial spatial coordinate at the start of the timestep.
        vn: The initial velocity at the start of the timestep.
        Dt: The length of the time step.
        M: The mass of the particle acting under a force.
    
    Returns
        The function returns the value of the spatial coordinate and velocity 
        after taking a timestep of length Dt using the Euler method.
    """
    qn1 = qn + Dt*vn
    # ensuring h is an exact machine represenable number (NRiC)
    # note using the symmetrized for of the numerical derivative (see NRiC)
    # may want to consider Richardson extrapolation for serious work, again see
    # NRiC pp. 231
#    vn1 = vn - Dt*((phi(qn + h) - phi(qn - h))/(2*h))
    useRidders = True
    if useRidders:
        dphidq, err = deriv.derivative(phi, qn, np.abs(qn1 - qn), useRidders)
    else:
        dphidq = deriv.derivative(phi, qn, np.abs(qn1 - qn), useRidders)
    vn1 = vn - (1/M)*Dt*dphidq
    return qn1, vn1
def rk4step(phi, qn, vn, Dt, M):
    """
    This function takes one timestep across a set of equations dq/dt = v, 
    M*dv/dt = -dphi(q)/dq using a fourth order Runge-Kutta method, where q is a 
    generalised spatial coordinate, v the velocity, t is time and phi is the 
    potential.
    
    Parameters
        phi: A function defining the potential of the system.
        qn: The initial spatial coordinate at the start of the timestep.
        vn: The initial velocity at the start of the timestep.
        Dt: The length of the time step.
        M: The mass of the particle acting under a force.
    
    Returns
        The function returns the value of the spatial coordinate and velocity 
        after taking a timestep of length Dt using a fourth order Runge-Kutta 
        method.
    """    
    # Z1
    Q1 = qn
    V1 = vn        
    dphidQ1, err = deriv.derivative(phi, Q1, np.abs(Dt*vn))
    # Z2
    Q2 = qn + 0.5*Dt*V1
    V2 = vn - 0.5*Dt*1.0/M*dphidQ1
    dphidQ2, err = deriv.derivative(phi, Q2, np.abs(Dt*vn))
    # Z3
    Q3 = qn + 0.5*Dt*V2
    V3 = vn - 0.5*Dt*1.0/M*dphidQ2
    dphidQ3, err = deriv.derivative(phi, Q3, np.abs(Dt*vn))
    # Z4
    Q4 = qn + Dt*V3
    V4 = vn - Dt*1.0/M*dphidQ3
    dphidQ4, err = deriv.derivative(phi, Q4, np.abs(Dt*vn))
    # final step
    qn1 = qn + Dt/6.0*(V1 + 2.0*V2 + 2.0*V3 + V4)
    vn1 = vn - Dt/(6.0*M)*(dphidQ1 + 2.0*dphidQ2 + 2.0*dphidQ3 + dphidQ4)
    
    return qn1, vn1
def eulerBstep(phi, qn, vn, Dt, M):
    """
    This function takes one timestep across a set of equations dq/dt = v,
    M*dv/dt = -dphi(q)/dq using the Euler B method, where q is a generalised
    spatial coordinate, v the velocity, t is time and phi is the potential.

    Parameters
        phi: A function defining the potential of the system.
        qn: The initial spatial coordinate at the start of the timestep.
        vn: The initial velocity at the start of the timestep.
        Dt: The length of the time step.
        M: The mass of the particle acting under a force.

    Returns
        The function returns the value of the spatial coordinate and velocity
        after taking a timestep of length Dt using the Euler B method.
    """
    dphidq, err = deriv.derivative(phi, qn, np.abs(Dt*vn))
    vn1 = vn + (1/M)*Dt*dphidq
    qn1 = qn - Dt*vn1
    return qn1, vn1
e2norm = np.zeros(N + 1, dtype = np.float64)
energyError = np.zeros(N + 1, dtype = np.float64)
phi = np.zeros(N + 1, dtype = np.float64)
dphidq_analytic = np.zeros(N + 1, dtype = np.float64)
dphidq_numeric = np.zeros(N + 1, dtype = np.float64)

# initial conditions
q[0] = 1.9
v[0] = -0.0001
energyConst = 0.5*v[0]  + LJ(q[0])

# calcualte phase space
for ii in range(0, N):
    t[ii + 1] = t[ii] + Dt
    q[ii + 1], v[ii + 1] = step.eulerstep(LJ, q[ii], v[ii], Dt, M)
    dphidq = -deriv.derivative(LJ, q[ii], np.abs(q[ii + 1] - q[ii]), False)[0]
    d2phidq2 = -deriv.derivative(LJdiff, q[ii], np.abs(q[ii + 1] - q[ii]), False)[0]
    a[ii] = np.max([1, d2phidq2])
    E = 0.5*v[ii]**2 + LJ(q[ii])
    b[ii] = 0.5*np.sqrt(dphidq**2 + 2*d2phidq2**2*(E - LJ(q[ii])))
    energyError[ii] = E - energyConst
    
# plot
plt.figure(1)
plt.clf()
ax1 = plt.subplot2grid((3, 2), (0, 0), rowspan = 3)
ax1.plot(q, v)
plt.title('Phase Plot')
plt.xlabel('Generalised Coordainte, $q$')
plt.ylabel('Generalised Velocity, $v(q)$')
plt.axis([0.5, 2.5, -0.5, 0.5])