Finding roots of function in Python - python

I'm trying to calculate the roots for a function using the scipy function fsolve, but an error keeps flagging:
TypeError: 'numpy.array' object is not callable
I assume it's probably easier to define the equation as a function but I've tried that a few times to no avail.
Code:
import scipy
import numpy as np
import matplotlib.pyplot as plt
from scipy import optimize
# Constants
wavelength = 0.6328
ncore = 1.462420
nclad = 1.457420
a = 8.335
# Mode Order
l = 0
# Mode parameters
V = (2 * np.pi * a / wavelength) * np.sqrt(ncore**2 - nclad**2)
U = np.arange(0, V, 0.01)
W = np.sqrt(V**2-U**2)
func = U * scipy.special.jv(l+1, U) / scipy.special.jv(l, U) - W * scipy.special.kv(l+1, W) / scipy.special.kv(l, W)
from scipy.optimize import fsolve
x = fsolve(func,0)
print x
StackTrace:
Traceback (most recent call last):
File "<ipython-input-52-081a9cc9c0ea>", line 1, in <module>
runfile('/home/luke/Documents/PythonPrograms/ModeSolver_StepIndex/ModeSolver_StepIndex.py', wdir='/home/luke/Documents/PythonPrograms/ModeSolver_StepIndex')
File "/usr/lib/python2.7/site-packages/spyderlib/widgets/externalshell/sitecustomize.py", line 580, in runfile
execfile(filename, namespace)
File "/home/luke/Documents/PythonPrograms/ModeSolver_StepIndex/ModeSolver_StepIndex.py", line 52, in <module>
x = fsolve(func,0)
File "/usr/lib64/python2.7/site-packages/scipy/optimize/minpack.py", line 140, in fsolve
res = _root_hybr(func, x0, args, jac=fprime, **options)
File "/usr/lib64/python2.7/site-packages/scipy/optimize/minpack.py", line 197, in _root_hybr
shape, dtype = _check_func('fsolve', 'func', func, x0, args, n, (n,))
File "/usr/lib64/python2.7/site-packages/scipy/optimize/minpack.py", line 20, in _check_func
res = atleast_1d(thefunc(*((x0[:numinputs],) + args)))
TypeError: 'numpy.ndarray' object is not callable

That is because fsolve takes a function as argument.
Try this, Note you still will encounter some runtime error , you will have to check if your return from func is properly constructed, I will leave that for you to figure out.
import scipy
import numpy as np
import matplotlib.pyplot as plt
from scipy import optimize
# Constants
wavelength = 0.6328
ncore = 1.462420
nclad = 1.457420
a = 8.335
# Mode Order
# l = 0
# Mode parameters
V = (2 * np.pi * a / wavelength) * np.sqrt(ncore**2 - nclad**2)
U = np.arange(0, V, 0.01)
W = np.sqrt(V**2-U**2)
def func(l):
return U * scipy.special.jv(l+1, U) / scipy.special.jv(l, U) - W * scipy.special.kv(l+1, W) / scipy.special.kv(l, W)
from scipy.optimize import fsolve
x = fsolve(func,0)
print x

You need to pass a function to fsolve not an array.
If I just print your func:
func
array([ -1.04882076e+01, -1.04881526e+01, -1.04879876e+01,
-1.04877125e+01, -1.04873274e+01, -1.04868321e+01,
-1.04862266e+01, -1.04855109e+01, -1.04846847e+01,
-1.04837481e+01, -1.04827008e+01, -1.04815428e+01,
-1.04802738e+01, -1.04788938e+01, -1.04774024e+01,
-1.04757996e+01, -1.04740850e+01, -1.04722585e+01,
-1.04703198e+01, -1.04682686e+01, -1.04661046e+01,
-1.04638275e+01, -1.04614371e+01, -1.04589330e+01,
-1.04563147e+01, -1.04535820e+01, -1.04507345e+01,
-1.04477718e+01, -1.04446934e+01, -1.04414988e+01,
... ]
that's an array but you want a function. Something like this works:
def linear(x):
return 2*x+4
fsolve(linear, 0)
don't know how one could define your function, though.

Related

Cant plot an equation properly in Python

I am trying to plot a 3rd rank polynomial that I calculated through Lagrance with a given set of x,y.(x=P_Maz , y=H_Maz).I can't really find what I am doing wrong.Even some plots I tried would show me wrong graph(Because x=0 wouldnt give y=25).
The equation is as follows:
3 2
3.395e-06 x - 0.001528 x + 1.976 x + 25
The part of my function and plot is this one:
P_Maz = np.array([120, 180, 270, 300]) # x
H_Maz = np.array([246, 351, 514, 572]) # y
def Lagrange(Lx, Ly):
x = symbols('x')
y = 0
for k in range(len(Lx)):
p = 1
for j in range(len(Lx)):
if j != k:
p = p * ((x - Lx[j]) / (Lx[k] - Lx[j]))
y += p * Ly[k]
return y
poly1=simplify(Lagrange(P_Maz,H_Maz))
plot(P_Maz,poly1(H_Maz))
grid()
show()
I get the following error:
Traceback (most recent call last):
File "C:\Users\Egw\Desktop\Analysh\Askhsh1\askhsh5.py", line 36, in <module>
plot(P_Maz,poly1(H_Maz))
TypeError: 'Add' object is not callable
Full code is as follows:
import numpy as np
from matplotlib.pyplot import plot, show, grid
import numpy.polynomial.polynomial as poly
import matplotlib.pyplot as plt
from scipy.interpolate import lagrange
from sympy import symbols, Eq,simplify
P_Maz = np.array([120, 180, 270, 300]) # x
H_Maz = np.array([246, 351, 514, 572]) # y
P_Lig = np.array([150, 215, 285, 300]) # x
H_Lig = np.array([307, 427, 563, 594]) # y
def Lagrange(Lx, Ly):
x = symbols('x')
y = 0
for k in range(len(Lx)):
p = 1
for j in range(len(Lx)):
if j != k:
p = p * ((x - Lx[j]) / (Lx[k] - Lx[j]))
y += p * Ly[k]
return y
poly1=simplify(Lagrange(P_Maz,H_Maz))
poly2=simplify(Lagrange(P_Lig,H_Lig))
print(Lagrange(P_Maz, H_Maz)) #Morfh Klasmatwn
print(poly1) #Telikh eksiswsh
print(lagrange(P_Maz,H_Maz)) #Telikh eksiswsh me built in method
print(Lagrange(P_Lig, H_Lig)) #Morfh Klasmatwn
print(poly2) #Telikh eksiswsh
print(lagrange(P_Lig,H_Lig)) #Telikh eksiswsh me built in method
plot(P_Maz,poly1(P_Maz))
grid()
show()
With simplify, you get an expression, but then you need to evaluate that expression for all the desired values.
You can do that through lambdify:
from sympy import lambdify
f = lambdify(x, simplify(Lagrange(P_Maz,H_Maz)), "numpy")
# above, just copied your simplify call with your variables and used in the lambdify
plot(P_Maz, f(P_Maz)) # use the created lambdify function, f, not the unevaluated expression
Mind the warning in the documentation (docs): "lambdify uses eval. Don’t use it on unsanitized input."

How to solve this time-dependent PDE in Python?

Is there a way to numerically solve the following PDE in Python?
The second term on the RHS has a derivative with respect to time as well as space.
I tried using Py-PDE package in Python, it solves only the form dy(x,t)/dt = f(y(x,t)) so I tried to use a root finding algorithm similar to scipy fzero to get the solution to dy(x,t)/dt - f(y(x,t),dy(x,t)/dt) = 0 (solving for dy(x,t)/dt).
class nonlinearPDE(pde.PDEBase):
def __init__(self, bc={"derivative":0}):
self.bc = bc #boundary conditions for operators
def _make_pde_rhs_numba(self, state):
"""numba-compiled implementation of the PDE"""
laplace = state.grid.make_operator("laplace", bc=self.bc)
def findroot(f, df, x0, nmax):
"""Newton–Raphson method"""
for i in range(nmax):
x0 = x0 - f(x0)/df(x0)
return x0
#jit
def pde_rhs(y, t):
func = lambda dydt : dydt - a*laplace(y) - b*laplace(dydt)
dydt = findroot(func, lambda x : 1, 0, 1)
return dydt
return pde_rhs
However, when the program tries to solve the PDE it throws an error:
File "...\anaconda3\lib\site-packages\pde\solvers\controller.py", line 191, in run
t = stepper(state, t, t_break)
File "...\anaconda3\lib\site-packages\pde\solvers\scipy.py", line 82, in stepper
sol = integrate.solve_ivp(
File "...\anaconda3\lib\site-packages\scipy\integrate\_ivp\ivp.py", line 542, in solve_ivp
solver = method(fun, t0, y0, tf, vectorized=vectorized, **options)
File "...\anaconda3\lib\site-packages\scipy\integrate\_ivp\rk.py", line 94, in __init__
self.f = self.fun(self.t, self.y)
File "...\anaconda3\lib\site-packages\scipy\integrate\_ivp\base.py", line 138, in fun
return self.fun_single(t, y)
File "...\anaconda3\lib\site-packages\scipy\integrate\_ivp\base.py", line 20, in fun_wrapped
return np.asarray(fun(t, y), dtype=dtype)
File "...\anaconda3\lib\site-packages\pde\solvers\scipy.py", line 74, in rhs_helper
return rhs(state_flat.reshape(shape), t).flat # type: ignore
File "...\anaconda3\lib\site-packages\numba\core\dispatcher.py", line 420, in _compile_for_args
error_rewrite(e, 'typing')
File "...\anaconda3\lib\site-packages\numba\core\dispatcher.py", line 361, in error_rewrite
raise e.with_traceback(None)
TypingError: Cannot capture the non-constant value associated with variable 'y' in a function that will escape.
Since no one has posted an answer yet, I managed to get a minimal working example by using scipy odeint with a method of lines to solve the PDE, that is, by discretizing the Laplace operator, and then wrapping the differential equation inside fsolve to get dydt:
import numpy as np
from scipy.integrate import odeint
from scipy.optimize import fsolve
a=1
b=1
t = np.linspace(0,1,100)
y0 = np.asarray([1,0.5,0]) #starting values
N=len(y0)
def laplace(y):
""" Laplace operator in cartesian coordinate system """
d2y_0 = [2*(y[1] - y[0])] #at i=0
d2y_N = [2*(y[-2] - y[-1])] #at i=N
d2y_i = [y[i+2] - 2*y[i+1] + y[i] for i in range(N-2)] #elsewhere
return np.asarray(d2y_0 + d2y_i + d2y_N)
def rhs(y, dydt, t):
""" RHS of PDE including dydt term """
return a*laplace(y) + b*laplace(dydt)
def pde(y, t):
""" Function that solves for dydt using root finding """
return fsolve(lambda dydt : dydt - rhs(y, dydt, t),np.zeros(N))
#calculate
sol = odeint(pde, y0, t)
#plot odeint with fsolve
import matplotlib.pyplot as plt
plt.figure()
plt.plot(t,sol)
plt.grid(ls='--')
plt.xlabel('$t$')
plt.ylabel('$y_i$')
plt.title('odeint with fsolve')
plt.legend(["$i=$"+str(i) for i in range(N)])
#plot theoretical
u10=y0[0]
u20=y0[1]
u30=y0[2]
E1=np.exp(-2*a*t/(1+2*b)) #1st exponential term
E2=np.exp(-4*a*t/(1+4*b)) #2nd exponential term
u1 = 1/4*((1+2*E1+E2)*u10 - 2*(E2-1)*u20 + (1-2*E1+E2)*u30)
u2 = 1/4*((1-E2)*u10 + 2*(E2+1)*u20 + (1-E2)*u30)
u3 = 1/4*((1-2*E1+E2)*u10 - 2*(E2-1)*u20 + (1+2*E1+E2)*u30)
plt.figure()
plt.plot(t,u1,t,u2,t,u3)
plt.grid(ls='--')
plt.xlabel('$t$')
plt.ylabel('$y_i$')
plt.title('Theoretical')
plt.legend(["$i=$"+str(i) for i in range(N)])
Note that the same Laplace discretization method allows us to solve a system of ODEs to get the exact analytical solution with which we verify our numerical calculation (for a N=3 size system):
>>> np.allclose(sol[:,0],u1)
True
>>> np.allclose(sol[:,1],u2)
True
>>> np.allclose(sol[:,2],u3)
True
Seems it works as intended.

Theano tensor length unknown for division but ok for addition in pymc3 hierarchical model

I am trying to run a hierarchical model with pymc3 in a Win10 environment using Spyder.
I have some global model parameters (theta, omega, sigma) and one specific parameter (Ci).
It takes a pd Dataframe as input that contains the relevant data. First column is called 'Cohort', second column is called 'Period', third column contains the observations.
The number of observations differs between cohorts.
The code looks like this:
import pymc3 as pm
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import theano.tensor as tt
cohorts_idx, cohorts = pd.factorize(inputs['Cohort'], sort = True)
periods_idx, periods = pd.factorize(inputs['Period'], sort = True)
coords = {
"cohort": cohorts,
"period": periods,
"collections": np.arange(len(cohorts_idx))
}
with pm.Model(coords = coords) as model:
# global model parameters
omega = pm.HalfNormal("omega", sigma = 3)
theta = pm.HalfNormal("theta", sigma = 5)
sigma = pm.HalfNormal("sigma", sigma = 20)
# cohort specific parameter
Ci = pm.TruncatedNormal("Ci", mu = 60, sigma = 10, lower = 10, upper = 110, dims = "cohort")
mu_i_t = Ci[cohorts_idx] * (1 - tt.exp(- (periods[periods_idx] / theta) ** omega))
sigma_i_t = sigma * mu_i_t ** 0.5
_ = pm.Normal("Collections_i_t",
mu = mu_i_t,
sigma = sigma_i_t,
observed = inputs['Collections'],
dims = "collections")
results = pm.sample(draws = 1000, tune = 1000, cores = 8)
return pm.summary(results)
The resulting error message is:
mu_i_t = Ci[cohorts_idx] * (1 - tt.exp(- (periods[periods_idx] / theta) ** omega))
File "C:\Users\alexi\Anaconda3\lib\site-packages\pandas\core\indexes\base.py", line 139, in
index_arithmetic_method
result = op(Series(self), other)
File "C:\Users\alexi\Anaconda3\lib\site-packages\pandas\core\ops\common.py", line 64, in new_method
return method(self, other)
File "C:\Users\alexi\Anaconda3\lib\site-packages\pandas\core\ops\__init__.py", line 505, in wrapper
return _construct_result(left, result, index=left.index, name=res_name)
File "C:\Users\alexi\Anaconda3\lib\site-packages\pandas\core\ops\__init__.py", line 478, in _
construct_result
out = left._constructor(result, index=index)
File "C:\Users\alexi\Anaconda3\lib\site-packages\pandas\core\series.py", line 279, in __init__
data = com.maybe_iterable_to_list(data)
File "C:\Users\alexi\Anaconda3\lib\site-packages\pandas\core\common.py", line 280, in
maybe_iterable_to_list
return list(obj)
File "C:\Users\alexi\Anaconda3\lib\site-packages\theano\tensor\var.py", line 640, in __iter__
for i in xrange(theano.tensor.basic.get_vector_length(self)):
File "C:\Users\alexi\Anaconda3\lib\site-packages\theano\tensor\basic.py", line 4828, in
get_vector_length
raise ValueError("length not known: %s" % msg)
ValueError: length not known: Elemwise{true_div,no_inplace} [id A] ''
|TensorConstant{[ 1 2 3 .. 1 2 1]} [id B]
|InplaceDimShuffle{x} [id C] ''
|ViewOp [id D] 'theta'
|Elemwise{exp,no_inplace} [id E] ''
|theta_log__ [id F]
I have no clue why. Note that if instead of dividing by Theta in the line that causes an error, I do an addition instead, then it works (obviously it is not what I want though).
How can I resolve this and get this division working?
OK, I found out. I am not good enough to explain why, but the following works. One needs to replace line:
mu_i_t = Ci[cohorts_idx] * (1 - tt.exp(- (periods[periods_idx] / theta) ** omega))
by:
mu_i_t = Ci[cohorts_idx] * (1 - tt.exp(- (periods[periods_idx].to_numpy() / theta) ** omega))

curve_fit with ODE of unknown coefficients

I'm trying to solve the equation Iy'' + b|y'|y' + ky = 0 and fit the coefficients to data.
This is the code I have so far (ready to run):
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
import pandas as pd
from scipy.optimize import curve_fit
# Define derivatives of function
def f(y, t, I, b, k):
theta, omega = y
derivs = [omega, -b / I * omega * abs(omega) - k / I * theta]
return derivs
# integrate the function
def yint(t, I, b, k, y0, y1):
y = odeint(f, [y0, y1], t, args=(I, b, k))
return y.ravel()
# define t and y to fit
y0 = [.5, 0]
t = np.arange(0, 3, .01)
y = np.cos(t)*np.e**(-.01*t)
# fit
vals, cov = curve_fit(yint, t, y, p0=[0.002245, 1e-5, 0.2492, y0[0], y[1]])
However, when I run the function, I get the error:
Traceback (most recent call last):
File "---", line 24, in <module>
vals, cov = curve_fit(yint, t, y, p0=[0.002245, 1e-5, 0.2492, y0[0], y[1]])
File "---.py", line 578, in curve_fit
res = leastsq(func, p0, args=args, full_output=1, **kw)
File "---.py", line 371, in leastsq
shape, dtype = _check_func('leastsq', 'func', func, x0, args, n)
File "---.py", line 20, in _check_func
res = atleast_1d(thefunc(*((x0[:numinputs],) + args)))
File "---.py", line 447, in _general_function
return function(xdata, *params) - ydata
ValueError: operands could not be broadcast together with shapes (600,) (300,)
Any thoughts on how to fix this?
The problem is that function yint returns an array of shape (600,) for the argument of shape (300,). Think again about yint: it solves a second-order differential equation by representing it as a system of two first-order equations. So the result of y = odeint(...) has two columns, one for the solution itself, the second for its derivative. Its shape is (300,2). Mashing the solution and its derivative together with ravel does not make sense. Instead, you should only take the actual solution, that's the thing you are fitting.
So, replace
return y.ravel()
with
return y[:, 0]

Fit an integral function with parametric limit to data with Python (Debye Model)

I am trying to fit resistivity vs temperature data to Bloch-Gruneisen formula for resistivity in metals:
function
as you can see there is an integral function with a parametric limit. I don't know how to implement an algorithm to run a least squares fit.
I came up with:
import matplotlib.pyplot as plt
import numpy as np
import pylab as pl
import scipy as sp
from scipy.optimize import leastsq
#retrieve data from file
data = pl.loadtxt('salita.txt')
Temp = data[:, 0]
Res = data[:, 2]
def debye_func(p, T, r):
rho0, AD, TD = p
coeff = AD*np.power(T, 5)/np.power(TD, 4)
f = np.power(x^5)/np.power(np.sinh(x), 2) #function to integrate
err_debye = r - rho0 - coeff * #integral???
return err_debye
p0 = sp.array([0.0001 , 0.00001, 50])
plsq = leastsq(debye_func, p0, args=(Temp, Res))
print plsq
Ideas on how could I write it?
EDIT: my code has become:
import matplotlib.pyplot as plt
import numpy as np
import pylab as pl
import scipy as sp
from scipy.optimize import leastsq
from scipy.integrate import quad
#retrieve data from file
data = pl.loadtxt('salita.txt')
Temp = data[:, 0]
Res = data[:, 2]
def debye_integrand(x):
return np.power(x, 5)/np.power(np.sinh(x), 2)
def debye_func(p, T, r):
rho0, AD, TD = p
coeff = AD*np.power(T, 5)/np.power(TD, 4)
err_debye = r - rho0 - coeff * quad(debye_integrand, 0, TD/(2*T))
return err_debye
p0 = sp.array([0.0001 , 0.00001, 50])
plsq = leastsq(debye_func, p0, args=(Temp, Res))
print plsq
Now I get a ValueError:
Traceback (most recent call last):
File "debye.py", line 24, in <module>
plsq = leastsq(debye_func, p0, args=(Temp, Res))
File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/scipy/optimize/minpack.py", line 348, in leastsq
m = _check_func('leastsq', 'func', func, x0, args, n)[0]
File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/scipy/optimize/minpack.py", line 14, in _check_func
res = atleast_1d(thefunc(*((x0[:numinputs],) + args)))
File "debye.py", line 19, in debye_func
err_debye = r - rho0 - coeff * quad(debye_integrand, 0, TD/(2*T))
File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/scipy/integrate/quadpack.py", line 247, in quad
retval = _quad(func,a,b,args,full_output,epsabs,epsrel,limit,points)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/scipy/integrate/quadpack.py", line 296, in _quad
if (b != Inf and a != -Inf):
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
I think that means I'm providing leastsq an argument it can't take, but I don't know how to modify my code.
EDIT2: I solved my equation analytically with Maxima and I got
import matplotlib.pyplot as plt
import numpy as np
import pylab as pl
import scipy as sp
from scipy.optimize import leastsq
from scipy.integrate import quad
from scipy.special import zetac
from mpmath import polylog
#retrieve data from file
data = pl.loadtxt('salita.txt')
Temp = data[:, 0]
Res = data[:, 2]
def debye_integrand(x):
return np.power(x, 5)/np.power(np.sinh(x), 2)
def debye_func(p, T, r, integral):
rho0, AD, TD = p
coeff = AD*np.power(T, 5)/np.power(TD, 4)
den = np.exp(TD/T) -1
m1 = 5*((TD/(2*T))**4)*np.log(np.exp(TD/(2*T)+1)*(np.exp(TD/T)-1)+120*polylog(5, np.exp(TD/(T))*(1-np.exp(TD/(2*T)))
m2 = 120*(TD/(2*T))*polylog(4, np.exp(TD/(2*T)))*(np.exp(np.exp(TD/T))-1)+60*((TD/(2*T))**2)*polylog(3, np.exp(TD/(2*T))*(1-np.exp((TD/(2*T)))
m3 = 20*((TD/(2*T))**3)*polylog(2, np.exp(TD/(2*T))*(np.exp(TD/T)-1)+120**polylog(5, -np.exp(TD/(2*T)))*(1-np.exp(TD/T))
m4 = 120*(TD/(2*T))*polylog(4, -np.exp(TD/(2*T)))*(np.exp(TD/T)-1)+60*((TD/(2*T))**2)*polylog(3, -np.exp(TD/(2*T)))*(1-np.exp(TD/T))
m5 = 20*((TD/(2*T))**3)*polylog(2, -np.exp(TD/(2*T)))*(np.exp(TD/T)-1) -2*((TD/(2*T))**5)*np.exp(TD/T)
m6 = 5*((TD/(2*T))**4)*np.log(1-np.exp(TD/(2*T))*(np.exp(TD/T)-1)
zeta = 15.0*zetac(5)/2
integral = (m1+m2+m3+m4+m5+m6)/den +zeta
err_debye = r - rho0 - coeff * integral
return err_debye
#initizalied with Einstein model fit
p0 = sp.array([0.00001 , 0.0000001, 70.0])
plsq = leastsq(debye_func, p0, args=(Temp, Res))
print plsq
It says SyntaxError: invalid syntax in m2. I tried to do it with loops in the numerical way, but I didn't succeed.
My .txt file is here, if you want to try. First column is temperature, third one is resistivity.
You could, for instance, separately define the integrand function,
def debye_integrand(x, n):
return x**n/((np.exp(x) - 1)*(1 - np.exp(-x)))
and then use scipy.integrate.quad to do this integration numerically,
from scipy.integrate import quad
def debye_func(p, T, r):
# [...] the rest of your code from above here
err_debye = r - rho0 - coeff * quad(debye_integrand, 0, T/TD, args=(n,))
return np.sum(err_debye**2)
That's the general idea, and this might need to be adapted further to your code. An ideal solution would be to find an analytical solution to that integral, or rewrite it with classical integral functions from scipy.special, but it might not be straightforward (see below).
Also you should use the more general scipy.opitimize.minimize function instead of the least-square fit, since it provides algorithms that are more efficient and robust for non-smooth optimizations. The default optimization method BFGS is a good start.
Edit: actually, there is an analytical solution to this integral (for n=5), that you can obtain, for instance, with Maxima,
>> integrate(x**5/((exp(x) - 1)*(1 - exp(-x))), x, 0, a)
where a is the integration limit, li_k the Polylogarithm function of order k (see mpmath.polylog) and ζ is the Riemann Zeta function (see scipy.special.zetac).
Although, depending on your needs, it might be faster to just go with a numerical integration (or pre-calculated table lookup) rather than puyting all of this together, and converting it to python.
Edit 2: Here the final solution with analytical calculation of the integral,
import numpy as np
import mpmath as mp
from scipy.optimize import minimize
from scipy.integrate import quad
import matplotlib.pyplot as plt
def debye_integral_sym_scalar(x):
"""
Calculate the Debye integral for a scalar using multi precision math,
as otherwise it overflows with 64bit floats
"""
exp_x = mp.exp(x)
m1 = -120*mp.polylog(5, exp_x)
m2 = 120*x*mp.polylog(4, exp_x)
m3 = -60*x**2*mp.polylog(3, exp_x)
m4 = 20*x**3*mp.polylog(2, exp_x)
m5 = 5*x**4*mp.log(1 - exp_x)
m6 = - x**5*exp_x
return m1 + m2 + m3 + m4 + m5 + m6/(exp_x - 1) + 120*mp.zeta(5)
# this is the actual function that we can use
def debye_integral_sym(x):
f = np.vectorize(debye_integral_sym_scalar, otypes=[np.complex])
return f(x).real
def debye_integrand(x, n):
return x**n/((np.exp(x) - 1)*(1 - np.exp(-x)))
# test that debye_integral_sym returns the same result as quad
a = 10.0
res0 = quad(debye_integrand, 0, a, args=(5,))[0]
res1 = debye_integral_sym(a)
np.testing.assert_allclose(res0, res1)
def resistivity_fit(p, T):
rho0, AD, TD = p
coeff = AD*np.power(T, 5)/np.power(TD, 4)
return rho0 + coeff * debye_integral_sym(TD/(2*T))
def debye_err_func(p, T, r):
return np.sum((r - resistivity_fit(p, T))**2)
# wget "http://pastebin.com/raw.php?i=tvzcdxYA" -O salita.txt
data = np.loadtxt('salita.txt')
temp_exp = data[:, 0]
res_exp = data[:, 2]
p0 = np.array([0.0001 , 0.00001, 50])
p_opt = minimize(debye_err_func, p0, args=(temp_exp, res_exp))
print p_opt
temp = np.linspace(temp_exp.min(), temp_exp.max(), 100)
plt.plot(temp_exp, res_exp, '.', label='Experimental data')
plt.plot(temp, resistivity_fit(p_opt.x, temp), 'r', label='Bloch-Gruneisen fit')
plt.legend(loc='best')
plt.xlabel('Temperature [K]')
plt.ylabel('Resistivity')
plt.show()
With the output of the optimization function,
status: 0
success: True
njev: 5
nfev: 25
hess_inv: array([[ 7.32764243e-01, -4.89555962e-01, -1.93879729e-08],
[ -4.89555962e-01, 3.27690582e-01, -2.09510086e-08],
[ -1.93879729e-08, -2.09510086e-08, 1.00000000e+00]])
fun: 1.784420370873494e-11
x: array([ 9.96468440e-06, 7.40349389e-06, 5.00000000e+01])
message: 'Optimization terminated successfully.'
jac: array([ -1.11880569e-06, 1.28115957e-06, 2.31303410e-12])
and the resulting plot,

Categories

Resources