I've been meaning to integrate in python, but I don't use Scipy, Numpy, or none of any other programs which go can be integrated into python. I'm pretty much a novice when it comes to coding, but I need help integrating. I've copied a small code but I still need to improve it.
def LeftEndSum(startingx, endingx, numberofRectangles) :
width = (float(endingx) - float(startingx)) / numberofRectangles
runningSum = 0
for i in range(numberofRectangles) :
height = f(startingx + i*width)
area = height * width
runningSum += area
return runningSum
I'm trying to integrate, but i want to get a list of data points that I can then graph into a plot at the end of the integration
I had an idea of defining an interval [a,b] and a delta n=(a change in # boxes between points), where I can do a convergence test in order to stop the loop to get the points. the convergence test would go
if I (n(old value)+delta(n))-I (n(old value))/I (n(old)) < epsilon
where epsilon=1x10^-6
in which if the integrated values the code breaks
Let's say you want to integrate y = x*x from 0.0 to 10.0, we know the answer is 333.33333. Here's some code to do that:
def y_equals_x_squared(x):
y = x*x
return y
def LeftEndSum(startingx, endingx, numberofRectangles) :
width = (float(endingx) - float(startingx)) / numberofRectangles
#print "width = " + str(width)
runningSum = 0
i = 1
while i <= numberofRectangles:
x = (endingx - startingx)/(numberofRectangles) * (i - 1) + startingx
height = y_equals_x_squared(x)
area = height * width
#print "i, x , height, area = " + str(i) + ", " + str(x) + ", " + str(height) + ", " + str(area)
runningSum += area
i += 1
return runningSum
#-----------------------------------------------------------------------------
startingx = 0.0
endingx = 10.0
#
numberofRectangles = 3
old_answer = LeftEndSum(startingx, endingx, numberofRectangles)
#
numberofRectangles = 4
new_answer = LeftEndSum(startingx, endingx, numberofRectangles)
#
delta_answer = abs(new_answer - old_answer)
#
tolerance = 0.0001
max_iterations = 500
iteration_count = 0
iterations = []
answers = []
while delta_answer > tolerance:
numberofRectangles += 100
new_answer = LeftEndSum(startingx, endingx, numberofRectangles)
delta_answer = abs(new_answer - old_answer)
old_answer = new_answer
iteration_count += 1
iterations.append(iteration_count)
answers.append(new_answer)
print "iteration_count, new_answer = " + str(iteration_count) + ", " + str(new_answer)
if(iteration_count > max_iterations):
print "reached max_iterations, breaking"
break
#
OutputFile = "Integration_Results.txt"
with open(OutputFile, 'a') as the_file:
for index in range(len(answers)):
the_file.write(str(index) + " " + str(answers[index]) + "\n")
#
import matplotlib.pyplot as plt
#
fig, ax = plt.subplots()
ax.plot(iterations, answers, 'r-', label = "Increasing # Rectangles")
title_temp = "Simple Integration"
plt.title(title_temp, fontsize=12, fontweight='bold', color='green')
ax.legend(loc='best', ncol=1, fancybox=True, shadow=True)
plt.xlabel('Number of Iterations')
plt.ylabel('Answer')
ax.grid(True)
plt.show(block=True)
Notice we plot the answer versus number of iterations at the end and it approaches the real answer very slowly as the number of iterations increase. There are other integration methods that will do better than simple rectangles such as the trapezoidal rule. When you put in a while loop and check for a tolerance always put in a max_iterations check so you don't get stuck in an infinite loop.
You can check your answer here:
http://www.integral-calculator.com/
That's how we know the answer is 333.3333
It makes more sense to include f as a parameter in the function. Why hardwire in a fixed formula? Furthermore, the code can be drastically simplified by using the built-in function sum applied to a generator:
def riemannSum(f,a,b,n,sample = 'L'):
"""computes Riemann sum of f over [a,b] using n rectangles and left ('L'), right ('R') or midpoints ('M')"""
h = (b-a)/float(n)
if sample.upper() == 'L':
s = a #s = first sample point
elif sample.upper() == 'R':
s = a + h
else:
s = a + h/2.0
return h*sum(f(s+i*h) for i in range(n))
You can define functions explicitly and then integrate them:
>>> def reciprocal(x): return 1/x
>>> riemannSum(reciprocal,1,2,100)
0.6956534304818242
(the exact value is the natural log of 2, which is approximately 0.693147)
Or, you can use anonymous functions (lambda expressions):
>>> riemannSum(lambda x: x**2,0,1,100,'m')
0.333325
Or, you can use functions already in the math module:
>>> riemannSum(math.sin,0,math.pi,10)
1.9835235375094546
None of these methods are very accurate. A more accurate on is Simpson's Rule which is also pretty easy to do in Python:
def simpsonsRule(f,a,b,n):
if n%2 == 1:
return "Not applicable"
else:
h = (b-a)/float(n)
s = f(a) + sum((4 if i%2 == 1 else 2)*f(a+i*h) for i in range(1,n)) + f(b)
return s*h/3.0
For example:
>>> simpsonsRule(math.sin,0,math.pi,10)
2.0001095173150043
This is much more accurate than the Riemann sum with 10 rectangles (the true value is 2).
Related
I have been trying to replicate Figures 1 and 2 from O'Dwyer's paper, "Electronic and thermal transport in hot carrier solar cells with low-dimensional contacts" (Link to O'Dwyer Paper), with Python on Spyder.
Figures to replicate
Figure 1: w = 1e-5
Figure 1
Figure 2 = w = 1e-2
Figure 2
Method
To find the absorber temperature, T_H, one needs to equate the net incoming energy flow due to radiation, Qrad, and the net heat current flowing out of the hot absorber reservoir, Qabs. Their equations are as follows:
Equations for Qrad and Qabs
The bold lined plots from Figures 1 and 2 refer to Wurfel's solutions that are given by the following equations:
Wurfel's Solutions
I am having some success replicating Figure 2 where w=1e-2 (my results shown below) but have no success getting Figure 1 where w=1e-5 (points and num_T below refer to the number of plotting points and the number of temperatures to iterate through respectively).
My attempt at Figure 2 when w=1e-2, points = 21, num_T = 300
My attempt at Figure 2
I think I am currently having trouble with the "overflow encountered in exp" warning trying to get Figure 1 with w=1e-5 to work. When I try to calculate Qabs (refer to code below in 'Parameters' function), it gives absurd values with orders of magnitude ~1e-70. However, when I run the same equations in WolframAlpha, I get a more reasonable result.
For example, the T_H value when W = 1e-5, N = 1e12 and Voltage = 0 V is ~T_H = 1448K (refer to Figure 1, top left graph).Using WolframAlpha, I get 4.54986×10^22 for Qrad and 4.83602×10^22 for Qabs (WolframAlpha solution for Qrad at w=1e-5,N=1e12,V=0) and WolframAlpha solution for Qabs at w=1e-5,N=1e12,V=0)) which are the results I want in Python. Find below all my code.
All Code
import os
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button
import matplotlib.ticker as ticker
from scipy.integrate import quad
from scipy.special import expit
import time
from sympy import symbols, Eq, solve
# import warnings
# warnings.filterwarnings("ignore")
t0= time.perf_counter()
directory = r'C:\Users\gyanj\Documents\GADGET BACKUP\University\5th Year\Thesis\Python Simul\ODwyer\Plots'
os.chdir(directory)
c = 3e8 #speed of light, m/s
q = 1.602e-19 # charge of electron, C
h = 6.626e-34/q #Planck's Constant, eVs
k = 8.617e-5 # Boltzmann's Constant, eVK^-1
stefan = 5.67e-8 #Stefan-Boltzmann's Constant, Wm^-2K^-4
T_C = 300 #Cold Reservoir Temperature, K
T_S = 6000 #Sun Temperature, K
Omega = np.pi #Absorption/Emission Solid Angle, sr
A = 1e-4 #Absorber Area, m^2
points = 21 # Number of plotting points
num_T = 300 #Number of temperatures to iterate through
Temperatures = np.linspace(T_C,T_S,num_T) # array of temperatures
E_u = 1 #Average electrochemical potential of system, eV
V = np.linspace(0,1,points) #V applied symetrically across device
max_lim = np.inf# integral upper limit
W = [1e-2] #Transmission function width
N = [1e9,1e10,1e12] #Number of contacts
#Following block used for progress bar (not relevant to calculations)
global total
total = len(W)*len(N)*(points)*len(Temperatures)
progress = 0
counter = 0
full_time = 0
#Object containing all relevant parameters
class param:
def __init__(self, TH, I, P, n, Qrad, Qabs):
self.TH = TH #Hot reservoir/Absorber Temperature, K
self.I = I # Current, A/m^2
self.P = P #Power, W/m^2
self.n = n #Efficiency
self.Qrad = Qrad #net incoming energy flow due to radiation
self.Qabs = Qabs #net heat current flowing out of the hot absorber reservoir
Data = np.empty([len(W),len(N),points], dtype = object) #Contain all param objects
datafile = 'ODwyer.dat'
fout = open(datafile,'w')
fout.write('')
fout.close()
for i in range(len(W)):
for j in range(len(N)):
for x in range(points):
Data[i][j][x] = param(0,0,0,0,0,0)
# Function Paramaters calculates Qrad,Qabs and I for a given T_H,u_H,u_C,N_contact,w,voltage
def Parameters (T_H, u_H, u_C, N_contact, w, voltage):
eqn1 = lambda E: ((E)**3/(np.exp(E/(k*T_S))-1)-(E)**3/(np.exp(E/(k*T_H))-1))
Qrad = ((2*Omega*A*q)/((h**3)*(c**2)))*quad(eqn1,0,max_lim)[0]
eqn2 = lambda E:(E-u_H)*(expit(-(E-u_H)/(k*T_H))-expit(-(E-u_C)/(k*T_C)))*(np.exp(-(E-E_u/2)**2/(w)))
Qabs = ((4*N_contact*q)/h)*quad(eqn2,0,max_lim)[0]
if Qabs < 0:
Qabs = np.inf
error = abs(Qrad-Qabs)
eqn3 = lambda E:(expit(-(E-u_H)/(k*T_H))-expit(-(E-u_C)/(k*T_C)))*(np.exp(-(E-E_u/2)**2/(w)))
I = -((2*N_contact*q)/h)*quad(eqn3,0,max_lim)[0]/A
fout = open(datafile,'a')
fout.write('%.2e\t%.2e\t%.1f\t%.2f\t%.2e\t%.2e\n'%(w,N_contact,T_H,voltage,Qrad,Qabs))
fout.close()
return error, I, Qrad, Qabs
#Progress bar for simulation time (not relevant for calculations)
def progressbar(progress):
if (progress >= 0.01):
t1 = time.perf_counter() - t0
full_time = t1*1/progress*100
timeleft = full_time-t1
if timeleft >= 3600:
timelefthrs = int(round(timeleft/3600,0))
timeleftmins = int((timeleft-timelefthrs*3600)%60)
print('\rSimulation Progress: %.2f%%\t Estimated Time Left: %dh %dm '%(progress,timelefthrs, timeleftmins), end='')
elif timeleft >= 60 and timeleft <3600: # in mins
timeleftmins = int(round(timeleft/60,0))
timeleftsecs = int((timeleft-timeleftmins*60)%60)
print('\rSimulation Progress: %.2f%%\t Estimated Time Left: %dm %ds '%(progress,timeleftmins, timeleftsecs), end='')
else:
print('\rSimulation Progress: %.2f%%\t Estimated Time Left: %ds '%(progress,timeleft), end='')
else:
print('\rSimulation Progress: %.2f%%'%(progress), end='')
def Odwyer(index, counter):
for j in range(len(N)):
for i in range(points): #per V
u_H = E_u+V[i]/2 #Hot absorber electrochemical potential, eV
u_C = E_u-V[i]/2 #Cold Reservoir electrochemical potential, eV
error = np.inf #initialise error between Qrad and Qabs as inf
for x in range(len(Temperatures)):
temperature = Temperatures[x]
diff, I, Qrad, Qabs= Parameters(Temperatures[x], u_H, u_C, N[j], W[index], V[i])
if diff <= error: #if difference between Qabs and Qrad is smaller than previous error, use this Temperature[x]
Data[index][j][i].TH = temperature
Data[index][j][i].Qrad = Qrad
Data[index][j][i].Qabs = Qabs
Data[index][j][i].I = I
Data[index][j][i].P = I*V[i]
Data[index][j][i].n = I*V[i]/(stefan*(T_S**4))
error = abs(diff)
counter += 1
progress = counter/total*100
progressbar(progress)
#Plotting
fig, axs= plt.subplots(2,2, constrained_layout=True)
ax1 = axs[0,0]
ax2 = axs[0,1]
ax3 = axs[1,0]
ax4 = axs[1,1]
for i in range(2):
for j in range(2):
axs[i,j].set_xlim(0,1)
axs[i,j].xaxis.set_major_locator(ticker.MultipleLocator(0.5))
axs[i,j].set_xlabel("Voltage (V)")
ax1.set_ylim(0,T_S)
ax1.set_ylabel("TH (K)")
ax1.yaxis.set_major_locator(ticker.MultipleLocator(2000))
ax2.set_ylim(0,1e8)
ax2.set_ylabel("I (A/m^2)")
ax2.yaxis.set_major_locator(ticker.MultipleLocator(2e7))
ax3.set_ylim(0,1e8)
ax3.set_ylabel("Power (W/m^2)")
ax3.yaxis.set_major_locator(ticker.MultipleLocator(2e7))
ax4.set_ylim(0,1)
ax4.set_ylabel("Efficiency")
ax4.yaxis.set_major_locator(ticker.MultipleLocator(0.2))
TH = np.empty([len(N),points])
I = np.empty([len(N),points])
P = np.empty([len(N),points])
n = np.empty([len(N),points])
for j in range(len(N)):
for x in range(points):
TH[j][x] = Data[index][j][x].TH
I[j][x] = Data[index][j][x].I
P[j][x] = Data[index][j][x].P
n[j][x] = Data[index][j][x].n
#Wurfel's Solution
TH_W = []
I_W = []
P_W = []
n_W = []
for x in range(points):
if V[x] == E_u:
TH_wurfel = 1e20
else:
TH_wurfel = T_C/(1-V[x]/E_u)
TH_W.append(TH_wurfel)
Iwurfel = (stefan)/(E_u)*(T_S**4-TH_wurfel**4)
Pwurfel = stefan*(T_S**4-TH_wurfel**4)*(1-T_C/TH_wurfel)
nwurfel = (T_S**4-TH_wurfel**4)/(T_S**4)*(1-T_C/TH_wurfel)
I_W.append(Iwurfel)
P_W.append(Pwurfel)
n_W.append(nwurfel)
linestyles = ['--','-','-.']
for j in range(len(N)):
for x in range(points):
if TH[j][x] == T_S:
TH[j][x] = 1e8
for i in range(len(N)):
ax1.plot(V,TH[i], label='N = %.0e'%N[i], color = 'black', linestyle = linestyles[i], linewidth = 1)
ax2.plot(V,I[i], label='N = %.0e'%N[i], color = 'black', linestyle = linestyles[i], linewidth = 1)
ax3.plot(V,P[i], label='N = %.0e'%N[i], color = 'black', linestyle = linestyles[i], linewidth = 1)
ax4.plot(V,n[i], label='N = %.0e'%N[i], color = 'black', linestyle = linestyles[i], linewidth = 1)
ax1.plot(V,TH_W, color = 'black', label='Wurfel', linewidth = 3)
ax2.plot(V,I_W, color = 'black', label='Wurfel', linewidth = 3)
ax3.plot(V,P_W, color = 'black', label='Wurfel', linewidth = 3)
ax4.plot(V,n_W, color = 'black', label='Wurfel', linewidth = 3)
fig.suptitle('w = %.0e eV' % W[index])
ax1.legend(loc='upper right', fontsize = 8)
ax2.legend(loc='upper right', fontsize = 8)
ax3.legend(loc='upper right', fontsize = 8)
ax4.legend(loc='upper right', fontsize = 8)
#Saving figure
fig.savefig('w = %.0e eV, pp = %d, num_T = %d.jpg' %(W[index],points,num_T), dpi=800)
return counter
for x in range(len(W)):
counter = Odwyer(x, counter)
# Printing out object values
for x in range(len(W)):
for j in range(len(N)):
print('Parameters for W = %0.e, N = %.0e'%(W[x],N[j]))
for i in range(points):
print('w = %.0e\tV = %.2f\tTH = %.0f\tQrad = %.2e\tQabs = %.2e\tI = %.2e'%(W[x],V[i],Data[x][j][i].TH,Data[x][j][i].Qrad,Data[x][j][i].Qabs,Data[x][j][i].I))
print('\nComplete!')
What I've tried
I have tried changing the upper limit of the integrals from inf to lower values and although it removed the overflow warning for values ~<15, it made Qabs = 0.00e00. I also tried changing arguments for 'limit' and 'epsabs' in the 'quad' function but couldn't get that to work either. Changing the variables 'points' and 'num_T' did not improve the accuracy of my values either. I have also read and tried solutions from relevant posts regarding overflows such as Overflow Post but to no avail. This is my first post so if you require any further information from me to fix my problem, feel free to let me know!
Here's a quick and dirty stdlib (no numpy) script that got something close to the WolframAlpha answer:
from math import exp, pi
C1 = 8.617e-5 * 6000
C2 = 8.617e-5 * 1448
def f(x):
denom1 = exp(x / C1)
denom2 = exp(x / C2)
# did some algebra
difference = (denom2 - denom1) / (denom1 - 1) / (denom2 - 1)
return x ** 3 * difference
bins = 10_000
endpoint = 10
total = 0.0
for i in range(1, bins+1):
x = i * endpoint / bins
total += f(x)
# account for widths
total *= (endpoint / bins)
scaled = float(total) * 2 * pi * 1e-4 / (4.14e-15)**3 / (3e8)**2
print(scaled)
# 4.549838698077388e+22
Part of the problem (I'm guessing, not sure) would be that 1/(a-1) - 1/(b-1) will be wildly imprecise for a and b close to 1, so you can do some algebra to try and fix that, and make it (b-a)/(a-1)/(b-1).
I'm trying to implement an RK4 method to solve for the orbit of a rocket around the Earth. Eventually this code will be used for more complex solar system simulations, but I'm just trying to get it working in this simple system first.
My code is below - I'm hoping someone can tell me what is wrong with it.
My trouble-shooting efforts have been long and unfruitful, but I'll summarise what I've found:
I believe the acceleration function is fine and correct, as it gives believable values and agrees with my calculator/brain
It appears as though the problem lies somewhere in the calculation of the next "r" value - when you run this code, an x-y graph will appear, showing that the rocket initially falls in towards the Earth, then bounces away again, then back. I printed all the relevant values at this point, and found that "v" and "a" were negative in both components, despite the rocket clearly moving in the positive y direction. This makes me think that the calculation of the new "r" is in disagreement with the physics.
The rocket is falling to Earth much faster than it should, which is also suspicious (technically it shouldn't fall into Earth at all, since the initial velocity is set to the required orbital velocity)
Either way I would greatly appreciate if anyone could find the error, as I have been unable up to this point.
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
mE = 5.9742e24 #earth mass
mM = 7.35e22 #moon mass
dM = 379728240.5 #distance from moon to barycentre
dE = 4671759.5 #distance from earth to barycentre
s = 6.4686973e7 #hypothesised distance from moon to Lagrange-2 point
sr = 6.5420e7 #alternate L2 distance
def Simulate(iterations):
x = dM #initialise rocket positions
y = 0
a = 10 #set the time step
xdot = 0. #initialise rocket velocity
ydot = -((6.6726e-11)*mE/x)**0.5
rocket_history_x, rocket_history_y = [[] for _ in range(2)]
history_mx, history_my = [[] for _ in range(2)]
history_ex, history_ey = [[] for _ in range(2)]
sep_history, step_history = [[] for _ in range(2)] #create lists to store data in
history_vx, history_vy = [[] for _ in range(2)]
history_ax, history_ay = [[] for _ in range(2)]
n = 1500
m = 10000 #n,m,p are for storing every nth, mth and pth value to the lists
p = 60000
r = np.array((x,y)) #create rocket position vector
v = np.array((xdot, ydot)) #create rocket velocity vector
for i in range(iterations):
xe, ye = 0, 0 #position of earth
re = np.array((xe,ye)) #create earth position vector
phi = np.arctan2((r[1]-ye),(r[0]-xe)) #calculate phi, the angle between the rocket and the earth, as measured from the earth
r_hat_e = np.array((np.cos(phi), np.sin(phi))) #create vector along which earth's acceleration acts
def acc(r): #define the acceleration vector function
return ((-6.6726e-11)*(mE)/abs(np.dot((r-re),(r-re))))*r_hat_e
k1v = acc(r) #use RK4 method
k1r = v
k2v = acc(r + k1r*(a/2)) #acc(r + (a/2)*v)
k2r = v * (a/2) * k1v # v*acc(r)
k3v = acc(r + k2r*(a/2)) #acc(r + (a/2)*v*acc(r))
k3r = v * k2v * (a/2) #v*(a/2)*acc(r + (a/2)*v)
k4v = acc(r + k3r*a) #acc(r + (a**2/2)*v*acc(r + (a/2)*v))
k4r = v * k3v * a #v*a*acc(r + (a/2)*v*acc(r))
v = v + (a/6) * (k1v + 2*k2v + 2*k3v + k4v) #update v
r = r + (a/6) * (k1r + 2*k2r + 2*k3r + k4r) #update r
sep = np.sqrt(np.dot((r-re),(r-re))) #separation of rocket and earth, useful for visualisation/trouble-shooting
if i% n == 0: # Check for the step
rocket_history_x.append(r[0])
rocket_history_y.append(r[1])
history_ex.append(xe)
history_ey.append(ye)
sep_history.append(sep) #putting data into lists for plotting and troubleshooting
step_history.append(i)
history_ax.append(acc(r)[0])
history_ay.append(acc(r)[1])
history_vx.append(v[0])
history_vy.append(v[1])
#if i% m == 0: # Check for the step
#print r
#print acc(r)
#if i% p == 0: # Check for the step
#print ((a/6)*(k1v + 2*k2v + 2*k3v + k4v))
#print ((a/6)*(k1r + 2*k2r + 2*k3r + k4r))
#print k1v, k2v, k3v, k4v
#print k1r, k2r, k3r, k4r
return rocket_history_x, rocket_history_y, history_ex, history_ey, history_mx, history_my, sep_history, step_history, history_ax, history_ay, history_vx, history_vy
x , y, xe, ye, mx, my, sep, step, ax, ay, vx, vy = Simulate(130000)
#print x,y,vx,vy,ax,ay,step
print ("Plotting graph...")
plt.figure()
plt.subplot(311)
plt.plot(x, y, linestyle='--', color = 'green')
#plt.plot(mx, my, linestyle='-', color = 'blue')
plt.plot(xe, ye, linestyle='-', color = 'red')
#plt.plot(xm, ym)
plt.xlabel("Orbit X")
plt.ylabel("Orbit Y")
'''
plt.plot(step, vy)
plt.ylabel("vy")
'''
plt.subplot(312)
plt.plot(step, sep)
plt.xlabel("steps")
plt.ylabel("separation")
plt.subplot(313)
plt.plot(step, ay)
plt.ylabel("ay")
plt.show()
print("Simulation Complete")
Your most grave error is that in the computation of the v slopes, you used multiplication instead of addition.
k1v = acc(r) #use RK4 method
k1r = v
k2v = acc(r + (a/2) * k1r)
k2r = v + (a/2) * k1v
k3v = acc(r + (a/2) * k2r)
k3r = v + (a/2) * k2v
k4v = acc(r + a * k3r)
k4r = v + a * k3v
A second error is that you use a value from a different state in the acceleration computation of the changed states. This might reduce the order of the method down to 1. Which might not change this plot visibly but will have larger errors over longer integration periods. Use
def acc(r): #define the acceleration vector function
return ((-6.6726e-11)*(mE)/abs(np.dot((r-re),(r-re)))**1.5)*(r-re)
I am trying Bayesian regression using Metropolis-Hastings. The test data is generated as follows (Python code, I didn't copy the entire code):
trueA = 5 ; trueB = 7 ;trueSD = 10 ; sample_size = 261
x = np.arange(-sample_size/8, sample_size/8, (sample_size*2/8)/sample_size)
y = trueA *x + trueB + npr.normal(loc=0, scale=trueSD, size=sample_size)
I defined log likelihood as follows:
def likelihood(param):
a = param[0][0] ; b = param[0][1] ; sd = param[0][2] ; pred = a*x + b
sumSqError = np.power((y - pred), 2).sum()
likelihoodsum = ((sample_size/2)*(np.log(1)-np.log(np.power(sd,2)))) + (- 1/(2*np.power(sd,2)) * sumSqError)
return likelihoodsum
To make next points, I prepared the following function:
def next_param(param, param_index):
a_next = param[0][0] ; b_next = param[0][1] ; sd_next = param[0][2]
if param_index == 0:
a_next = param[0][0] + npr.normal(0, 0.1)
elif param_index == 1:
b_next = param[0][1] + npr.normal(0, 0.1)
elif param_index == 2:
sd_next = param[0][2] + npr.normal(0, 0.1)
return np.array([[a_next, b_next, sd_next]])
This code works well (acceptance rate is high enough and I can estimate the parameters), though I know sd_next can go negative in the above code, which is weird.
So, I decided to use log for sd_next:
elif param_index == 2:
sd_next = np.log(param[0][2]) + npr.normal(0, 0.1)
return np.array([[a_next, b_next, np.exp(sd_next)]])
However, the estimated parameters are far from the true values. How can I make a standard deviation always positive in Metropolis-Hastings?
JFI, here is MCMC part:
num_sampling = 1000
chain = np.zeros((num_sampling, 1, 3))
chain[0][0][0] = 20 # starting value for a
chain[0][0][1] = 15 # starting value for b
chain[0][0][2] = 15 # starting value for sd
num_accepted = 0
for i in range(num_sampling-1):
chain_previous = chain[i][:]
chain_new = np.zeros((1, 1, 3))
for p in range(3):
proposal = next_param(chain_previous, p)
probab = likelihood(proposal) - likelihood(chain_previous)
if 0 < probab:
chain_new[0][0][p] = proposal[0][p]
num_accepted += 1
else:
chain_new[0][0][p] = chain[i][0][p]
chain[i+1] = chain_new[0][:]
It is not weird at all that you get a negative standard deviation $\sigma$ when your proposal is a Normal distribution, with support $(-\infty,+\infty)$.
And the Metropolis-Hastings accept-reject step should also include the prior distribution on the three parameters. Including the Jacobian when the proposal is on $\log\sigma$.
As written the Metropolis-Hastings accept-reject step is incorrect!
if 0 < probab:
is not the right condition for accepting a move to the proposed value: one should compared the (log-)probability with a (log-)uniform. In the current format, you converge to a maximum of the likelihood.
I am trying to define the archimedean spiral: when I'm trying to define the inclination angle (incl) of the tangent vector to the orbit ( i.e: tan(incl))
I'm getting an error:
'numpy.ufunc' object does not support item assignment"
and "can't assign to function call"
the same error when I want to calculate cos(incl), and sin(incl).
Any suggestions and helps.
My code is:
T = 100
N = 10000
dt = float(T)/N
D = 2
DII = 10
a = 2.
v = 0.23
omega = 0.2
r0 = v/omega
t = np.linspace(0,T,N+1)
r = v*t
theta = a + r/r0
theta = omega*t
x = r * np.cos(omega*t)
y = r * np.sin(omega*t)
dxdr = np.cos(theta) - (r/r0)*np.sin(theta)
dydr = np.sin(theta) + (r/r0)*np.cos(theta)
dydx = (r0*np.sin(theta) + r*np.cos(theta))/r0*np.cos(theta) - r*np.sin(theta)
np.tan[incl] = dydx
incl = np.arctan((dydx))
### Calculate cos(incl) ,sin(incl) :
np.sin[np.incl] = np.tan(np.incl)/np.sqrt(1 + np.tan(np.incl)*2)
np.cos[incl] = 1/np.sqrt(1 + np.tan(incl)*2)
p1, = plt.plot(xx, yy)
i= 0 # this is the first value of the array
Bx = np.array([np.cos(i), -np.sin(i)])
By = np.array([np.sin(i), np.cos(i)])
n = 1000
seed(2)
finalpositions = []
for number in range(0, 10):
x = []
y = []
x.append(0)
y.append(0)
for i in range(n):
s = np.random.normal(0, 1, 2)
deltaX = Bx[0]*np.sqrt(2*DII*dt)*s[0] + Bx[1]*np.sqrt(2*D*dt)*s[1]
deltaY = By[0]*np.sqrt(2*DII*dt)*s[0] + By[1]*np.sqrt(2*D*dt)*s[1]
x.append(x[-1] + deltaX)
y.append(y[-1] + deltaY)
finalpositions.append([x[-1], y[-1]])
p2, = plt.plot(finalpositions[:,0],finalpositions[:,1],'*')
plt.show()
The error message is correct, you are trying to assign to a function! I think you're trying to compute a value that represents the sin, cos or tan of a value, but that doesn't mean you need to assign to np.sin, etc. What you want is to calculate the value which represents the trig function, and then use the inverse trig function to get the angle:
## np.tan[incl]= dydx ## np.tan is a function, so you cannot index it like an array, and you should not assign to it.
incl = np.arctan((dydx)) ## this is all you need to get "incl"
### Calculate cos(incl) ,sin(incl) :
## NOTE: you already have the angle you need!! No need for a complicated formulate to compute the sin or cos!
sin_incl = np.sin(incl)
cos_incl = np.cos(incl)
EDIT: One additional comment...np is a module that contains lots of numeric methods. When you calculate incl, it is not part of np! So there is no need to reference it like np.incl. Just use incl.
EDIT2: Another problem I found is this line:
dydx = (r0*np.sin(theta) + r*np.cos(theta))/r0*np.cos(theta) - r*np.sin(theta)
To calculate dydx, you're just dividing dydr by dxdr, but that's not what your code does! You need parens around the denominator like this:
dydx = (r0*np.sin(theta) + r*np.cos(theta))/(r0*np.cos(theta) - r*np.sin(theta))
I must solve the Euler Bernoulli differential beam equation which is:
u’’’’(x) = f(x) ;
(x is the coordinate of the beam axis points)
and boundary conditions:
u(0)=0, u’(0)=0, u’’(1)=0, u’’’(1)=a
I have studied the theory of numerically finite differences which expresses the series of derivations as:
U’k = (1/2*h)(Uk+1 - Uk-1)
U’’k = (1/h2)(Uk+1 - 2 Uk + Uk-1)
U’’’k = (1/2h3)(Uk+2 - 2 Uk+1 + 2 Uk-1 + Uk-2)
U’’’’k = (1/h4)(Uk+2 - 4 Uk+1 + 6 Uk - 4 Uk-1 + Uk-2)
(k+1, k+2, etc. etc are subscripts)
and I found a script which expresses it as follows:
import numpy as np
from scipy.linalg import solveh_banded
import matplotlib.pyplot as plt
def beam4(n,ffun,a):
x = np.linspace(0,1,n+1)
h = 1.0/n
stencil = np.array((1,-4,6))
B = np.outer(stencil,np.ones(n))
B[1,-1] = -2; B[2,0] = 7; B[2,-1] = 1; B[2,-2] = 5
f = ffun(x)
f *= h** 4; f[-1] *= 0.5; f[-1] -= a*h**3
u = np.zeros(n+1)
u[1:] = solveh_banded(B,f[1:],lower=False)
return x,u
But I can't understand why the coefficient matrix is built this way:
stencil = np.array((1,-4,6))
B = np.outer(stencil,np.ones(n))
B[1,-1] = -2; B[2,0] = 7; B[2,-1] = 1; B[2,-2] = 5
f = ffun(x)
f *= h**4; f[-1] *= 0.5; f[-1] -= a*h**3 "
Thanks in advance!!!!
Hope this is helpful. (due to this is my first time to post an answer)
The coefficients of this Hermitian positive-definite banded matrix are due to applied of ghost node method. It is one of most efficient and popular method for treating the boundary conditions of FDM without lossing of accuracy (here these coefficients will give a second order converge rate in general). If you have trouble to visual the matrix please check the 'K' matrix in my code below:
from numpy import linspace,zeros,array,eye,dot
from numpy.linalg import solve
from pylab import plot,xlabel,ylabel,legend,show
a = 0.2 ;b = 0.0
LX,dx = 1.0,0.05 ;nx = int(LX/dx)+1
X = linspace(0.0,LX,nx)
Fs = X**2.0
""""""""""""""""""""""""""""""""""""""""""""""""""""""
def calcB(l,b):
if b==0: return [0.0,0.0]
elif b==1: return 1.0/l/l/l/l*array([-4.0,7.0,-4.0,1.0])
elif b==nx-2: return 1.0/l/l/l/l*array([1.0,-4.0,5.0,-2.0])
elif b==nx-1: return 1.0/l/l/l/l*array([2.0,-4.0,2.0])
else: return 1.0/l/l/l/l*array([1.0,-4.0,6.0,-4.0,1.0])
U = zeros(nx) ;V = zeros(nx)
M = eye(nx) ;K = zeros((nx,nx))
F = zeros(nx) ;F[nx-2] = -b/dx/dx
F[nx-1] = 2.0*b/dx/dx-2.0*a/dx
for i in range(nx):
if i == 0: I = [i,i+1]
elif i == 1: I = [i-1,i,i+1,i+2]
elif i == nx-2: I = [i-2,i-1,i,i+1]
elif i == nx-1: I = [i-2,i-1,i]
else: I = [i-2,i-1,i,i+1,i+2]
for k,j in enumerate(I):
K[i,j] += calcB(dx,i)[k]
""""""""""""""""""""""""""""""""""""""""""""""""""""""
pn = [0] ;eq2 = pn
eq1 = [i for i in range(nx) if i not in pn]
MM1_ = K[eq1,:]; MM11,MM12 = MM1_[:,eq1],MM1_[:,eq2]
RR = F+Fs
U[eq2] = [0.0]
U[eq1] = solve(MM11,(RR[eq1]-dot(MM12,U[eq2])))
######################Plotting#########################
Us = lambda x: x**6.0/360.0+x**3.0/6.0*(a-1.0/3.0)+x**2.0/2.0*(1.0/4.0-a)
plot(X,U,'bo',label='FDM') ;plot(X,Us(X),'g-',label='solution')
xlabel('X'); ylabel('U'); legend(loc='best')
show()
cheers