In [1]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import scipy.optimize as opt
from scipy.integrate import quad, dblquad
from IPython.html.widgets import interact, interactive, fixed
from IPython.display import display


:0: FutureWarning: IPython widgets are experimental and may change in the future.

Constants


In [2]:
# 1D ~ 3.3e-30 C.m
DebyeToSI = (1/(299792458))*1e-21
kb = 1.3806488e-23

1. Import Excel Data


In [3]:
thetamin = 17.8 * (np.pi/180)
thetamax = 33.4 * (np.pi/180)

In [4]:
df = pd.read_excel('ShenData.xlsx', sheetname=None)

In [5]:
del df['Intro']

In [6]:
for key in df:
    del df[key]['Vp']
    del df[key]['θ1']
    del df[key]['θ2']
    del df[key]['Extinction +']
    del df[key]['Extinction -']
    del df[key]['Retardation']
    del df[key]['Birefringence']

In [7]:
df['40C']


Out[7]:
Field (V/μm) Tilt Angle Temperature (Celsius)
0 0.0000 0.0000 40
1 2.1388 7.0985 NaN
2 3.2494 18.5636 NaN
3 4.3599 23.7588 NaN
4 5.3470 28.0582 NaN
5 6.4987 29.9840 NaN
6 8.6375 30.7006 NaN
7 12.9973 31.6411 NaN
8 17.3573 32.5368 NaN
9 26.0771 33.4325 NaN
10 34.7969 33.4325 NaN
11 43.5167 33.4325 NaN
12 52.1954 33.4325 NaN
13 60.8740 33.4325 NaN

In [8]:
Fields = np.array([df[key]['Field (V/μm)'] for key in df])
Angles = np.array([df[key]['Tilt Angle'] for key in df])
TempsC = np.array([df[key]['Temperature (Celsius)'][0] for key in df])
TempsK = np.array([entry + 273.15 for entry in TempsC])

In [9]:
plt.figure(figsize=(8,6));
colors = np.array(['red','green','blue','orange','brown','pink', 'purple','black','cyan','magenta','yellow','gray'])
for i in range(len(Fields)):
    plt.scatter(Fields[i],Angles[i],c=colors[i],label= str(int(TempsC[i])) + '$^\circ$C',marker = 'o',s=50, edgecolor = colors[i]);
plt.xlabel('Field (V/$\mu$m)',size=15);
plt.ylabel('Tilt Angle',size=15);
plt.title('Tilt Angle for Shen Data')
plt.xlim(0,70);
plt.ylim(0,40);
plt.legend(loc=4);


2. Curve Fitting

Langevin-Debye Model

$$ U(\theta, \phi, E, \rho_0) = -\rho_0E\:sin\:\theta\:cos\:\phi\:(1+\alpha E\:cos\:\phi) $$
$$ tan(2\psi) = \frac{\langle sin\:2\theta\:cos\:{\phi}\rangle}{\langle{cos}^2{\theta} - {sin}^2{\theta}\:{cos}^2{\phi}\rangle} $$
$$ \langle X \rangle = \int_{\theta_{min}}^{\theta_{max}} \int_0^{2\pi} X(\theta,\phi) f(\theta, \phi) sin(\theta) d\theta d\phi $$
$$ f(\theta,\phi) = \frac{e^{\frac{-U}{k_b T}}}{\int_{\theta_{min}}^{\theta_{max}} \int_0^{2\pi} e^{\frac{-U}{k_b T}} \:sin\:{\theta}\: d\theta d\phi} $$
$$ tan(2\psi) = \frac{\int_{\theta_{min}}^{\theta_{max}} \int_0^{2\pi} {sin\:{2\theta}\:cos\:{\phi}}\:e^{\frac{-U}{k_bT}}\:sin\:{\theta}\: d\theta d\phi}{\int_{\theta_{min}}^{\theta_{max}} \int_0^{2\pi} ({{cos}^2{\theta} - {sin}^2{\theta}\:{cos}^2{\phi}})\:e^{\frac{-U}{k_bT}}\:sin\:{\theta}\: d\theta d\phi} $$

Computing


In [32]:
#Test values at 40C
E_array = np.array(Fields[-1])
T = TempsK[-1]
PSI = np.array(Angles[-1])

#Phenomenological points from Shen paper at 40C
p0 = 619 #Debye
alpha = .338 # Micron/Volt


#Convert to SI
p0 = p0 * DebyeToSI
alpha = .338 * 1e-6
E = E_array[1]*1e6


#Combine p0 and kb to a single more reasonable number
p0k = p0/kb


#Scaling try: Puts E, alpha, and p0k all on the same order of magnitude ~200.
E = E*1e-4
alpha = alpha*1e9
p0k = p0k*1e6
E_array = E_array*1e2

In [33]:
def compute_psi(E,p0k,alpha):
    """Computes the tilt angle(psi) via the tan(2psi) equation at one temperature.
    Returns
    -------
    Float:
    The statistical tilt angle with conditions T,p0k,alpha,E"""
    
    def Boltz(theta,phi,T,p0k,alpha,E):
        """Compute the integrand for the Boltzmann factor.
        Returns
        -------
        A function of theta,phi,T,p0k,alpha,E to be used within dblquad
        """
        return np.exp((1/T)*p0k*E*np.sin(theta)*np.cos(phi)*((1*1e-2)+(alpha*E*np.cos(phi)*1e-7)))*np.sin(theta)
        #Factors: 1e-2 and 1-e7 and to cancel out the rescaling, of E,alpha, and p0k, done in the above cell
    
    
    def numerator(theta,phi,T,p0k,alpha,E):
        boltz = Boltz(theta,phi,T,p0k,alpha,E)
        return np.sin(2*theta)*np.cos(phi)*boltz

    def denominator(theta,phi,T,p0k,alpha,E):
        boltz = Boltz(theta,phi,T,p0k,alpha,E)
        return (np.cos(theta)**2 - ((np.sin(theta)*np.cos(phi))**2))*boltz
    
    avg_numerator, avg_numerator_error = dblquad(numerator, 0, 2*np.pi, lambda theta1: thetamin, lambda theta2: thetamax,args=(T,p0k,alpha,E))
    
    avg_denominator, avg_denominator_error = dblquad(denominator, 0, 2*np.pi, lambda theta3: thetamin, lambda theta4: thetamax,args=(T,p0k,alpha,E))
    
    psi = np.arctan(avg_numerator / avg_denominator) * (180 /(2*np.pi)) #Convert to degrees from radians and divide by two
    return psi

In [34]:
compute_psi(E,p0k,alpha)


Out[34]:
7.8605662621993444

In [39]:
plt.plot(E_array*1e-2,[compute_psi(entry,p0k,alpha) for entry in E_array],label = "Compute Output")
plt.scatter(E_array*1e-2,PSI,label="Shen's Data",color = 'red');
#Factor: 1e-2 is added to cancel out the rescaling of E_array
plt.xlabel('Field (V/$\mu$m)',size=15);
plt.ylabel('Tilt Angle',size=15);
plt.title('Tilt Angle Comparison for ' + str(T-273.15) + "$^\circ$C");
plt.xlim(0,70);
plt.ylim(0,40);
plt.legend(loc=4);


Compute_psi is working as intended. When supplied the correct parameters as taken from Shen's paper, Compute_psi outputs the same curve as seen in Shen's paper.

Scipy Curve Fit

Now that compute_psi is functioning, the goal is to reverse engineer the same phenomonological parameters (alpha, p0) that were used above.


In [46]:
guess = (p0k+100,alpha+100)

In [47]:
theta_best, theta_cov = opt.curve_fit(compute_psi, E_array, PSI, guess)


---------------------------------------------------------------------------
error                                     Traceback (most recent call last)
<ipython-input-47-138cce4ca107> in <module>()
----> 1 theta_best, theta_cov = opt.curve_fit(compute_psi, E_array, PSI, guess)

C:\Anaconda3\lib\site-packages\scipy\optimize\minpack.py in curve_fit(f, xdata, ydata, p0, sigma, absolute_sigma, check_finite, **kw)
    579     # Remove full_output from kw, otherwise we're passing it in twice.
    580     return_full = kw.pop('full_output', False)
--> 581     res = leastsq(func, p0, args=args, full_output=1, **kw)
    582     (popt, pcov, infodict, errmsg, ier) = res
    583 

C:\Anaconda3\lib\site-packages\scipy\optimize\minpack.py in leastsq(func, x0, args, Dfun, full_output, col_deriv, ftol, xtol, gtol, maxfev, epsfcn, factor, diag)
    369     if not isinstance(args, tuple):
    370         args = (args,)
--> 371     shape, dtype = _check_func('leastsq', 'func', func, x0, args, n)
    372     m = shape[0]
    373     if n > m:

C:\Anaconda3\lib\site-packages\scipy\optimize\minpack.py in _check_func(checker, argname, thefunc, x0, args, numinputs, output_shape)
     18 def _check_func(checker, argname, thefunc, x0, args, numinputs,
     19                 output_shape=None):
---> 20     res = atleast_1d(thefunc(*((x0[:numinputs],) + args)))
     21     if (output_shape is not None) and (shape(res) != output_shape):
     22         if (output_shape[0] != 1):

C:\Anaconda3\lib\site-packages\scipy\optimize\minpack.py in _general_function(params, xdata, ydata, function)
    445 
    446 def _general_function(params, xdata, ydata, function):
--> 447     return function(xdata, *params) - ydata
    448 
    449 

<ipython-input-33-10e4f9b5066a> in compute_psi(E, p0k, alpha)
     25         return (np.cos(theta)**2 - ((np.sin(theta)*np.cos(phi))**2))*boltz
     26 
---> 27     avg_numerator, avg_numerator_error = dblquad(numerator, 0, 2*np.pi, lambda theta1: thetamin, lambda theta2: thetamax,args=(T,p0k,alpha,E))
     28 
     29     avg_denominator, avg_denominator_error = dblquad(denominator, 0, 2*np.pi, lambda theta3: thetamin, lambda theta4: thetamax,args=(T,p0k,alpha,E))

C:\Anaconda3\lib\site-packages\scipy\integrate\quadpack.py in dblquad(func, a, b, gfun, hfun, args, epsabs, epsrel)
    497     """
    498     return quad(_infunc, a, b, (func, gfun, hfun, args),
--> 499                 epsabs=epsabs, epsrel=epsrel)
    500 
    501 

C:\Anaconda3\lib\site-packages\scipy\integrate\quadpack.py in quad(func, a, b, args, full_output, epsabs, epsrel, limit, points, weight, wvar, wopts, maxp1, limlst)
    309     if (weight is None):
    310         retval = _quad(func, a, b, args, full_output, epsabs, epsrel, limit,
--> 311                        points)
    312     else:
    313         retval = _quad_weight(func, a, b, args, full_output, epsabs, epsrel,

C:\Anaconda3\lib\site-packages\scipy\integrate\quadpack.py in _quad(func, a, b, args, full_output, epsabs, epsrel, limit, points)
    374     if points is None:
    375         if infbounds == 0:
--> 376             return _quadpack._qagse(func,a,b,args,full_output,epsabs,epsrel,limit)
    377         else:
    378             return _quadpack._qagie(func,bound,infbounds,args,full_output,epsabs,epsrel,limit)

C:\Anaconda3\lib\site-packages\scipy\integrate\quadpack.py in _infunc(x, func, gfun, hfun, more_args)
    444     b = hfun(x)
    445     myargs = (x,) + more_args
--> 446     return quad(func,a,b,args=myargs)[0]
    447 
    448 

C:\Anaconda3\lib\site-packages\scipy\integrate\quadpack.py in quad(func, a, b, args, full_output, epsabs, epsrel, limit, points, weight, wvar, wopts, maxp1, limlst)
    309     if (weight is None):
    310         retval = _quad(func, a, b, args, full_output, epsabs, epsrel, limit,
--> 311                        points)
    312     else:
    313         retval = _quad_weight(func, a, b, args, full_output, epsabs, epsrel,

C:\Anaconda3\lib\site-packages\scipy\integrate\quadpack.py in _quad(func, a, b, args, full_output, epsabs, epsrel, limit, points)
    374     if points is None:
    375         if infbounds == 0:
--> 376             return _quadpack._qagse(func,a,b,args,full_output,epsabs,epsrel,limit)
    377         else:
    378             return _quadpack._qagie(func,bound,infbounds,args,full_output,epsabs,epsrel,limit)

error: Supplied function does not return a valid float.

In [48]:
E,alpha,p0k


Out[48]:
(213.88000000000002, 338.0, 149.55010639031164)

In [18]:
E*1e-4


Out[18]:
213.88000000000002

In [19]:
alpha*1e9


Out[19]:
338.0

In [20]:
p0k*1e6


Out[20]:
149.55010639031164