I am running the code below to build a one-dimensional array for each z and t. At the present moment, I am trying to make their sizes equivalent so that they each have a length of 501.
import numpy as np
#constants & parameters
omega = 1.
eps = 1.
c = 3.*(10.**8.)
hbar = 1.
eta = 0.01
nn = 10.**7.
n = eta*nn
lambdaOH = c/(1612.*10.**(6.))
gamma = 1.282*(10.**(-11.))
Tsp = 1./gamma
TR = 604800.
L = (Tsp/TR)*(np.pi)/((3.*(lambdaOH**2.))*n)
#time
Ngridt = 500.
tmax = 1.
dt = tmax/Ngridt
intervalt = tmax/dt + 1
t = np.linspace(0.01,tmax,intervalt)
#z space
Ngridz = 500.
zmax = L
dz = zmax/Ngridz
intervalz = zmax/dz + 1
z = np.linspace(0.01,zmax,intervalz)
When running the code, both intervalt and intervalz equal 501.0, but when checking the length of both z and t, len(z) = 500 while len(t) = 501. I have played around with the code above to yield len(z) = 501 by modifying certain parts. For example, if I insert the code
zmax = int(zmax)
then len(z) = 501. But I am wondering why the initial code, exactly as written, does not yield an array z with length 501?
(I am using Python 2.7.)
It is a problem of rounding. If you try to subtract 501 from intervalz you will find a very small negative number, -5.68e-14; linspace just takes the integer part of it, that is 500, and provides a 500-long list.
Notice two other problems with your code:
dt does not provide the correct spacing because you don't remove the initial t (same for dz)
Ngridt and Ngridz are conceptually integers, while you initialize them as floats. Just remove the dot at the end.
I think that your code could be simplified by writing (notice that Ngridt and Ngridz are initialized to 501)
#time
Ngridt = 501
tmax = 1.
t, dt = np.linspace(0.01,tmax,Ngridt,retstep=True)
#z space
Ngridz = 501
zmax = L
z, dz = np.linspace(0.01,zmax,Ngridz,retstep=True)
This is related to float arithmetic inaccuracy. It so happens that the formula for intervalz yields 500.99999999999994. This is just floating accuracy issue you can find all over SO. The np.linspace command then takes this number as 500 and not as 501.
Since linspace expects an int it is better to make sure you give it one.
BTW: mathematically speaking I don't see why you don't set
intervalz = Ngridz + 1
since intervalz = zmax/dz + 1 = zmax/(zmax/Ngridz) + 1 = Ngridz + 1
Related
I am implemententing the Verlet algorithm for a double well potential V(x) = x^4-20x^2, as to create a simple phase portrait.The generated phase portrait has an augmented oval shape and is clearly incorrect. I have a feeling that my problem is occurring in my definition of the of x^3 but I am not sure. I have also included the algorithm for a classical harmonic oscillator to show that my code works correctly.
import numpy as np
import matplotlib.pyplot as plt
###Constants
w = 2
m=1
N=500
dt=0.05
t = np.linspace(0, N*dt, N+1)
np.shape(t)
x = np.zeros(N+1)
p = np.zeros(N+1)
p_0 = 0
x_0 = 1
x[0] = x_0
p[0] = p_0
#Velocity Verlet Tuckerman
#x(dt) = x(0) +p(0)/m*dt + 1/(2m) * F(x(0))
#p(dt = p(0) + dt/2[F(x(0)) + F(x(dt))]
#Harmonic Oscillator F(x) = -kx = -mw^2x
for n in range(N):
x[n+1] = x[n] + (p[n]/m)*dt - (0.5)*w**2*x[n]*dt*dt
p[n+1] = p[n] - m*(0.5)*w**2*x[n]*dt - m*0.5*w**2*x[n+1]*dt
plt.plot(x,p)
#Symmetric Double Well: F(x) = -4x^3 + 40x
#V(x) = x^4 -20x^2
for n in range(N):
x[n+1] = x[n] + (p[n]/m)*dt +1/(2*m)*( -4*(x[n]*x[n]*x[n])*dt*dt +40*x[n]*dt*dt)
p[n+1] = p[n] + (1/2)*(-4*m*(x[n]*x[n]*x[n])*dt +40*m*x[n]*dt - 4*m*(x[n+1]*x[n+1]*x[n+1])*dt +40*m*x[n+1]*dt)
plt.plot(x,p)
Thanks!
To be more precise, V has a minimum at x=+-sqrt(10) with value -100, the local maximum at x=0 gives value 0. The initial position x0=1, v0=0 places the solution in the right valley, oscillating around sqrt(10).
To get a figure-8 shape you need an initial point with V(x0) slightly larger than zero. For instance with x0=5 one gets V=25*(25-20)=125. Or take x0=4.5 ==> x0^2=20.25 ==> V ~ 5.
I am currently trying to write some python code to solve an arbitrary system of first order ODEs, using a general explicit Runge-Kutta method defined by the values alpha, gamma (both vectors of dimension m) and beta (lower triangular matrix of dimension m x m) of the Butcher table which are passed in by the user. My code appears to work for single ODEs, having tested it on a few different examples, but I'm struggling to generalise my code to vector valued ODEs (i.e. systems).
In particular, I try to solve a Van der Pol oscillator ODE (reduced to a first order system) using Heun's method defined by the Butcher Tableau values given in my code, but I receive the errors
"RuntimeWarning: overflow encountered in double_scalars f = lambda t,u: np.array(... etc)" and
"RuntimeWarning: invalid value encountered in add kvec[i] = f(t+alpha[i]*h,y+h*sum)"
followed by my solution vector that is clearly blowing up. Note that the commented out code below is one of the examples of single ODEs that I tried and is solved correctly. Could anyone please help? Here is my code:
import numpy as np
def rk(t,y,h,f,alpha,beta,gamma):
'''Runga Kutta iteration'''
return y + h*phi(t,y,h,f,alpha,beta,gamma)
def phi(t,y,h,f,alpha,beta,gamma):
'''Phi function for the Runga Kutta iteration'''
m = len(alpha)
count = np.zeros(len(f(t,y)))
kvec = k(t,y,h,f,alpha,beta,gamma)
for i in range(1,m+1):
count = count + gamma[i-1]*kvec[i-1]
return count
def k(t,y,h,f,alpha,beta,gamma):
'''returning a vector containing each step k_{i} in the m step Runga Kutta method'''
m = len(alpha)
kvec = np.zeros((m,len(f(t,y))))
kvec[0] = f(t,y)
for i in range(1,m):
sum = np.zeros(len(f(t,y)))
for l in range(1,i+1):
sum = sum + beta[i][l-1]*kvec[l-1]
kvec[i] = f(t+alpha[i]*h,y+h*sum)
return kvec
def timeLoop(y0,N,f,alpha,beta,gamma,h,rk):
'''function that loops through time using the RK method'''
t = np.zeros([N+1])
y = np.zeros([N+1,len(y0)])
y[0] = y0
t[0] = 0
for i in range(1,N+1):
y[i] = rk(t[i-1],y[i-1], h, f,alpha,beta,gamma)
t[i] = t[i-1]+h
return t,y
#################################################################
'''f = lambda t,y: (c-y)**2
Y = lambda t: np.array([(1+t*c*(c-1))/(1+t*(c-1))])
h0 = 1
c = 1.5
T = 10
alpha = np.array([0,1])
gamma = np.array([0.5,0.5])
beta = np.array([[0,0],[1,0]])
eff_rk = compute(h0,Y(0),T,f,alpha,beta,gamma,rk, Y,11)'''
#constants
mu = 100
T = 1000
h = 0.01
N = int(T/h)
#initial conditions
y0 = 0.02
d0 = 0
init = np.array([y0,d0])
#Butcher Tableau for Heun's method
alpha = np.array([0,1])
gamma = np.array([0.5,0.5])
beta = np.array([[0,0],[1,0]])
#rhs of the ode system
f = lambda t,u: np.array([u[1],mu*(1-u[0]**2)*u[1]-u[0]])
#solving the system
time, sol = timeLoop(init,N,f,alpha,beta,gamma,h,rk)
print(sol)
Your step size is not small enough. The Van der Pol oscillator with mu=100 is a fast-slow system with very sharp turns at the switching of the modes, so rather stiff. With explicit methods this requires small step sizes, the smallest sensible step size is 1e-5 to 1e-6. You get a solution on the limit cycle already for h=0.001, with resulting velocities up to 150.
You can reduce some of that stiffness by using a different velocity/impulse variable. In the equation
x'' - mu*(1-x^2)*x' + x = 0
you can combine the first two terms into a derivative,
mu*v = x' - mu*(1-x^2/3)*x
so that
x' = mu*(v+(1-x^2/3)*x)
v' = -x/mu
The second equation is now uniformly slow close to the limit cycle, while the first has long relatively straight jumps when v leaves the cubic v=x^3/3-x.
This integrates nicely with the original h=0.01, keeping the solution inside the box [-3,3]x[-2,2], even if it shows some strange oscillations that are not present for smaller step sizes and the exact solution.
I am writing code for summing the Fourier Series that ranges from [-n,n]. However, I'm having trouble with it iterating when it gets to n = 0. I wrote an 'if' statement inside my while loop so it can ignore it, but it seems like it isn't. Here's my code:
from __future__ import division
import numpy as np
import math
import matplotlib.pyplot as plt
#initial values
ni = -10
nf = 10
ti = -3
tf = 3
dt = 0.01
yi = 0 #initial f(t) value
j = complex(0,1)
#initialization
tarray = [ti]
yarray = [yi]
t = ti
n = ni
y = yi
cn = 1/(8*(np.pi)**3*n**3*j**3)*(j*4*np.pi*n) #part (b)
#iterating loop
while t<tf:
n = ni
y = yi
while n<nf:
if n == 0:
cn = 1/6
y += cn
n += 1
else:
y += cn*np.exp(j*np.pi*n*t)
n += 1
yarray.append(y)
t+=dt
tarray.append(t)
#converting list-array
tarray = np.array(tarray)
yarray = np.array(yarray)
#plotting
plt.plot(tarray,yarray, linewidth = 1)
plt.axis("tight")
plt.xlabel('t')
plt.ylabel('f(t) upto n partial sums')
plt.title('Fourier Series for n terms')
plt.legend()
plt.show()
I want it to iterate and create an array of y-values for n ranging from some negative number to some positive number (say for n from [-10,10]), but as soon as it hits n = 0 it seems to be plugging that in into the 'else' clause even though I want it to use what's in the 'if' clause, giving me a "ZeroDivisionError: complex division by zero". How do I fix this?
Edit: Put the entire code block here so you can see the context.
This is not the most elegant way at all but try this:
while t<tf:
n = ni
y = yi
while n<nf:
try:
1/n
cn = 1/6
y += cn
n += 1
except ZeroDivisionError:
y += cn*np.exp(j*np.pi*n*t) #1/n*np.sin(n*t)
n += 1
yarray.append(y)
t+=dt
tarray.append(t)
The coefficient cn is a function of n and should be updated in every loop. You made it constant (and even equal to 1/6 for positive n).
The inner loop could look like
y = 1/6 # starting with n = 0
for n in range(1,nf):
y -= 1/(2*np.pi*n)**2 * np.sin(np.pi*n*t) # see below
Corresponding coefficients for positive and negative n's are equal and exp(ix) - exp(-ix) = 2i sin(x), so it nicely reduces. (Double check the calculation.)
i'm currently incredibly stuck on what isn't working in my code and have been staring at it for hours. I have created some functions to approximate the solution to the laplace equation adaptively using the finite element method then estimate it's error using the dual weighted residual. The error function should give a vector of errors (one error for each element), i then choose the biggest errors, add more elements around them, solve again and then recheck the error; however i have no idea why my error estimate isn't changing!
My first 4 functions are correct but i will include them incase someone wants to try the code:
def Poisson_Stiffness(x0):
"""Finds the Poisson equation stiffness matrix with any non uniform mesh x0"""
x0 = np.array(x0)
N = len(x0) - 1 # The amount of elements; x0, x1, ..., xN
h = x0[1:] - x0[:-1]
a = np.zeros(N+1)
a[0] = 1 #BOUNDARY CONDITIONS
a[1:-1] = 1/h[1:] + 1/h[:-1]
a[-1] = 1/h[-1]
a[N] = 1 #BOUNDARY CONDITIONS
b = -1/h
b[0] = 0 #BOUNDARY CONDITIONS
c = -1/h
c[N-1] = 0 #BOUNDARY CONDITIONS: DIRICHLET
data = [a.tolist(), b.tolist(), c.tolist()]
Positions = [0, 1, -1]
Stiffness_Matrix = diags(data, Positions, (N+1,N+1))
return Stiffness_Matrix
def NodalQuadrature(x0):
"""Finds the Nodal Quadrature Approximation of sin(pi x)"""
x0 = np.array(x0)
h = x0[1:] - x0[:-1]
N = len(x0) - 1
approx = np.zeros(len(x0))
approx[0] = 0 #BOUNDARY CONDITIONS
for i in range(1,N):
approx[i] = math.sin(math.pi*x0[i])
approx[i] = (approx[i]*h[i-1] + approx[i]*h[i])/2
approx[N] = 0 #BOUNDARY CONDITIONS
return approx
def Solver(x0):
Stiff_Matrix = Poisson_Stiffness(x0)
NodalApproximation = NodalQuadrature(x0)
NodalApproximation[0] = 0
U = scipy.sparse.linalg.spsolve(Stiff_Matrix, NodalApproximation)
return U
def Dualsolution(rich_mesh,qoi_rich_node): #BOUNDARY CONDITIONS?
"""Find Z from stiffness matrix Z = K^-1 Q over richer mesh"""
K = Poisson_Stiffness(rich_mesh)
Q = np.zeros(len(rich_mesh))
Q[qoi_rich_node] = 1.0
Z = scipy.sparse.linalg.spsolve(K,Q)
return Z
My error indicator function takes in an approximation Uh, with the mesh it is solved over, and finds eta = (f - Bu)z.
def Error_Indicators(Uh,U_mesh,Z,Z_mesh,f):
"""Take in U, Interpolate to same mesh as Z then solve for eta vector"""
u_inter = interp1d(U_mesh,Uh) #Interpolation of old mesh
U2 = u_inter(Z_mesh) #New function u for the new mesh to use in
Bz = Poisson_Stiffness(Z_mesh)
Bz = Bz.tocsr()
eta = np.empty(len(Z_mesh))
for i in range(len(Z_mesh)):
for j in range(len(Z_mesh)):
eta[i] += (f[i] - Bz[i,j]*U2[j])
for i in range(len(Z)):
eta[i] = eta[i]*Z[i]
return eta
My next function seems to adapt the mesh very well to the given error indicator! Just no idea why the indicator seems to stay the same regardless?
def Mesh_Refinement(base_mesh,tolerance,refinement,z_mesh,QOI_z_mesh):
"""Solve for U on a normal mesh, Take in Z, Find error indicators, adapt. OUTPUT NEW MESH"""
New_mesh = base_mesh
Z = Dualsolution(z_mesh,QOI_z_mesh) #Solve dual solution only once
f = np.empty(len(z_mesh))
for i in range(len(z_mesh)):
f[i] = math.sin(math.pi*z_mesh[i])
U = Solver(New_mesh)
eta = Error_Indicators(U,base_mesh,Z,z_mesh,f)
while max(abs(k) for k in eta) > tolerance:
orderedeta = np.sort(eta) #Sort error indicators LENGTH 40
biggest = np.flipud(orderedeta[int((1-refinement)*len(eta)):len(eta)])
position = np.empty(len(biggest))
ratio = float(len(New_mesh))/float(len(z_mesh))
for i in range(len(biggest)):
position[i] = eta.tolist().index(biggest[i])*ratio #GIVES WHAT NUMBER NODE TO REFINE
refine = np.zeros(len(position))
for i in range(len(position)):
refine[i] = math.floor(position[i])+0.5 #AT WHAT NODE TO PUT NEW ELEMENT 5.5 ETC
refine = np.flipud(sorted(set(refine)))
for i in range(len(refine)):
New_mesh = np.insert(New_mesh,refine[i]+0.5,(New_mesh[refine[i]+0.5]+New_mesh[refine[i]-0.5])/2)
U = Solver(New_mesh)
eta = Error_Indicators(U,New_mesh,Z,z_mesh,f)
print eta
An example input for this would be:
Mesh_Refinement(np.linspace(0,1,3),0.1,0.2,np.linspace(0,1,60),20)
I understand there is alot of code here but i am at a loss, i have no idea where to turn!
Please consider this piece of code from def Error_Indicators:
eta = np.empty(len(Z_mesh))
for i in range(len(Z_mesh)):
for j in range(len(Z_mesh)):
eta[i] = (f[i] - Bz[i,j]*U2[j])
Here you override eta[i] each j iteration, so the inner cycle proves useless and you can go directly to the last possible j. Did you mean to find a sum of the (f[i] - Bz[i,j]*U2[j]) series?
eta = np.empty(len(Z_mesh))
for i in range(len(Z_mesh)):
for j in range(len(Z_mesh)):
eta[i] += (f[i] - Bz[i,j]*U2[j])
I am new to programming, and working on a relatively complicated problem. I am modeling a linear forced pendulum, and need to figure out how the amplitude of the swinging motion (in radians) depends on the friction (q-value) and the frequency of the driving force (Omega_D). So, I thought I would need a for-loop inside a for-loop because I need to do plots of Omega_D vs Amplitude for 3 q-values. Thus, iterating 3 times for q, and many more times within that for Omega_D. However, what I wrote is only giving me one amplitude value per q value. Here is my code; let me know what suggestions you may have.
import numpy as np
import matplotlib.pyplot as plt
from ode import rk4_step
def derivs(t, starting_values):
d0dt = starting_values[1]
d20dt2 = g/l * starting_values[0] - starting_values[3] * \
starting_values[1] + starting_values[4] * np.sin(starting_values[5] * t)
dqdt = 0.
dFdt = 0.
dldt = 0.
d_Omega_dt = 0. # defining these for later use in RK4
derivatives = np.array([d0dt, d20dt2, dqdt, dFdt, dldt, d_Omega_dt])
return derivatives
qs = [0.1, 1.0, 1.6] #Pick arbitrary q-values to run through
for i in qs:
theta_0 = 10. #initial values, chosen at random
theta_v0 = 10.
l = 1.
Omega_D = np.linspace(0.5, 5, 100)
F_D = .3
for x in Omega_D:
starting_values = [theta_0, theta_v0, l, i, F_D, x]
solution = np.copy(starting_values)
last_values = np.zeros(solution.size)
dt = .01
g = -9.8
t = 0.
Amp = 0.
starttime = 150.
while Amp == 0. : #Amp==0 because it never actually WILL be zero.
#So, the while loop to find amplitude only needs to run \
#until a nonzero amp is found
two_values_ago = np.copy(last_values)
last_values = np.copy(solution)
t = t + dt
solution = rk4_step(solution, derivs, t, dt) #take a step
if solution[1] == 0 and t > starttime: #if somehow we hit v=0 exactly
Amp == np.abs(solution[0])
print Amp
#This if statement interpolates to find the amp at the point where v=0
if solution[1] * last_values[1] < 0 and t > starttime:
fit_vs = np.array([two_values_ago[1], last_values[1]])
fit_xs = np.array([two_values_ago[0], last_values[0]])
v_interp = 0.
Amp = np.abs(np.interp(v_interp, fit_vs, fit_xs))
w = np.sqrt(-g / l) # This is the natural frequency
#Calculate the analytic soln
exact_solution = F_D / np.sqrt((w**2 - Omega_D**2)**2 + (i * Omega_D)**2)
#plot num and exact solns together
plt.plot(Omega_D, exact_solution)
plt.plot(Omega_D, Amp)
plt.title('q = ')
plt.ylabel('Amplitude (radians)')
plt.xlabel('$\Omega_{D}$ (rad/s)')
print Amp
plt.show()
Your problem is with indentation. This portion of the code is being run as part of the "outer" for loop, which means it is run for only the last value of "Amp" that is left when the inner "for" loop finishes:
w = np.sqrt(-g / l) # This is the natural frequency
#Calculate the analytic soln
exact_solution = F_D / np.sqrt((w**2 - Omega_D**2)**2 + (i * Omega_D)**2)
#plot num and exact solns together
plt.plot(Omega_D, exact_solution)
plt.plot(Omega_D, Amp)
plt.title('q = ')
plt.ylabel('Amplitude (radians)')
plt.xlabel('$\Omega_{D}$ (rad/s)')
print Amp
You need to indent one level more so it is run as part of the inner "for" loop.
Also, this line is not doing what you want:
Amp == np.abs(solution[0])
You are trying to assign np.abs(solution[0]) to Amp, but instead you are testing if np.abs(solution[0]) is equal to Amp (but then tossing away the result of the test). This line should be:
Amp = np.abs(solution[0])