💻 Open in Colab
Ashwin Kharat
#-- This will reset the runtime and clear all variable.%reset -f#A new Begining
#-- importing all the necessary Librariesimport numpy as npfrom matplotlib import pyplot as pltfrom matplotlib import animation, rcimport ipywidgets as widgetsfrom IPython.display import display, HTML## --
#-- Custom functions to make life easy.# g3 represent Group 3# lable class creates a label object which can be used with new plot and multiPlot functions# lable format is#1.Legend of the plot that u want to make#2.X-axis label, 3. Y-axis label#3.style of line plotted#this can all be skipped and a default plot is made with the default label#newPlot makes a fresh new Figure and plot taking the g3_lable#formate is (x values , y values , g3_lable object )# multiPlot can be used repeatedly to plot on the same figure.#formate is (x values , y values , g3_lable object ).
class label(): def __init__(self, ll = None ,lx = 'x',ly ='y',style = '-'): self.ll = ll # the lable for legend self.lx = lx # x axis self.ly = ly # y axis self.style = style
class g3:
def newPlot(x, y, L = label() ): fig = plt.figure() easy = fig.add_subplot(111) easy.plot( x, y, L.style, label = L.ll ) easy.legend() plt.xlabel( L.lx ) plt.ylabel( L.ly )
def multiPlot(x, y, L = label() ): plt.plot( x, y, L.style, label = L.ll ,alpha=0.5) plt.legend() plt.xlabel( L.lx ) plt.ylabel( L.ly )#--##Problem 1 : Implementation of Euler and Euler-Cromer ODE.
Part A
Write a function for solving the Newton’s laws of motion using Euler and Euler-Cromer algorithms. As shown in class, this 2nd order ODE can be broken into a set of 2 coupled 1st order ODEs:
The function should take following as input:
a. a list or array containing initial condition for and i.e. .
b. a list or array with starting time and end time i.e. .
c. The function to calculate the right-hand side of the above equation. It can be defined with a Python function as follows:
and return a list containing the velocity and acceleration at time :
Hence the functional form for the should be as follows:
where is a list containing the initial conditions at and is the list containing the time-range to integrate the equations.
Make a similar function for Euler-Cromer algorithm .
Solution part A
# Just a BluePrint.
def rhs(xk,vk,tk):#This is the general definition for any 2nd order vectorised ODE.#we are calling another function 'a(xk,vk,tk)' which has the definition for acceleration written. return [vk , a(xk,vk,tk)]
def EulerODE(initCondn, tRange, rhs): # we could have given a value to dt but wanted user to have a control over it. dt = tRange[2] time = np.arange(tRange[0], tRange[1] + dt , dt)
position = np.zeros(len(time)) velocity = np.zeros(len(time))
position[0] = initCondn[0] velocity[0] = initCondn[1]
for i in range(0, len(time)-1): position[i+1] = position[i] + rhs(position[i],velocity[i], time[i] )[0] * dt velocity[i+1] = velocity[i] + rhs(position[i],velocity[i], time[i] )[1] * dt
return position, velocity
def EulerCromerODE(initCondn, tRange, rhs):
dt = tRange[2] time = np.arange(tRange[0], tRange[1] + dt , dt) position = np.zeros(len(time)) velocity = np.zeros(len(time))
position[0] = initCondn[0] velocity[0] = initCondn[1]
for i in range(0, len(time)-1): velocity[i+1] = velocity[i] + rhs(position[i],velocity[i], None )[1] * dt position[i+1] = position[i] + rhs(position[i+1],velocity[i+1], None )[0] * dt
return position, velocity# An example code is written for a spring mass system with the format given above.def a(xk,vk,t): mass = 1 #kg c = 0 # damping constant taking zero for simple case / demo purpose
k = 1 # spring constant return -1/mass*(c*vk + k* xk )# -- Initializationtime_0 = 0time_max = 50time_step = 1/10 # 10th part of a secondposition_0 = 5 # in meters difference from rest positionvelocity_0 = 0 # angular velocity#--
initCondn = [position_0, velocity_0]tRange = [time_0, time_max, time_step]dt = tRange[2]time = np.arange(tRange[0], tRange[1] + dt , dt)
# calling euler fn to solve for the given initial condition.position, velocity = EulerODE(initCondn, tRange, rhs)
lable = label('trajectory in phase space','position (m)','velocity ( m/s )','--')g3.newPlot(position, velocity,lable)plt.plot(position[0], velocity[0],'*r')#plt.legend()plt.title('Phase Space by Euler method')plt.show()
lable = label('time vs velocity ','time (sec)','velocity ( m/s ) ','--')g3.newPlot(time, velocity,lable)
lable = label('oscillation amplitude','time (sec)','position (m)','--')g3.newPlot(time, position ,lable)


Simulation of SHO : Euler.
x_data = []y_data = []l = 10 # length of unstretched spring.
fig, ax = plt.subplots(figsize=(6,6))ax = plt.axes(xlim=(-l, l*3),ylim=(-l*0.8, l*0.8 ))line, = ax.plot([], [])dot, = ax.plot([], [], 'sk', markersize = 20 )plt.plot( np.zeros(10),np.linspace(0,2,10), '-k')plt.plot( np.linspace(-10,30,10) ,np.zeros(10), '-b')
plt.xlabel('x (meter)')plt.ylabel('y (meter)')plt.title('Simulation using Euler method')
def animation_frame(i):
x_data = np.linspace(0, (l + position[i]), 50) temp = 0.5 * np.sin(x_data * (l*2*np.pi)/(l + position[i])) y_data = temp + 0.5 # just putting spring in place.
line.set_xdata(x_data) line.set_ydata(y_data)
dot.set_ydata([0.5]) dot.set_xdata([(l + position[i])])
return line, dot
# simple plot is just the initial condition.# Animination is a video.
animation_vid = animation.FuncAnimation(fig, func = animation_frame, frames = np.arange(0, len(time)-1 , 1) , interval= dt*1000 )HTML(animation_vid.to_html5_video())
The above numerical solution is using the euler method which is why we are getting an error. The solution shows that an extra energy is pumped into the system which is not.
position, velocity = EulerCromerODE(initCondn, tRange, rhs)
lable = label('trajectory in phase space','position (m)','velocity ( m/s )','--')g3.newPlot(position, velocity,lable)plt.plot(position[0], velocity[0],'*r')#plt.legend()plt.title('Phase Space by Euler Cromer method')plt.show()
lable = label('time vs velocity ','time (sec)','velocity ( m/s ) ','--')g3.newPlot(time, velocity,lable)
lable = label('oscillation amplitude','time (sec)','position (m)','--')g3.newPlot(time, position ,lable)


Here using the Euler cromer method we get the proper phase space trajectory and the energy of the system is conserved. Here the velocity and position shows an oscillatory variation with a phase difference of i.e. the two are in quadrature just as expected for a Simple harmonic oscillator.
Simulation of SHO: Euler Cromer.
x_data = []y_data = []l = 10 # length of unstretched spring.
fig, ax = plt.subplots(figsize=(6,6))ax = plt.axes(xlim=(-l, l*3),ylim=(-l*0.8, l*0.8 ))line, = ax.plot([], [])dot, = ax.plot([], [], 'sk', markersize = 20 )plt.plot( np.zeros(10),np.linspace(0,2,10), '-k')plt.plot( np.linspace(-10,30,10) ,np.zeros(10), '-b')
plt.xlabel('x (meter)')plt.ylabel('y (meter)')plt.title('Simulation using Euler cromer method')
def animation_frame(i):
x_data = np.linspace(0, (l + position[i]), 50) temp = 0.5 * np.sin(x_data * (l*2*np.pi)/(l + position[i])) y_data = temp + 0.5 # just puting spring in place.
line.set_xdata(x_data) line.set_ydata(y_data)
dot.set_ydata([0.5]) dot.set_xdata([l + position[i]])
return line, dot
# simple plot is just the initial condition# Animination is a video.
animation_vid = animation.FuncAnimation(fig, func = animation_frame, frames = np.arange(0, len(time)-1 , 1) , interval= dt*1000 )HTML(animation_vid.to_html5_video())
Part B
Use these functions to solve for the simple harmonic pendulum: with boundary conditions: at , . Note that the initial amplitude is quite large, so it is not really a simple harmonic oscillator.
Plot Vs and the phase space trajectory.
Explore the system for small oscillations and explain any qualitative difference from the previous case. (For this choose suitable boundary conditions).
Solution using Euler Method.
We are solving an ODE of the form
For a simple harmonic pendulum, the equation of motion is second-order ODE:
we can vectorize this as
\
The initial condition is given:
# Functions used in Part B solution.# the Functions are created using same principles from Part A.def rhs(theta, omega, time): g = 9.8 r = 1 k = g/r return [omega, -k * np.sin(theta)]
def EulerODE(initCondn, tRange, rhs):
dt = tRange[2] time = np.arange(tRange[0], tRange[1] + dt , dt) theta = np.zeros(len(time)) omega = np.zeros(len(time))
theta[0] = initCondn[0] omega[0] = initCondn[1]
for i in range(0, len(time)-1): theta[i+1] = theta[i] + rhs(theta[i],omega[i], None )[0] * dt omega[i+1] = omega[i] + rhs(theta[i],omega[i], None )[1] * dt
return theta, omega
def EulerCromerODE(initCondn, tRange, rhs):
dt = tRange[2] time = np.arange(tRange[0], tRange[1] + dt , dt) theta = np.zeros(len(time)) omega = np.zeros(len(time))
theta[0] = initCondn[0] omega[0] = initCondn[1]
for i in range(0, len(time)-1): omega[i+1] = omega[i] + rhs( theta[i], omega[i] , None )[1] * dt theta[i+1] = theta[i] + rhs( theta[i+1], omega[i+1] , None )[0] * dt return theta, omegatime_0 = 0time_max = 5time_step = 1/10 # 10th part of a second
theta_0 = np.pi/4omega_0 = 0 # angular velocity
initCondn = [theta_0, omega_0]tRange = [time_0, time_max, time_step]
dt = tRange[2]time = np.arange(tRange[0], tRange[1] + dt , dt)
theta, omega = EulerODE(initCondn, tRange, rhs)
lable = label('trajectory in phase space','angle (radian)','angular velocity (radian/s)','--')g3.newPlot(theta, omega,lable)
plt.plot(theta[0], omega[0],'*r')#plt.legend()plt.title('Phase Space by Euler method')plt.show()
lable = label(' ','time (sec)','angular velocity (radian / sec)','--')g3.newPlot(time, omega,lable)
lable = label(' ','time (sec)','theta (radian)','--')g3.newPlot(time, theta ,lable)plt.show()
plt.polar(theta, np.ones(len(theta)), 'g.')plt.title("all the angles taken by Pendulum")
#saving the data in dictionary form to use later for calculating energyeuelrSolution = {'theta' : theta, 'omega': omega}



We can see the angle taken by Pendulum are higher then the initial angle, which should not be posible. We get this error because of euler method , as it is just an approximation.
x_data = []y_data = []r = 1
fig, ax = plt.subplots(figsize=(6,6))ax = plt.axes(xlim=(-r*1.1, r*1.1 ), ylim=(-r*1.1, r*1.1))line, = ax.plot([], [])dot, = ax.plot([], [],'ro', markersize = 10 )plt.xlabel('x (meter)')plt.ylabel('y (meter)')plt.title("Simulation of Simple Pendulum: Euler Method")
def animation_frame(i): x_data = np.linspace(0, r * np.sin(theta[i]), 10) y_data = np.linspace(0, - r * np.cos(theta[i]) ,10 ) line.set_xdata(x_data) line.set_ydata(y_data)
dot.set_xdata([ r * np.sin(theta[i]) ]) dot.set_ydata([- r * np.cos(theta[i])])
return line, dot
animation_vid = animation.FuncAnimation(fig, func = animation_frame, frames = np.arange(0, len(time)-1 , 1) , interval= dt*1000 )HTML(animation_vid.to_html5_video())
####Solution using Euler-Cromer algorithm
time_0 = 0time_max = 5time_step = 1/10 # 10th part of a second
theta_0 = np.pi/4omega_0 = 0 # angular velocity
initCondn = [theta_0, omega_0]tRange = [time_0, time_max, time_step]
dt = tRange[2]time = np.arange(tRange[0], tRange[1] + dt , dt)
theta, omega = EulerCromerODE(initCondn, tRange, rhs)
lable = label('trajectory in phase space','angle (radian)','angular velocity (radian/s)','--')g3.newPlot(theta, omega,lable)
plt.plot(theta[0], omega[0],'*r')#plt.legend()plt.title('Phase Space by Euler method')plt.show()
lable = label(' ','time (sec)','angular velocity (radian / sec)','--')g3.newPlot(time, omega,lable)
lable = label(' ','time (sec)','theta (radian)','--')g3.newPlot(time, theta ,lable)plt.show()
plt.polar(theta, np.ones(len(theta)), 'g.')plt.title("all the angles taken by Pendulum")
#saving the data in dictionary form to use later for calculating energyeuelrCromerSolution = {'theta' : theta, 'omega': omega}



x_data = []y_data = []r = 1 # meter#length of pendulume rod
fig, ax = plt.subplots(figsize=(6,6))ax = plt.axes(xlim=(-r*1.1, r*1.1 ), ylim=(-r*1.1, r*1.1))line, = ax.plot(0, 0)dot, = ax.plot(0, 0,'ro', markersize = 10 )plt.xlabel('x (meter)')plt.ylabel('y (meter)')plt.title("Simulation of Simple Pendulum: Euler cromer Method")
def animation_frame(i): x_data = np.linspace(0, r * np.sin(theta[i]), 10) y_data = np.linspace(0, - r * np.cos(theta[i]) ,10 ) line.set_xdata(x_data) line.set_ydata(y_data)
dot.set_xdata([r * np.sin(theta[i])]) dot.set_ydata([- r * np.cos(theta[i])])
return line, dot
animation_vid = animation.FuncAnimation(fig, func=animation_frame, frames = np.arange(0, len(time)-1 , 1) , interval= dt*1000 )HTML(animation_vid.to_html5_video())
Part C
Plot the total energy as a function of time for both the methods. Notice any oscillations?
####Solution Energy
# befor running this make sure that the initial condition is the same for both Methods above.
mass = 1 # kgheight = radius = 1 # meterg = 9.8 #m/s
plt.figure(figsize=(7,5))
energy_euler = mass / 2 *( 0 +radius**2 * np.array(euelrSolution.get('omega'))**2 ) + mass * g * (height - radius*np.cos(euelrSolution.get('theta')))
energy_eulerCromer = mass / 2 *( 0 +radius**2 * np.array(euelrCromerSolution.get('omega'))**2 ) + mass * g * (height - radius* np.cos(euelrCromerSolution.get('theta')))
plt.plot(time, energy_euler, label='Euler')plt.plot(time, energy_eulerCromer, label='Euler-Cromer')plt.plot(time, np.zeros(len(time)), label='Zero Energy Level')
plt.legend()plt.xlabel('time (sec)')plt.ylabel('total energy (joule)')Text(0, 0.5, 'total energy (joule)')
The total energy of a system executing simple harmonic motion remains constant i.e. remains conserved. The oscillations seen in the graph are due to the term in the potential energy. It is observed that increasing the radius gives more oscillations in the total energy graph.
For small oscillations,
can be approximated to and as 1.
Hence the equation of motion will be:
##Problem 2: Exploring Liouville’s theorem with symplectic integrators
Part A
The Euler and Euler-Cromer algorithms define a map from the time steps. For a Simple Harmonic Oscillator (SHO), calculate the Jacobian of the transformation. For this, explain why Euler algorithm is not suitable for solving the Newton’s equations of motion?
####Solution
For a Simple Harmonic Oscillator, Euler algorithm is not suitable for solving Newton’s equations of motion because along with inducing error in the solution due to cancellation of higher-order differential terms, it does not conserve the energy of the system. Euler algorithm is unstable since the trajectory produced by the algorithm spirals continually outwards away from the exact solution. This means that the Euler algorithm does not conserve energy. In the SHO problem we saw that in the Euler method, the object’s maximum displacement and maximum speed are steadily increasing, so its total energy must also be increasing.
According to Liouville’s Theorem, a conservative system (such as simple harmonic oscillator) must preserve the volume (or area) occupied by an ensemble of trajectories in phase space. The system evolves with time and the cluster of points move through phase space but the region containing the points remains constant.
In the Euler method, this region in phase space grows in size with the passage of time.
For SHO, the transformation in the Euler method is given by the set of below equations:
If we treat the above equations as a two-dimensional map, then this map will preserve phase space volume if and only if the Jacobian of the map is equal to unity. The Jacobian of this transformation is given by:
Thus in contradiction to Liouville’s Theorem, the Euler algorithm does not preserve phase space volume and grows in size with time.
Hence it is not suitable for solving Newton’s equations of motion.
The transformation equations for SHO in the case of Euler-Cromer method are given by:
The Jacobian of this transformation is given by:
Hence Euler-Cromer method preserves the phase space volume and conserves energy for SHO system.
Part B
For an SHO, consider a small square element of phase-space area at whose corners are labelled A, B, C and D respectively. In this exercise, we would like to follow the area enclosed as the system evolves in time.
One way to do this is to evolve the four different initial conditions in time and plot the resultant polygon in the phase-space after regular intervals of time. For a conserved system, we expect this area conserved in time. For a symplectic solver (like Euler-Cromer with a global accuracy of ), this is true. However, this is not for the Euler algorithm.
Alternately, consider the trajectory of a cloud of points through phase space. Show that as the cloud stretches in one coordinate – say – it shrinks in the corresponding direction so that the product remains constant.
####Solution
def rhs(position, velocity, time):
#k is the spring constant k = 9.8 #N·m−1 m =1. #mass kg return [velocity, -k /m * position]
#just name a class to store all functions# this are the same definations frome above#for using the class/object method to make code simple.
# as the following code is written with generalized functions# the theta and omega can be and are used alternatively for position and velocity and vice-versa
class Algorithms(): def __init__(self, initCondn , tRange, rhs ): self.initCondn = initCondn self.tRange = tRange self.rhs = rhs
def EulerODE(self):
dt = self.tRange[2] time = np.arange(self.tRange[0], self.tRange[1] + dt , dt) theta = np.zeros(len(time)) omega = np.zeros(len(time))
theta[0] = self.initCondn[0] omega[0] = self.initCondn[1]
for i in range(0, len(time)-1): theta[i+1] = theta[i] + self.rhs(theta[i],omega[i], None )[0] * dt omega[i+1] = omega[i] + self.rhs(theta[i],omega[i], None )[1] * dt
return theta, omega
def EulerCromerODE(self):
dt = self.tRange[2] time = np.arange(self.tRange[0], self.tRange[1] + dt , dt) theta = np.zeros(len(time)) omega = np.zeros(len(time))
theta[0] = self.initCondn[0] omega[0] = self.initCondn[1]
for i in range(0, len(time)-1): omega[i+1] = omega[i] + self.rhs( theta[i], omega[i] , None )[1] * dt theta[i+1] = theta[i] + self.rhs( theta[i+1], omega[i+1] , None )[0] * dt return theta, omegaEuler Method
time_0 = 0time_max = 5time_step = 1/10 # 10th part of a second
position = 0.3 # metersvelocity = 0.3 # m/sdp = 0.1
#all the values are sored in dictionary.phaseSpace = {}
#giving initial values to A,B,C,Dposition_0 = positionvelocity_0 = 0AinitCondn = [position_0, velocity_0]
position_0 = 2*positionvelocity_0 = 0BinitCondn = [position_0, velocity_0]
position_0 = 2 * positionvelocity_0 = 1 * velocityCinitCondn = [position_0, velocity_0]
position_0 = 1*positionvelocity_0 = 1*velocityDinitCondn = [position_0, velocity_0]
tRange = [time_0, time_max, time_step]dt = tRange[2]time = np.arange(tRange[0], tRange[1] + dt , dt)
A = Algorithms(AinitCondn, tRange, rhs)B = Algorithms(BinitCondn, tRange, rhs)C = Algorithms(CinitCondn, tRange, rhs)D = Algorithms(DinitCondn, tRange, rhs)
# all lines below are just to make the plots.fig = plt.figure()
lable = label('A','position (meters)','velocity (m/s)','o')phaseSpace['A_theta'],phaseSpace['A_omega'] = theta, omega = A.EulerODE()g3.multiPlot(theta, omega,lable)
lable = label('B','position (meters)','velocity (m/s)','o')phaseSpace['B_theta'],phaseSpace['B_omega'] = theta, omega = B.EulerODE()g3.multiPlot(theta, omega,lable)
lable = label('C','position (meters)','velocity (m/s)','o')phaseSpace['C_theta'],phaseSpace['C_omega'] = theta, omega = C.EulerODE()g3.multiPlot(theta, omega,lable)
lable = label('D','position (meters)','velocity (m/s)','o')phaseSpace['D_theta'],phaseSpace['D_omega'] = theta, omega = D.EulerODE()g3.multiPlot(theta, omega,lable)
skip = 5x = []y = []areaE = list()fig = plt.figure()#plt.axes(xlim=(-5,5), ylim=(-5,5))plt.figsize=(10,10)
#This follow definations is to find are of any polygon.#Only this folling 2 lines taken from online Q/A source (stackoverflow).def PolyArea(x,y): return 0.5*np.abs(np.dot(x,np.roll(y,1))-np.dot(y,np.roll(x,1)))#---
for i in range(0,len(phaseSpace['A_theta']), skip): x = [phaseSpace['A_theta'][i],phaseSpace['B_theta'][i],phaseSpace['C_theta'][i],phaseSpace['D_theta'][i],phaseSpace['A_theta'][i]] y = [phaseSpace['A_omega'][i],phaseSpace['B_omega'][i],phaseSpace['C_omega'][i],phaseSpace['D_omega'][i],phaseSpace['A_omega'][i]] areaE.append(PolyArea(x[:4],y[:4])) plt.plot(x,y,'--r') plt.plot(x,y,'*b')
plt.xlabel('position (meters)')plt.ylabel('velocity (m/s)')plt.title('area elements')plt.show()
skip = 5for i in range(0,len(phaseSpace['A_theta']), skip): x = [phaseSpace['A_theta'][i],phaseSpace['B_theta'][i],phaseSpace['C_theta'][i],phaseSpace['D_theta'][i],phaseSpace['A_theta'][i]] y = [phaseSpace['A_omega'][i],phaseSpace['B_omega'][i],phaseSpace['C_omega'][i],phaseSpace['D_omega'][i],phaseSpace['A_omega'][i]] plt.fill(x[:4],y[:4])
lable = label('A','position (meters)','velocity (m/s)','o')phaseSpace['A_theta'],phaseSpace['A_omega'] = theta, omega = A.EulerODE()g3.multiPlot(theta, omega,lable)
lable = label('B','position (meters)','velocity (m/s)','o')phaseSpace['B_theta'],phaseSpace['B_omega'] = theta, omega = B.EulerODE()g3.multiPlot(theta, omega,lable)
lable = label('C','position (meters)','velocity (m/s)','o')phaseSpace['C_theta'],phaseSpace['C_omega'] = theta, omega = C.EulerODE()g3.multiPlot(theta, omega,lable)
lable = label('D','position (meters)','velocity (m/s)','o')phaseSpace['D_theta'],phaseSpace['D_omega'] = theta, omega = D.EulerODE()g3.multiPlot(theta, omega,lable)
plt.title('Phase Space with area element: euler method')plt.show()


Euler Cromer Method
lable = label('A','position (meters)','velocity (m/s)','o')phaseSpace['A_theta'],phaseSpace['A_omega'] = theta, omega = A.EulerCromerODE()g3.multiPlot(theta, omega,lable)
lable = label('B','position (meters)','velocity (m/s)','o')phaseSpace['B_theta'],phaseSpace['B_omega'] = theta, omega = B.EulerCromerODE()g3.multiPlot(theta, omega,lable)
lable = label('C','position (meters)','velocity (m/s)','o')phaseSpace['C_theta'],phaseSpace['C_omega'] = theta, omega = C.EulerCromerODE()g3.multiPlot(theta, omega,lable)
lable = label('D','position (meters)','velocity (m/s)','o')phaseSpace['D_theta'],phaseSpace['D_omega'] = theta, omega = D.EulerCromerODE()g3.multiPlot(theta, omega,lable)
skip = 5x = []y = []areaEC = list()fig = plt.figure()plt.axes(xlim=(-2,2), ylim=(-2,2))plt.figsize=(10,10)
def PolyArea(x,y): return 0.5*np.abs(np.dot(x,np.roll(y,1))-np.dot(y,np.roll(x,1)))
for i in range(0,len(phaseSpace['A_theta']), skip): x = [phaseSpace['A_theta'][i],phaseSpace['B_theta'][i],phaseSpace['C_theta'][i],phaseSpace['D_theta'][i],phaseSpace['A_theta'][i]] y = [phaseSpace['A_omega'][i],phaseSpace['B_omega'][i],phaseSpace['C_omega'][i],phaseSpace['D_omega'][i],phaseSpace['A_omega'][i]] areaEC.append(PolyArea(x[:4],y[:4])) plt.plot(x,y,'--r') plt.plot(x,y,'*b')plt.xlabel('position (meters)')plt.ylabel('velocity (m/s)')plt.title('area elements')plt.show()for i in range(0,len(phaseSpace['A_theta']), skip): x = [phaseSpace['A_theta'][i],phaseSpace['B_theta'][i],phaseSpace['C_theta'][i],phaseSpace['D_theta'][i],phaseSpace['A_theta'][i]] y = [phaseSpace['A_omega'][i],phaseSpace['B_omega'][i],phaseSpace['C_omega'][i],phaseSpace['D_omega'][i],phaseSpace['A_omega'][i]] plt.fill(x[:4],y[:4])
lable = label('A','position (meters)','velocity (m/s)','o')phaseSpace['A_theta'],phaseSpace['A_omega'] = theta, omega = A.EulerCromerODE()g3.multiPlot(theta, omega,lable)
lable = label('B','position (meters)','velocity (m/s)','o')phaseSpace['B_theta'],phaseSpace['B_omega'] = theta, omega = B.EulerCromerODE()g3.multiPlot(theta, omega,lable)
lable = label('C','position (meters)','velocity (m/s)','o')phaseSpace['C_theta'],phaseSpace['C_omega'] = theta, omega = C.EulerCromerODE()g3.multiPlot(theta, omega,lable)
lable = label('D','position (meters)','velocity (m/s)','o')phaseSpace['D_theta'],phaseSpace['D_omega'] = theta, omega = D.EulerCromerODE()g3.multiPlot(theta, omega,lable)
plt.title('Phase Space with area element : Eueler Cromer method')plt.show()


#The area is not calculated for all elements just for the coloured ones above.plt.plot(range(len(areaEC)),areaEC,'-g',label ="Euler Cromer , max = {0} m^2".format(round(max(areaEC),2)))plt.plot(range(len(areaE)),areaE,'--r',label ="Euler, max = {0} m^2".format(round(max(areaE),2)))plt.legend()plt.xlabel('area element taken as time passes')plt.ylabel('area (m^2)')plt.show()
The area elements generated by choosing four initial conditions and evolving (numerically solving) in time results in an increasing area when the Euler method is used. and when we use the Euler cromer method the area is conserved.
##Problem 3: Linearly-damped harmonic oscillator
In the real world, all classical harmonic oscillators have some form of damping that converts their mechanical energy into heat and eventually brings the system to rest. The simplest way to represent such an effect is to add a linear term to the simple harmonic oscillator corresponding to a frictional force that is proportional to the velocity, \begin{eqnarray} \dot{x} &=& v, \ \dot{v} &=& -x. \end{eqnarray}
The resulting system is called a damped harmonic oscillator, and b (if positive) is the damping constant. It describes the exponential rate at which orbits spiral into the origin of the phase-space at and is related to the (quality factor) of the oscillator by . The of an oscillator is the number of radians of oscillation required for the energy to decay to of its original value. A system with (or ) is critically damped, and smaller values of (or ) do not oscillate, but they rapidly approach the origin.
It seems that it should be possible to eliminate the in above by a linear rescaling of , , and , but that cannot be done as simple algebra shows.
This system is special in that it has two distinct time-scales, the damping rate and the frequency of oscillation, and the parameter controls their ratio.
Write a code to plot , and phase-space trajectories for different values of the quality factor (demonstrating underdamped, critically damped and overdamped behaviour of this oscillator). Use both the Euler and Euler-Cromer algorithms. Write down an expression for the determinant of the Jacobian that maps in both the cases.
The damped harmonic oscillator is an example of a dissipative system. For is it time-reversible? Explain your answer.
SOLUTION:
We are solving
We can vectorize it in the form
Explicitly:
For Damped SHO, the tranformation in Euler method is given by the set of below equations:
The Jacobian of this transformation is given by:
The transformation equations for SHO in the case of Euler-Cromer method are given by:
The Jacobian of this transformation is given by:
Hence we can say that Euler-Cromer does not preserve the energy for damped oscillator system due to which we need to use other numerical methods to get a more accurate result.
The solution is an exponentially decaying with time. Reversing time will make it an exponentially growing function.
Hence we can say that it is not time reversible.
# This is the defination for SHO with drag.def a(xk,vk,t,): mass = 1 #kg k = 1 # spring constant b = 1/quality_factor # ve are varing value of b return -1/mass* k* xk - b * vk
# just the same definitions above again to make each solution self compiles
def rhs(xk,vk,tk):
return [vk, a(xk,vk,tk)]
def EulerODE(initCondn, tRange, rhs):
dt = tRange[2] time = np.arange(tRange[0], tRange[1] + dt , dt)
position = np.zeros(len(time)) velocity = np.zeros(len(time))
position[0] = initCondn[0] velocity[0] = initCondn[1]
for i in range(0, len(time)-1): position[i+1] = position[i] + rhs(position[i],velocity[i], None )[0] * dt velocity[i+1] = velocity[i] + rhs(position[i],velocity[i], None )[1] * dt
return position, velocity
def EulerCromerODE(initCondn, tRange, rhs):
dt = tRange[2] time = np.arange(tRange[0], tRange[1] + dt , dt) position = np.zeros(len(time)) velocity = np.zeros(len(time))
position[0] = initCondn[0] velocity[0] = initCondn[1]
for i in range(0, len(time)-1): velocity[i+1] = velocity[i] + rhs(position[i],velocity[i], None )[1] * dt position[i+1] = position[i] + rhs(position[i+1],velocity[i+1], None )[0] * dt
return position, velocitytime_0 = 0time_max = 50time_step = 1/10 # 10th part of a second
position_0 = 5 # in meters difference from rest positionvelocity_0 = 0 # angular velocity
initCondn = [position_0, velocity_0]tRange = [time_0, time_max, time_step]
dt = tRange[2]time = np.arange(tRange[0], tRange[1] + dt , dt)
quality_factor = 1position1, velocity1 = EulerODE(initCondn, tRange, rhs)
quality_factor = 2position2, velocity2 = EulerODE(initCondn, tRange, rhs)
quality_factor = 3position3, velocity3 = EulerODE(initCondn, tRange, rhs)
lable = label('Q = 1','angle(rad)','Angular velocity (rad/s)','--')g3.newPlot(position1, velocity1,lable)lable = label('Q = 2','angle(rad)','Angular velocity (rad/s)','--')g3.multiPlot(position2, velocity2,lable)lable = label('Q = 3','angle(rad)','Angular velocity (rad/s)','--')g3.multiPlot(position3, velocity3,lable)plt.plot(position_0,velocity_0,'*r')
lable = label('Q = 1','time(s)','Angular velocity (rad/s)','--')g3.newPlot(time, velocity1,lable)lable = label('Q = 2','time(s)','Angular velocity (rad/s)','--')g3.multiPlot(time, velocity2,lable)lable = label('Q = 3','time(s)','Angular velocity (rad/s)','--')g3.multiPlot(time, velocity3,lable)
lable = label('Q = 1','time(s)','angle(rad)','--')g3.newPlot(time, position1,lable)lable = label('Q = 2','time(s)','angle(rad)','--')g3.multiPlot(time, position2,lable)lable = label('Q = 3','time(s)','angle(rad)','--')g3.multiPlot(time, position3,lable)


#####Interactive solutions
def a(xk,vk,t): mass = mass_i.value #kg k = spring_const_i.value # spring constant b = 1/quality_factor_i.value return -1/mass* k* xk - b * vk
time_0 = 0time_max = 20time_step = 1/10 # 10th part of a secondtRange = [time_0, time_max, time_step]dt = tRange[2]time = np.arange(tRange[0], tRange[1] + dt , dt)
#--# Defining the variables.
#definations made for interactive plot.def update_plot(mass,spring_const,quality_factor,position_0):
initCondn = [position_0, velocity_0] position, velocity = EulerODE(initCondn, tRange, rhs) position_EC, velocity_EC = EulerCromerODE(initCondn, tRange, rhs)
L = label('for EulerODE Q = {0}'.format(quality_factor),'Position (meters)','velocity (meters/sec)','-r') g3.multiPlot(position, velocity, L ) plt.plot(position[0], velocity[0], '*r' )
L = label('for EulerCromerODE Q = {0}'.format(quality_factor),'Position (meters)','velocity (meters/sec)','-k') g3.multiPlot(position_EC, velocity_EC, L )
#making slidermass_i = widgets.FloatSlider(min=0, max=10, value=1,step = 0.5 , description = 'Mass (kg) : ')spring_const_i = widgets.FloatSlider(min=0, max=10, value=1,step = 0.5 , description = 'Spring Constant : ')quality_factor_i = widgets.FloatSlider(min=0.1, max=3, value=1,step = 0.2 , description = 'Quality factor : ')position_0_i = widgets.FloatSlider(min=0, max=10, value=1,step = 0.5 , description = 'Initial position from rest (meters) : ')#Starting the intractive sessionwidgets.interactive(update_plot,mass = mass_i, spring_const =spring_const_i ,quality_factor = quality_factor_i,position_0=position_0_i)interactive(children=(FloatSlider(value=1.0, description='Mass (kg) : ', max=10.0, step=0.5), FloatSlider(valu…The End
To Open Lab 3 https://github.com/ashwin-r-k/Computational-Physics_3labs/blob/main/LAB_3.ipynb