Source code for Stoner.analysis.fitting.models.e_transport

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Model classes and functions for various models of electron transport (other than tunnelling processes)."""
# pylint: disable=invalid-name
__all__ = ["BlochGrueneisen", "FluchsSondheimer", "WLfit", "blochGrueneisen", "fluchsSondheimer", "wlfit"]

import numpy as np
from scipy.integrate import quad
from scipy.special import digamma

from lmfit import Model
from lmfit.models import update_param_vals

try:  # numba is an optional dependency
    from numba import jit, float64, int64
except ImportError:
    from ....compat import _dummy, _jit as jit

    float64 = _dummy()
    int64 = _dummy()


@jit(float64(float64, int64), nopython=True)
def _bgintegrand(x, n):
    """Calculate the integrand for the Bloch Grueneisen model."""
    return x**n / ((np.exp(x) - 1) * (1 - np.exp(-x)))


[docs]def wlfit(B, s0, DS, B1, B2): """Implement the Weak localisation fitting function. Args: B (array): mag. field s0 (float): zero field conductance DS (float): scaling parameter B1 (float): elastic characteristic field (B1) B2 (float): inelastic characteristic field (B2) Returns: Conductance vs Field for a weak localisation system Notes: 2D WL model as per Wu et al PRL 98, 136801 (2007), Porter et al PRB 86, 064423 (2012) Example: .. plot:: samples/Fitting/weak_localisation.py :include-source: :outname: wlfit """ e = 1.6e-19 # C h = 6.62e-34 # Js # Sets up conductivity fit array cond = np.zeros(len(B)) if B2 == B1: B2 = B1 * 1.00001 # prevent dividing by zero # performs calculation for all parts for tt, Bi in enumerate(B): if Bi != 0: # prevent dividing by zero WLpt1 = digamma(0.5 + B2 / np.abs(Bi)) WLpt2 = digamma(0.5 + B1 / np.abs(Bi)) else: WLpt1 = (digamma(0.5 + B2 / np.abs(B[tt - 1])) + digamma(0.5 + B2 / np.abs(B[tt + 1]))) / 2 WLpt2 = (digamma(0.5 + B1 / np.abs(B[tt - 1])) + digamma(0.5 + B1 / np.abs(B[tt + 1]))) / 2 WLpt3 = np.log(B2 / B1) # Calculates fermi level smearing cond[tt] = (e**2 / (h * np.pi)) * (WLpt1 - WLpt2 - WLpt3) # cond = s0*cond / min(cond) cond = s0 + DS * cond return cond
[docs]def fluchsSondheimer(t, l, p, sigma_0): """Evaluate a Fluchs-Sondheumer model function for conductivity. Args: t (array): Thickness values l (float): mean-free-path p (float): reflection co-efficient sigma_0 (float): intrinsic conductivity Return: Reduced Resistivity Note: Expression used from: G.N.Gould and L.A. Moraga, Thin Solid Films 10 (2), 1972 pp 327-330 Example: .. plot:: samples/Fitting/f_s.py :include-source: :outname: fluchssondheimer """ k = t / l def kernel(x, k): return (x - x**3) * np.exp(-k * x) / (1 - np.exp(-k * x)) result = np.zeros(k.shape) for i, v in enumerate(k): ret1 = 1 - (3 * (1 - p) / (8 * v)) + (3 * (1 - p) / (2 * v)) ret2 = quad(kernel, 0, 1, (v,))[0] result[i] = ret1 * ret2 return result / sigma_0
[docs]def blochGrueneisen(T, thetaD, rho0, A, n): """Calculate the BlochGrueneiseen Function for fitting R(T). Args: T (array): Temperature Values to fit thetaD (float): Debye Temperature rho0 (float): Residual resisitivity A (float): scattering scaling factor n (float): Exponent term Return: Evaluation of the BlochGrueneisen function for R(T) Example: .. plot:: samples/Fitting/b_g.py :include-source: :outname: blochgruneisen """ ret = np.zeros(T.shape) for i, t in enumerate(T): intg = quad(_bgintegrand, 0, thetaD / (t), (n,))[0] ret[i] = rho0 + A * (t / thetaD) ** n * intg return ret
[docs]class WLfit(Model): """Weak localisation model class. Args: B (array): mag. field s0 (float): zero field conductance DS (float): scaling parameter B1 (float): elastic characteristic field (B1) B2 (float): inelastic characteristic field (B2) Returns: Conductance vs Field for a weak localisation system Notes: 2D WL model as per Wu et al PRL 98, 136801 (2007), Porter et al PRB 86, 064423 (2012) Example: .. plot:: samples/Fitting/weak_localisation.py :include-source: :outname: wlfit """ display_names = [r"\sigma_0", "D_S", "B_1", "B_2"] def __init__(self, *args, **kwargs): """Configure Initial fitting function.""" super().__init__(wlfit, *args, **kwargs)
[docs] def guess(self, data, x=None, **kwargs): """Guess parameters for weak localisation fit.""" s0, DS, B1, B2 = 1.0, 1.0, 1.0, 1.0 if x is not None: zpos = np.argmin(np.abs(x)) s0 = data[zpos] B1 = np.max(x) / 20.0 B2 = B1 * 10 DS = 1.0 pars = self.make_params(s0=s0, DS=DS, B1=B1, B2=B2) for p in pars: pars[p].min = 0.0 return update_param_vals(pars, self.prefix, **kwargs)
[docs]class FluchsSondheimer(Model): """Evaluate a Fluchs-Sondheumer model function for conductivity. Args: t (array): Thickness values l (float): mean-free-path p (float): reflection co-efficient sigma_0 (float): intrinsic conductivity Return: Reduced Resistivity Note: Expression used from: G.N.Gould and L.A. Moraga, Thin Solid Films 10 (2), 1972 pp 327-330 Example: .. plot:: samples/Fitting/f_s.py :include-source: :outname: fluchsdondheimer-class """ display_names = [r"\lambda_{mfp}", "p_{refl}", r"\sigma_0"] units = ["nm", "", r"\Omega^{-1}m^{-1}"] def __init__(self, *args, **kwargs): """Configure Initial fitting function.""" super().__init__(fluchsSondheimer, *args, **kwargs)
[docs] def guess(self, data, t=None, **kwargs): # pylint: disable=unused-argument """Guess some starting values - not very clever.""" pars = self.make_params(l=10.0, p=0.5, sigma_0=10.0) return update_param_vals(pars, self.prefix, **kwargs)
[docs]class BlochGrueneisen(Model): """BlochGrueneiseen Function for fitting R(T). Args: T (array): Temperature Values to fit thetaD (float): Debye Temperature rho0 (float): Residual resisitivity A (float): scattering scaling factor n (float): Exponent term Return: Evaluation of the BlochGrueneisen function for R(T) Example: .. plot:: samples/Fitting/b_g.py :include-source: :outname: blochgruneisen-class """ display_names = [r"\theta_D", r"\rho_0", "A", "n"] def __init__(self, *args, **kwargs): """Configure Initial fitting function.""" super().__init__(blochGrueneisen, *args, **kwargs)
[docs] def guess(self, data, x=None, **kwargs): # pylint: disable=unused-argument """Guess some starting values - not very clever.""" rho0 = data.min() if x is None: t = np.linspace(0, 1, len(data)) else: t = x / x.max() y = data - data.min() t = t[y > 0.05 * y.max()] y = y[y > 0.05 * y.max()] A = np.polyfit(t, y, 1)[0] pars = self.make_params(thetaD=500, rho0=rho0, A=A, n=5.0) pars["A"].min = 0 pars["n"].vary = False return update_param_vals(pars, self.prefix, **kwargs)