Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crystal: add calculation of the temperature factor #46

Open
srio opened this issue Sep 23, 2016 · 3 comments
Open

Crystal: add calculation of the temperature factor #46

srio opened this issue Sep 23, 2016 · 3 comments

Comments

@srio
Copy link
Member

srio commented Sep 23, 2016

Enhancement for the future

@SergioLordano
Copy link

Dear Manuel,

Meanwhile, do you know another way of calculating the temperature factor besides using the XOP in IDL? Perhaps another software or reference article?

@SergioLordano
Copy link

SergioLordano commented Jan 20, 2022

Dear @srio,

I found the reference you used inside the file debyewaller.pro, and inplemented an equivalent python function. I tested against XOP 2.4 using Si111 and Si311, and the results have a good match up to the 5th decimal number. Probably due to the different constant's values used. The only thing is that I could not find a pythonic source for the Debye temperature of the materials, so it must be given manually. Anyway, if you want, feel free to copy, modify, and even include it in Xoppy. Here it is:

(to test it for Si(111) and 80K, you can simply calcTemperatureFactor(80))

def calcTemperatureFactor(temperature, crystal='Si', debyeTemperature=644.92, 
                          millerIndex=[1,1,1], atomicMass=28.09,
                          dSpacing=3.1354162886330583):
    
    """
    Calculates the (Debye) temperature factor for single crystals.
    
    Parameters
    ----------
    temperature : float 
        Crystal temperature in Kelvin (positive number).
    crystal : str 
        Crystal single-element symbol (e.g. Si, Ge, ...).   
    debyeTemperature : float 
        Debye temperature of the crystal material in Kelvin.    
    millerIndex : array-like (1D), optional
        Miller indexes of the crystal orientation. For use with xraylib only.
    atomicMass : float, optional. 
        Atomic mass of the crystal element (amu unit). if atomicMass == 0, get from xraylib.
    dSpacing : float, optional. 
        dSpacing in Angstroms, given the crystal and millerIndex . if dSpacing == 0, get from xraylib.
        
    Returns:
    --------
    temperatureFactor : float
    
    Examples:
    ---------
        ### using xraylib:
            
        >>> calcTemperatureFactor(80, crystal='Si', millerIndex=[3,1,1], debyeTemperature=644.92, dSpacing=0, atomicMass=0)
        0.983851994268226
        
        
        ### forcing it to use given dSpacing and atomicMass:
        
        >>> calcTemperatureFactor(80, crystal='Si', millerIndex=[1,1,1], debyeTemperature=644.92, atomicMass=28.09, dSpacing=3.1354163)
        0.9955698950510736
    
    References:
    -----------
    
    [1]: A. Freund, Nucl. Instrum. and Meth. 213 (1983) 495-501
    
    [2]: M. Sanchez del Rio and R. J. Dejus, "Status of XOP: an x-ray optics software toolkit", 
         SPIE proc. vol. 5536 (2004) pp.171-174
        
    """
    
    def debyeFunc(x):
        return x / (np.exp(-x) - 1)
        
    def debyePhi(y):
        from scipy.integrate import quad
        integral = quad(lambda x: debyeFunc(x), 0, y)[0]
        return (1/y) * integral
    
    planck = 6.62607015e-34 # codata.Planck
    Kb = 1.380649e-23 # codata.Boltzmann
    atomicMassTokg = 1.6605390666e-27 # codata.atomic_mass
    
    try:        
        import xraylib
        h, k, l = millerIndex
        crystalDict = xraylib.Crystal_GetCrystal(crystal)
        if(dSpacing == 0):
            dSpacing = xraylib.Crystal_dSpacing(crystalDict, h, k, l)
        if(atomicMass == 0):
            atomicMass = xraylib.AtomicWeight(xraylib.SymbolToAtomicNumber(crystal))
        
    except:        
        print("xraylib not available. Please give dSpacing and atomicMass manually.")
        if((dSpacing == 0) or (atomicMass == 0)):
            return np.nan

    atomicMass *= atomicMassTokg # converting to [kg]
    dSpacing *= 1e-10 # converting to [m]
    
    x = debyeTemperature / (-1*temperature) # invert temperature sign (!!!)
    
    B0 = (3 * planck**2) / (2 * Kb * debyeTemperature * atomicMass) 
    
    BT = 4 * B0 * debyePhi(x) / x
    
    ratio = 1 / (2 * dSpacing)
    
    M = (B0 + BT) * ratio**2
    
    temperatureFactor = np.exp(-M)

    return temperatureFactor

@srio
Copy link
Member Author

srio commented Jan 20, 2022

Thanks a lot Sergio,
I will look at it and include it in xoppy. Actually, it comes in a good moment: I am refactoring some of that code.
Cheers, Manuel

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants