def test_EulerCromer_1dof(): """Plain u'' + u = 0.""" solver = odespy.EulerCromer(lambda v, t: [-v[1], v[0]]) #solver = odespy.EulerCromer(lambda u, t: [-u[1], u[0]]) solver.set_initial_condition([0, 1]) P = 2*np.pi N = 60 dt = P/N num_periods = 8 T = num_periods*P time_points = np.linspace(0, T, num_periods*N+1) u, t = solver.solve(time_points) x = u[:,1] v = u[:,0] x_exact = lambda t: np.cos(t) x_e = x_exact(t) #plot(t, x, t, x_e, legend=('EC', 'exact')) print('Testing EulerCromer') # Test difference in final value if N == 60: diff_exact = 0.0014700112828 diff_EC = abs(x[-1] - x_e[-1]) tol = 1E-14 assert abs(diff_exact - diff_EC) < tol, \ 'diff_exact=%g, diff_EulerCromer=%g' % (diff_exact, diff_EC) print('...ok')
def simulate( beta=0.9, Theta=30, epsilon=0, num_periods=6, time_steps_per_period=60, plot=True, ): from math import sin, cos, pi Theta = Theta*np.pi/180 #convert to radians #Initial position and velocity #(we order the equations such that Euler-Cromer in odespy #can be used, i.e., vx, x, vy, y) ic = [0, #x'=vx (1 + epsilon)*sin(Theta), #x 0, #y'=vy 1 - (1 + epsilon)*cos(Theta), #y ] def f(u, t, beta): vx, x, vy, y = u L = np.sqrt(x**2 + (y-1)**2) h = beta/(1-beta)*(1- beta/L) #help factor return [-h*x, vx, -h*(y-1) - beta, vy] #Non-elastic pendulum (scaled similarly in the limit beta=1) #Solution Theta*cos(t) P = 2*pi dt = P/time_steps_per_period T = num_periods*P omega = 2*pi/P time_points = np.linspace( 0, T, num_periods*time_steps_per_period+1) solver = odespy.EulerCromer(f, f_args=(beta,)) solver.set_initial_condition(ic) u, t = solver.solve(time_points) x = u[:,1] y = u[:,3] theta = np.arctan(x/(1-y)) if plot: plt.figure() plt.plot(x, y, 'b-', title='Pendulum motion', daspect=[1,1,1], daspectmode='equal', axis=[x.min(), x.max(), 1.3*y.min(), 1]) plt.savefig('tmp_xy.png') plt.savefig('tmp_xy.pdf') #plot theta in degrees plt.figure() plt.plot(t, theta*180/np.pi, 'b-', title='Angular displacement in degrees') plt.savefig('tmp_theta.png') plt.savefig('tmp_theta.pdf') if abs(Theta) < 10*pi/180: #compare theta and theta_e for small angles (< 10 degrees) theta_e = Theta*np.cos(omega*t) #non-elastic scaled sol. plt.figure() plt.plot(t, theta, t, theta_e, legend=['theta elastic', 'theta non-elastic'], title='Elastic vs non-elastic pendulum, '\ 'beta=%g' % beta) plt.savefig('tmp_compare.png') plt.savefig('tmp_compare.pdf') #Plot y vs x (the real physical motion) return x, y, theta, t
def f(u, t, m, k, L0): g = 9.81 vx, x, vy, y = u # mv' = -k*s*n + m*g*j, n: normal vector along the pendulum L = np.sqrt(x**2 + (y - L0)**2) s = L - L0 nx = x / L ny = (y - L0) / L #print 't=%g vx=%5.2f vy=%5.2f x=%5.2f, y=%5.2f, Fx=%5.2f, Fy=%5.2f' % (t, vx, vy, x, y, -k/m*s*nx, -k/m*s*ny - g) #print 'k=%g, s=%5.2g, ny=%5.2f, k/m*s*ny=%g, k*s*ny/m=%g' % (k,s,ny,k/m*s*ny,k*s*ny/m) return [-k / m * s * nx, vx, -k / m * s * ny - g, vy] solver = odespy.EulerCromer(f, f_args=(m, k, L0)) solver.set_initial_condition([0, x0, 0, y0]) # First test: vertical pendulum doing harmonic vertical motion, first # at rest, then slightly out of equilibrium # k and m should balance # For large k, this is theta'' + g/L0*sin(theta) = 0, so L0=g # implies theta'' + theta = 0 equation with theta0*cos(t) as solution P = 2 * np.pi N = 60 dt = P / N num_periods = 6 T = num_periods * P time_points = np.linspace(0, T, num_periods * N + 1) u, t = solver.solve(time_points) x = u[:, 1]
plt.savefig('vib_%d_%d_a.png' % (timesteps_per_period, num_periods)) # Define different sets of experiments solvers_theta = [ odespy.ForwardEuler(f), # Implicit methods must use Newton solver to converge odespy.BackwardEuler(f, nonlinear_solver='Newton'), odespy.CrankNicolson(f, nonlinear_solver='Newton'), ] solvers_RK = [odespy.RK2(f), odespy.RK4(f)] solvers_accurate = [odespy.RK4(f), odespy.CrankNicolson(f, nonlinear_solver='Newton'), odespy.DormandPrince(f, atol=0.001, rtol=0.02)] solvers_CN = [odespy.CrankNicolson(f, nonlinear_solver='Newton')] solvers_EC = [odespy.EulerCromer(f)] if __name__ == '__main__': # Default values timesteps_per_period = 20 solver_collection = 'theta' num_periods = 1 # Override from command line try: # Example: python vib_undamped_odespy.py 30 accurate 50 timesteps_per_period = int(sys.argv[1]) solver_collection = sys.argv[2] num_periods = int(sys.argv[3]) except IndexError: pass # default values are ok solvers = eval('solvers_' + solver_collection) # list of solvers
import odespy from numpy import * from matplotlib.pyplot import * def f(u, t): omega, theta = u return [-c * sin(theta), omega] c = 1 Theta0_degrees = 30 solver = odespy.EulerCromer(f) Theta0 = Theta0_degrees * pi / 180 solver.set_initial_condition([0, Theta0]) # Solve for num_periods periods using formulas for small theta freq = sqrt(c) # frequency of oscillations period = 2 * pi / freq # one period N = 40 # intervals per period dt = period / N # time step num_periods = 10 T = num_periods * period # total simulation time time_points = linspace(0, T, num_periods * N + 1) u, t = solver.solve(time_points) # Extract components and plot theta theta = u[:, 1] omega = u[:, 0] theta_linear = lambda t: Theta0 * cos(sqrt(c) * t)
def run_solver_and_plot(solver, timesteps_per_period=20, num_periods=1, I=1, w=2 * np.pi): P = 2 * np.pi / w # duration of one period dt = P / timesteps_per_period Nt = num_periods * timesteps_per_period T = Nt * dt t_mesh = np.linspace(0, T, Nt + 1) solver.set(f_kwargs={'w': w}) solver.set_initial_condition([0, I]) u, t = solver.solve(t_mesh) from vib_undamped import solver u2, t2 = solver(I, w, dt, T) plt.plot(t, u[:, 1], 'r-', t2, u2, 'b-') plt.legend(['Euler-Cromer', '2nd-order ODE']) plt.xlabel('t') plt.ylabel('u') plt.savefig('tmp1.png') plt.savefig('tmp1.pdf') run_solver_and_plot(odespy.EulerCromer(f), timesteps_per_period=20, num_periods=9) raw_input()