Scipy integrate.quad not returning the expected values

前端 未结 1 468
没有蜡笔的小新
没有蜡笔的小新 2021-01-27 22:21

I have the following function which I would like to numerically integrate using python,

Using scipy, I have written this code:

def voigt(a,u):

         


        
1条回答
  •  生来不讨喜
    2021-01-27 22:57

    According to the wikipedia article Voigt profile, the Voigt functions U(x,t) and V(x,t) may be expressed in terms of the complex Faddeeva function w(z):

    U(x,t) + i*V(x,t) = sqrt(pi/(4*t))*w(i*z)
    

    The Voigt function H(a,u) can be expressed in terms of U(x,t) as

    H(a,u) = U(u/a, 1/(4*a**2))/(a*sqrt(pi))
    

    (Also see the DLMF section on Voigt functions.)

    scipy has an implementation of the Faddeeva function in scipy.special.wofz. Using that, here's an implementation of the Voigt functions:

    from __future__ import division
    
    import numpy as np
    from scipy.special import wofz
    
    
    _SQRTPI = np.sqrt(np.pi)
    _SQRTPI2 = _SQRTPI/2
    
    def voigtuv(x, t):
        """
        Voigt functions U(x,t) and V(x,t).
    
        The return value is U(x,t) + 1j*V(x,t).
        """
        sqrtt = np.sqrt(t)
        z = (1j + x)/(2*sqrtt)                    
        w = wofz(z) * _SQRTPI2 / sqrtt
        return w
    
    def voigth(a, u):
        """
        Voigt function H(a, u).
        """
        x = u/a
        t = 1/(4*a**2)
        voigtU = voigtuv(x, t).real
        h = voigtU/(a*_SQRTPI)
        return h
    

    You said that you know that value of H(a,u) is 1.410526851411200e−007 when a=0.01 and u=200. We can check:

    In [109]: voigth(0.01, 200)
    Out[109]: 1.41052685142231e-07
    

    The above doesn't answer the question of why your code doesn't work when u is large. To use quad successfully, it is always a good idea to have a good understanding of your integrand. In your case, when u is large, only a very small interval near x = u makes a significant contribution to the integral. quad doesn't detect this, so it misses a big part of the integral and returns a value that is too small.

    One way to fix this is to use the points argument of quad with a point that is very close to the end point of the interval. For example, I changed the call of quad to:

    integ = inter.quad(lambda x: np.exp(-(np.square(u)-np.square(x))) * np.sin(2*a*(u-x)),
                       0, u, points=[0.999*u])
    

    With that change, here's what your function returns for voigt(0.01, 200):

    In [191]: voigt(0.01, 200)
    Out[191]: 1.4105268514252487e-07
    

    I don't have a rigorous justification for the value 0.999*u; that is just a point close enough to the end of the interval to give a reasonable answer for u around 200 or so. Further investigation of the integrand could give you a better choice. (For example, can you find an analytical expression for the location of the maximum of the integrand? If so, that would be much better than 0.999*u.)

    You could also try tweaking the values of epsabs and epsrel, but in my few experiments, adding the points argument made the biggest impact.

    0 讨论(0)
提交回复
热议问题