Exemplo n.º 1
0
    def _fourier_analytic(self, cosmo, k, M, a, mass_def):
        M_use = np.atleast_1d(M)
        k_use = np.atleast_1d(k)

        # Comoving virial radius
        R_M = mass_def.get_radius(cosmo, M_use, a) / a
        c_M = self._get_cM(cosmo, M_use, a, mdef=mass_def)
        R_s = R_M / c_M

        x = k_use[None, :] * R_s[:, None]
        Si2, Ci2 = sici(x)
        P1 = M / (np.log(1 + c_M) - c_M / (1 + c_M))
        if self.truncated:
            Si1, Ci1 = sici((1 + c_M[:, None]) * x)
            P2 = np.sin(x) * (Si1 - Si2) + np.cos(x) * (Ci1 - Ci2)
            P3 = np.sin(c_M[:, None] * x) / ((1 + c_M[:, None]) * x)
            prof = P1[:, None] * (P2 - P3)
        else:
            P2 = np.sin(x) * (0.5 * np.pi - Si2) - np.cos(x) * Ci2
            prof = P1[:, None] * P2

        if np.ndim(k) == 0:
            prof = np.squeeze(prof, axis=-1)
        if np.ndim(M) == 0:
            prof = np.squeeze(prof, axis=0)
        return prof
Exemplo n.º 2
0
    def B_FT_integral(self, k):
        rst = 0.015 * self.r200
        rs = self.r_scale

        si_1, ci_1 = sici(k * rst)
        si_2, ci_2 = sici(k * (rst + self.r200))
        si_3, ci_3 = sici(k * rs)
        si_4, ci_4 = sici(k * (rs + self.r200))

        prefac = rs**3. * rst**2. / (rs - rst)**3.
        term1 = k * (-rs + rst) * np.cos(k * rst) * ci_1
        term2 = k * (rs - rst) * np.cos(k * rst) * ci_2
        term3 = -(rs - rst) * (2 * self.r200 + rs + rst) * np.sin(
            k * self.r200) / (self.r200 + rs) / (self.r200 + rst)
        term4 = ci_4 * (k * (rs - rst) * np.cos(k * rs) - 2 * np.sin(k * rs))
        term5 = ci_3 * (k * (rst - rs) * np.cos(k * rs) + 2 * np.sin(k * rs))
        term6 = -2. * ci_1 * np.sin(k * rst) + 2. * ci_2 * np.sin(k * rst)
        term7 = -k * (rs - rst) * np.sin(k * rs) * si_3
        term8 = k * (rs - rst) * np.sin(k * rs) * si_4
        term9 = 2. * np.cos(k * rs) * (-si_3 + si_4) + 2. * np.cos(
            k * rst) * si_1 - k * (rs - rst) * np.sin(k * rst) * si_1
        term10 = -2 * np.cos(k * rst) * si_2 + k * (rs - rst) * np.sin(
            k * rst) * si_2
        return prefac * (term1 + term2 + term3 + term4 + term5 + term6 +
                         term7 + term8 + term9 + term10)
Exemplo n.º 3
0
    def fourier_profiles(self, cosmo, k, M, a, squeeze=True, **kwargs):
        """
        Computes the Fourier transform of the Navarro-Frenk-White profile.
        """
        # Input handling
        M, a, k = np.atleast_1d(M), np.atleast_1d(a), np.atleast_2d(k)

        # extract parameters
        bg = kwargs.get("beta_gal", 1.)
        bmax = kwargs.get("beta_max", 1.)

        c = concentration_duffy(M, a, is_D500=True, squeeze=False)
        R = R_Delta(cosmo, M, a, self.Delta, is_matter=False,
                    squeeze=False) / (c * a)
        x = k * R[..., None]

        c = c[..., None] * bmax  # optimise
        Si1, Ci1 = sici((bg + c) * x)
        Si2, Ci2 = sici(bg * x)

        P1 = 1 / (np.log(1 + c / bg) - c / (1 + c / bg))
        P2 = np.sin(bg * x) * (Si1 - Si2) + np.cos(bg * x) * (Ci1 - Ci2)
        P3 = np.sin(c * x) / ((bg + c) * x)

        F = P1 * (P2 - P3)
        return (F.squeeze(), (F**2).squeeze()) if squeeze else (F, F**2)
Exemplo n.º 4
0
def utconvut(r, rt, conc):
    """1-halo contribution to be substracted from the 2-halo term (ximm).

            Args:
                r (float or array like): 3d distances from halo center in Mpc/h comoving.
                rt (float): Truncation radius. The boundary of the halo.
                conc (float): Concentration.

            Returns:
                float or arraylike: 1-halo contribution.

    """

    # Get ut(k)
    # Analytic expression
    k = np.logspace(-4, 4, num=1000, base=10)
    rc = rt / conc
    mu = k * rc
    Put = (np.cos(mu) * (sici(mu + mu * conc)[1] - sici(mu)[1])
           + np.sin(mu) * (sici(mu + mu * conc)[0] - sici(mu)[0])
           - np.sin(mu * conc) / (mu + mu * conc)) \
          / (np.log(1 + conc) - conc / (1 + conc))

    # Product
    PutPut = Put * Put

    # Transform to real space
    integral = fastcorr.calc_corr(r, k, PutPut, N=500, h=1e-2)

    return integral
Exemplo n.º 5
0
  def impedance(self, wvln, Z):
    """
    Impedance of a dipole antenna

    Reference::
      https://en.wikipedia.org/wiki/Dipole_antenna#General_impedance_formulas

    @param wvln : wavelength (m)
    @type  wvln : (numpy array of) float

    @param Z : impedance of the medium, 376.73 ohm for free space
    """
    k = 2*math.pi*wavenumber(wvln)
    x = k*self.length
    try:
      from scipy.special import sici
      Si,Ci = sici(x)
      SiTwo,CiTwo = sici(2*x)
      Sia, Cia = sici(2*k*self.radius*self.radius/self.length)
    except ImportError:
      Si,Ci = sin(x), cos(x)
      SiTwo,CiTwo = sin(2*x), cos(2*x)
      a = 2*k*self.radius*self.radius/self.length
      Sia, Cia = sin(a), cos(a)
    multiplier = Z/(2*math.pi*numpy.sin(x/2.)**2)

    R = multiplier * (euler + numpy.log(x) - Ci
                      +0.5*numpy.sin(x)*(SiTwo - 2*Si)
                      +0.5*numpy.cos(x)*(euler + numpy.log(x/2.) + CiTwo -2*Ci))
    X = (multiplier/2.) * (2*Si + numpy.cos(x)*(2*Si - SiTwo)
                         - numpy.sin(x)*(2*Ci - CiTwo - Cia))
    return R, X
Exemplo n.º 6
0
    def u_nfw(self, k, m, z):
        """
        Normalized Fourier Transform of an NFW profile.
        
        ..note:: This is Equation 81 from Cooray & Sheth (2002).
        
        Parameters
        ----------
        k : int, float
            Wavenumber
        m : 
        """
        c, r_s = self.cm_relation(m, z, get_rs=True)

        K = k * r_s

        asi, ac = sp.sici((1 + c) * K)
        bs, bc = sp.sici(K)

        # The extra factor of np.log(1 + c) - c / (1 + c)) comes in because
        # there's really a normalization factor of 4 pi rho_s r_s^3 / m,
        # and m = 4 pi rho_s r_s^3 * the log term
        norm = 1. / (np.log(1 + c) - c / (1 + c))

        return norm * (np.sin(K) * (asi - bs) - np.sin(c * K) / ((1 + c) * K) \
            + np.cos(K) * (ac - bc))
Exemplo n.º 7
0
def psi_SFS_simple3d(d, prefac, xi, d0):
    """Return simple clean limit pair wavefunction from Demler
    """
    a = (d - d0)/xi
    (si,ci) = sici(a)
    vc = abs(prefac*(-np.pi/2*(a) + a*sici(a)[0] + np.cos(a)))
    return vc
Exemplo n.º 8
0
    def NFW_k(self, k, m, normalize=True):
        ''' This parameterization is from Scoccimarro et al. 2000 
            "How Many Galaxies Fit in a Halo?" '''
        R = self.mass_2_virial_radius(m).to(
            u.Mpc).value * self.Delta**(1. / 3.)
        c = self.concentration(m)
        khat = k * self.R_star * self.Delta**(-1. / 3.)
        y = ((R / self.R_star) / self.h)

        kappa = np.array(
            [khat.value * y[i].value / c[i].value for i in range(len(c))])
        kappa_c = np.array([kappa[i] * c[i].value for i in range(len(c))])
        kappa_1plus_c = np.array(
            [kappa[i] * (1. + c[i].value) for i in range(len(c))])

        SI_1, CI_1 = sici(kappa_1plus_c)
        SI_2, CI_2 = sici(kappa)

        # no fs given, yet...
        ufunc_no_f = np.sin(kappa) * (SI_1 - SI_2) + np.cos(kappa) * (
            CI_1 - CI_2) - (np.sin(kappa_c) / kappa_1plus_c)

        fs = self.f(c)

        ufunc = np.array([fs[i] * ufunc_no_f[i] for i in range(len(c))])

        if normalize:
            return np.array(
                [ufunc[i] / np.max(ufunc[i]) for i in range(ufunc.shape[0])])
        else:
            return ufunc
Exemplo n.º 9
0
 def fk(k):
     x = k * r_s
     Si1, Ci1 = sici((1 + c) * x)
     Si2, Ci2 = sici(x)
     P1 = 1 / (np.log(1 + c) - c / (1 + c))
     P2 = np.sin(x) * (Si1 - Si2) + np.cos(x) * (Ci1 - Ci2)
     P3 = np.sin(c * x) / ((1 + c) * x)
     return M * P1 * (P2 - P3)
Exemplo n.º 10
0
        def antideriv(k, x):
            si1, ci1 = sici(k * (x + 1))
            si2, ci2 = sici(k * (x + 0.75))

            return (1. / k) * (
                12 * (np.cos(k) * si1 - np.sin(k) * ci1) + 4 * k *
                (np.cos(k) * ci1 + np.sin(k) * si1) - 4 * np.sin(k * x) /
                (x + 1) + -12 *
                (np.cos(0.75 * k) * si2 - np.sin(0.75 * k) * ci2))
Exemplo n.º 11
0
 def __init__(self, flip_d, Trf, side_lobes=1, B0=_defaultB0):
     flip_r = np.radians(flip_d)
     self.flip_d = flip_d
     self.flip_r = flip_r
     self.length = Trf
     self.N = 2 * (side_lobes + 1)  # +1 for central lobe, *2 for symmetric
     Npi = self.N * np.pi
     self.amp = flip_r / (B0 * _gamma2pi * Trf * special.sici(Npi)[0] / Npi)
     self.int_omega2 = Trf * \
         (B0 * _gamma2pi * self.amp)**2 * special.sici(2*Npi)[0] / Npi
Exemplo n.º 12
0
    def _p(self, K, c):

        sk, ck = sp.sici(K)
        skp, ckp = sp.sici(K + c * K)

        f1 = K * ck * np.sin(K) - K * np.cos(K) * sk - 1
        f2 = -((1 + c) * K * np.cos(c * K) + np.sin(c * K)) / (1 + c)**2
        f3 = K**2 * (ckp * np.sin(K) - np.cos(K) * skp)

        return (-K / 2 * f1 + 0.5 * (f2 + f3)) / K
Exemplo n.º 13
0
 def FT_density(self, k):
     prefactor = 4. * np.pi * self.rho_s * self.r_scale**3
     si_1, ci_1 = sici(k * self.r_scale)
     si_2, ci_2 = sici(k * (self.r_scale + self.r200))
     bb = k * self.r_scale
     Xm = self.r200 / self.r_scale
     term1 = np.cos(bb) * (-ci_1 + ci_2)
     term2 = np.sin(bb) * (-si_1 + si_2)
     term3 = np.sin(bb * Xm) / ((1. + Xm) * bb)
     return prefactor * (term1 + term2 + term3)
Exemplo n.º 14
0
    def _p(self, K, c):

        sk, ck = sp.sici(K)
        skp, ckp = sp.sici(K + c * K)

        f1 = K * ck * np.sin(K) - K * np.cos(K) * sk - 1
        f2 = -((1 + c) * K * np.cos(c * K) + np.sin(c * K)) / (1 + c) ** 2
        f3 = K ** 2 * (ckp * np.sin(K) - np.cos(K) * skp)

        return (-K / 2 * f1 + 0.5 * (f2 + f3)) / K
Exemplo n.º 15
0
    def u_nfw(lm,k) :
        x=k*rsMf(lm) #k*r_s
        c=cMf(lm)
        sic,cic=sici(x*(1+c))
        six,cix=sici(x)
        fsin=np.sin(x)*(sic-six)
        fcos=np.cos(x)*(cic-cix)
        fsic=np.sin(c*x)/(x*(1+c))
        fcon=np.log(1.+c)-c/(1+c)

        return (fsin+fcos-fsic)/fcon
Exemplo n.º 16
0
def frl1(x):
    gamma = pi / 3.
    a2 = (11. / 3. - np.log(2) - np.euler_gamma - np.log(x)) * np.cos(
        2. * x) + (9. + np.cos(2. * x)) * sici(2. * x)[1]
    a1 = 15. - 9. * np.log(2.) - 9. * np.euler_gamma - 9. * np.log(x) + (
        2. * x + np.sin(2. * x)) * sici(2. * x)[0]
    b1 = 8. * (np.sin(2. * x) -
               (1 + np.cos(x)**2.) * x - np.sin(2. * x) * x * x) / x**3.
    c1 = nquad(frl, [[0., pi], [0., 2. * pi]], args=(x, gamma))
    z = (a2 + a1 + b1 - c1[0]) / 8. / x / x
    return z
Exemplo n.º 17
0
def y_halo_parameter2(k, M, halo_stuff, g):
    eta = k * halo_stuff.r_characteristic_array[g]
    c_eta = k * r_halo_virial(M)
    SiCi_term = np.subtract(sici((eta + c_eta)), sici(eta))
    Si_term = SiCi_term[0]
    Ci_term = SiCi_term[1]
    factor = 1 / (np.log(1 + halo_stuff.c_concentration_array[g]) -
                  halo_stuff.c_concentration_array[g] /
                  (1 + halo_stuff.c_concentration_array[g]))
    return (factor *
            (np.sin(eta) * Si_term + np.cos(eta) * Ci_term - np.sin(c_eta) /
             (eta + c_eta)))
Exemplo n.º 18
0
def W(k, M, z, model='diemer19', A=None, B=None, C=None, log10Mpv=None):
    '''
    W(k, M) is the normalized Fourier transform of the halo density profile. Eq. (9)

    k : float
        Fourier Mode in (Mpc/h)^-1
    M : float
        Halo mass in Msun/h
    z : float
        Redshift
    model : Colossus concentration model
        str (default diemer19)
    A : float (Only required if model == 'generic')
        c-M relation amplitude parameter
    B : float (Only required if model == 'generic')
        c-M relation power-law parameter
    C: float (Only required if model == 'generic')
        C-M relation minimum value
    log10Mpv : float (Only required if model == 'generic')
        c-M relation log10-mass pivot value
    '''
    if model == 'generic':

        try:

            conc = (A * (M / (10**log10Mpv))**B) + C

        except TypeError:

            raise RuntimeError(
                "A, B, C, Mpv have to be explictly set if model is generic!")

    else:

        conc = concentration.concentration(M, '200c', z, model=model)

    # Getting the comoving parameters assuming 200c
    R200c = (3 * M / (4 * np.pi * 200 * rhoc))**(1.0 / 3)
    rs = R200c / conc * (1 + z)
    rhos = conc**3 / 3 * 200 * rhoc / (np.log(conc + 1) - conc /
                                       (conc + 1)) / (1 + z)**3

    # Analytical Integration
    aux1 = k[:, np.newaxis] * (1.0 + conc) * rs
    aux2 = k[:, np.newaxis] * rs
    aux3 = k[:, np.newaxis] * conc * rs
    si1, ci1 = sici(aux1)
    si2, ci2 = sici(aux2)

    return 4.0 * np.pi * rhos * rs**3 *\
           ( np.sin( aux2 ) * (si1-si2) - np.sinc(aux3/np.pi) * (conc/(1.0+conc)) +\
             np.cos( aux2 ) * (ci1-ci2) )
Exemplo n.º 19
0
def single_slit_model_with_integration(x, A, z, d, wl, s, offset):
    return A * (2 * z *
                (-(z - z * np.cos(
                    (d * (2 * np.pi / wl) * (s - (x - offset))) / z) + d *
                   (2 * np.pi / wl) * (s - (x - offset)) * sici(
                       (d * (2 * np.pi / wl) * (-s + (x - offset))) / z)[0]) /
                 (2. * (s - (x - offset)) * z) +
                 ((-1 + np.cos(
                     (d * (2 * np.pi / wl) * (s + (x - offset))) / z)) /
                  (s + (x - offset)) + (d * (2 * np.pi / wl) * sici(
                      (d * (2 * np.pi / wl) *
                       (s + (x - offset))) / z)[0]) / z) / 2.)) / (
                           (2 * np.pi / wl) * np.pi)
Exemplo n.º 20
0
 def compute_integral(self,
                      sinc_loc,
                      sinc_amp,
                      Omega,
                      start_time,
                      end_time,
                      b=0):
     integral = b * (end_time - start_time)
     for l in range(len(sinc_loc)):
         integral += (sinc_amp[l] *
                      (sici(Omega * (end_time - sinc_loc[l]))[0] -
                       sici(Omega * (start_time - sinc_loc[l]))[0]) / np.pi)
     return integral
Exemplo n.º 21
0
def dipoleImpedance( frequency, length, diameter ):
  radius = diameter/2
  k = 2*pi*frequency/299792458

  resistance_m = (eta/(2*pi))*(euler + log(k*length) - sici(k*length)[1] + 0.5*sin(k*length)*(sici(2*k*length)[0] - 2*sici(k*length)[0]) + 0.5*cos(k*length)*(euler + log(k*length/2) + sici(2*k*length)[1] - 2*sici(k*length)[1]))

  resistance = resistance_m / (sin(k*length/2)*sin(k*length/2))

  reactance_m = (eta/(4*pi))*(2*sici(k*length)[0] + cos(k*length)*(2*sici(k*length)[0] - sici(2*k*length)[0]) - sin(k*length)*(2*sici(k*length)[1] - sici(2*k*length)[1] - sici(2*k*radius*radius/length)[1]))

  reactance = reactance_m / (sin(k*length/2)*sin(k*length/2))

  impedance = complex(resistance, reactance)
  return impedance
Exemplo n.º 22
0
    def _normalized_halo_profile(self, k_h, r_virial, c):
        """Compute the normalized halo profile function in Fourier space; :math:`u(k|m)`

        For details of the available profile parametrizations, see the class description.

        Note that the function returns unity for :math:`k \\leq 0`.

        Args:
            k_h (np.ndarray): Wavenumber in h/Mpc units.
            r_virial (np.ndarray): Virial radius in Mpc/h units.
            c (np.ndarray): Halo concentration parameter; :math:`c = r_\mathrm{virial}/r_\mathrm{scale}`.

        Returns:
            np.ndarray: Normalized halo profile :math:`u(k|m)`
        """

        if self.profile_name == 'NFW':

            r_scale = r_virial / c  # in Mpc/h units

            # Check if we have an array or a float input and compute accordingly
            if type(k_h) == np.ndarray:
                # filter out sections with k_h<=0
                filt = np.where(k_h > 0)

                # compute the matrix of r_scale * k_h
                ks0 = np.matmul(k_h.reshape(-1, 1), r_scale.reshape(1, -1))
                ks = ks0[filt, :]
            else:
                if k_h <= 0.:
                    return 1.
                ks = k_h * r_scale

            sici1 = sici(ks)
            sici2 = sici(ks * (1. + c))
            f1 = np.sin(ks) * (sici2[0] - sici1[0])
            f2 = np.cos(ks) * (sici2[1] - sici1[1])
            f3 = np.sin(c * ks) / (ks * (1. + c))
            fc = np.log(1. + c) - c / (1. + c)

            if type(k_h) == np.ndarray:
                output = np.ones_like(ks0)
                output[filt, :] = ((f1 + f2 - f3) / fc)
                return output
            else:
                return (f1 + f2 - f3) / fc
        else:
            raise NameError('Halo profile %s is not implemented yet' %
                            (self.profile_name))
Exemplo n.º 23
0
 def func(f, amp, time, a, b, c):
     return -amp * (
         ((f - a) / (b - a)) *
         (special.sici(time * 2 * math.pi *
                       (f - a))[0] - special.sici(time * 2 * math.pi *
                                                  (f - b))[0]) +
         (np.cos(time * 2 * math.pi * (f - a)) - np.cos(time * 2 * math.pi *
                                                        (f - b))) /
         (time * 2 * math.pi * (b - a)) + ((c - f) / (b - c)) *
         (special.sici(time * 2 * math.pi *
                       (f - c))[0] - special.sici(time * 2 * math.pi *
                                                  (f - b))[0]) -
         (np.cos(time * 2 * math.pi * (f - c)) - np.cos(time * 2 * math.pi *
                                                        (f - b))) /
         (time * 2 * math.pi * (b - c)))
Exemplo n.º 24
0
def xysincs(uvecs: [np.ndarray], a: float, xi: float = 0.0):
    """
    Solution for the xysincs model.

    Defined as:
        I(x,y) = sinc(ax) sinc(ay) cos(1 - x^2 - y^2), for |x| and |y| < 1/sqrt(2)
               = 0, otherwise

        where (x,y) is rotated from (l,m) by an angle xi.

    In UV space, this resembles a rotated square.

    Parameters
    ----------
    uvecs: ndarray of float
        Cartesian baseline vectors in wavelengths, shape (Nbls, 3)
    a: float
        Sinc parameter, giving width of the "box" in uv space.
        box width in UV is a/(2 pi)
    xi: float
        Rotation of (x,y) coordinates from (l,m) in radians.

    Returns
    -------
    ndarray of complex
        Visibilities, shape (Nbls,)
    """
    assert uvecs.ndim == 2 and uvecs.shape[1] == 3
    assert np.allclose(uvecs[:, 2], 0)
    cx, sx = np.cos(xi), np.sin(xi)
    rot = np.array([[cx, sx, 0], [-sx, cx, 0], [0, 0, 1]])

    xyvecs = np.dot(uvecs, rot)

    x = 2 * np.pi * xyvecs[:, 0]
    y = 2 * np.pi * xyvecs[:, 1]

    x = x.astype(complex)
    y = y.astype(complex)

    b = 1 / np.sqrt(2.0)

    xfac = (np.sign(a - x) - np.sign(a - x)) * np.pi / 2
    yfac = (np.sign(a - y) - np.sign(a - y)) * np.pi / 2
    xpart = (sici(b * (a - x))[0] + sici(b * (a + x))[0] + xfac) / a
    ypart = (sici(b * (a - y))[0] + sici(b * (a + y))[0] + yfac) / a

    return ypart * xpart
Exemplo n.º 25
0
def gen(x, name):
    """Generate fixture data and writes them to file.

    # Arguments

    * `x`: domain
    * `name::str`: output filename

    # Examples

    ``` python
    python> x = linspace(0.0, 1.0, 2001)
    python> gen(x, './data.json')
    ```
    """
    y = sici(x)

    # Store data to be written to file as a dictionary:
    data = {"x": x.tolist(), "si": y[0].tolist(), "ci": y[1].tolist()}

    # Based on the script directory, create an output filepath:
    filepath = os.path.join(DIR, name)

    # Write the data to the output filepath as JSON:
    with open(filepath, "w") as outfile:
        json.dump(data, outfile)
Exemplo n.º 26
0
 def test_sici_float32(self):
     x = np.array([[1, 2], [3, 4]], np.float32)
     x_gpu = gpuarray.to_gpu(x)
     (si_gpu, ci_gpu) = special.sici(x_gpu)
     (si, ci) = scipy.special.sici(x)
     np.testing.assert_allclose(si, si_gpu.get(), atol=1e-7)
     np.testing.assert_allclose(ci, ci_gpu.get(), atol=1e-7)
Exemplo n.º 27
0
    def __init__(self, pm):
        """Initializes variables

        parameters
        ----------
        pm: object
          input parameters
        """
        self.x_npt = pm.sys.grid
        self.x_delta = pm.sys.deltax

        # eqn (82)
        self.A = pm.lda.mix
        #kerker_length: float
        #  screening distance for Kerker preconditioning [a0]
        #  Default corresponds to :math:`2\pi/\lambda = 1.5\AA`

        self.q0 = 2 * np.pi / pm.lda.kerker_length
        dq = 2 * np.pi / (2 * pm.sys.xmax)
        q0_scaled = self.q0 / dq
        self.G_q = np.zeros((self.x_npt // 2 + 1), dtype=np.float)
        for q in range(len(self.G_q)):
            self.G_q[q] = self.A * q**2 / (q**2 + q0_scaled**2)
        #self.G_r = np.set_diagonal(diagonal

        # one-dimensional Kerker mixing
        a = pm.sys.acon
        q = dq * np.array(list(range(self.x_npt // 2 + 1)))
        aq = np.abs(a * q)
        Si, Ci = scsp.sici(aq)
        # verified that this agrees with Mathematica...
        v_k = -2 * (np.cos(aq) * Ci + np.sin(aq) * (Si - np.pi / 2))
        self.G_q_1d = self.A * 1 / (1 + v_k * self.q0)
Exemplo n.º 28
0
 def test_sici_float64(self):
     x = np.array([[1, 2], [3, 4]], np.float64)
     x_gpu = gpuarray.to_gpu(x)
     (si_gpu, ci_gpu) = special.sici(x_gpu)
     (si, ci) = scipy.special.sici(x)
     assert np.allclose(si, si_gpu.get())
     assert np.allclose(ci, ci_gpu.get())
Exemplo n.º 29
0
def step_t_finite_overshoot(w1, w2, t0, width, t):
    """
    Step function that goes from w1 to w2 at time t0
    as a function of t, with finite rise time and
    and overshoot defined by the parameter width.
    """
    return w1 + (w2 - w1) * (0.5 + sici((t - t0) / width)[0] / (np.pi))
Exemplo n.º 30
0
 def test_sici_float32(self):
     x = np.array([[1, 2], [3, 4]], np.float32)
     x_gpu = gpuarray.to_gpu(x)
     (si_gpu, ci_gpu) = special.sici(x_gpu)
     (si, ci) = scipy.special.sici(x)
     np.testing.assert_allclose(si, si_gpu.get(), atol=1e-7)
     np.testing.assert_allclose(ci, ci_gpu.get(), atol=1e-7)
Exemplo n.º 31
0
def func2():
    """Test fucntion sin(x)/x
    Returns: -f    (function) The fucntion to be integrated
             -name (string)   Function form in string
             -inte (fucntion) Analytic solution of the integral"""
    f = lambda x: np.sin(x)/x
    inte = lambda x: sici(x)[0]
    return f , "sin(x)/x", inte
Exemplo n.º 32
0
def vc_SFS_B82_nearTc(d, prefac, xi_f, doff):
    """clean limit vc-d by Buzdin (1982)
    Transparent interface.
    """
    a = (d - doff)/xi_f
    (si,ci) = sici(a)
    vc = prefac * abs( -1/2*(a**2*ci - a*np.sin(a) + np.cos(a)) )
    return vc
Exemplo n.º 33
0
    def exact(x: float) -> float:
        def f1(r: float) -> Any:
            return (np.sin(r) / r) ** 2

        # re-arranging the formula for sici from
        # https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.sici.html
        # to match Mehta (2004) p595, A.38, we get:
        int_2 = y + np.log(2 * p * x) - sici(2 * p * x)[1]
        # sici(x) returns (Sine integral from 0 to x, gamma + log(x) + Cosine integral from 0 to x)
        int_3 = (sici(np.inf)[0] - sici(p * x)[0]) ** 2
        t1 = 4 * x / p
        t2 = 2 / p ** 2
        t3 = t2 / 2
        res = (
            t1 * quad(f1, p * x, np.inf, limit=100)[0] + t2 * int_2 - 0.25 + t3 * int_3
        )
        return float(res)
Exemplo n.º 34
0
def fit_si(y, x0):
    '''Fits equidistant (=1) 1D data with integral sine.'''
    ### fitting function
    integralsine = lambda p, x: p[0] + p[1] * sici((x - p[2]) / p[3])[0]
    ### choose initial params
    pinit = (y[x0], y.ptp() / 3.7, x0, (y.argmax() - y.argmin()) / (2 * pi))
    x = linspace(0, y.size - 1, y.size)
    si_fit = fitcurve(integralsine, x, y, pinit)
    return si_fit.fit()
Exemplo n.º 35
0
def SI(x):
  nx = np.size(x)
  sint = np.zeros(nx)
  for i in range(0,nx):
    if math.fabs(x[i]) < 0.01:
      sint[i] = x[i] - x[i]**2/18.0 + x[i]**5/600.0 - x[i]**7/35280.0
    else:
      sint[i], junk = special.sici(x[i])
  return sint
Exemplo n.º 36
0
    def y(self, ln_k, mass):
        """Fourier transform of the halo profile.

        Using an analytic expression for the Fourier transform from
        White (2001).  This is only valid for an NFW profile.
        """

        k = numpy.exp(ln_k)/self._h
        con = self.concentration(mass)
        con_plus = 1.0 + con
        z = k*self.virial_radius(mass)/con
        si_z, ci_z = special.sici(z)
        si_cz, ci_cz = special.sici(con_plus*z)
        rho_km = (numpy.cos(z)*(ci_cz - ci_z) +
                  numpy.sin(z)*(si_cz - si_z) -
                  numpy.sin(con*z)/(con_plus*z))
        mass_k = numpy.log(con_plus) - con/con_plus

        return rho_km/mass_k
Exemplo n.º 37
0
def quad_mat(N, h):
	"""
	Compute the integration matrix.
	"""
	
	k = linspace(-(N - 1) / 2.0, (N - 1) / 2.0, N)	

	sigma = sici(pi * (k - k[0]))[0]
	col = h / 2.0 + (h / pi) * sigma
	row = h / 2.0 - (h / pi) * sigma
	
	return toeplitz(col, row)
Exemplo n.º 38
0
def test_sici_consistency():
    # Make sure the implementation of sici for real arguments agrees
    # with the implementation of sici for complex arguments.

    # On the negative real axis Cephes drops the imaginary part in ci
    def sici(x):
        si, ci = sc.sici(x + 0j)
        return si.real, ci.real
    
    x = np.r_[-np.logspace(8, -30, 200), 0, np.logspace(-30, 8, 200)]
    si, ci = sc.sici(x)
    dataset = np.column_stack((x, si, ci))
    FuncData(sici, dataset, 0, (1, 2), rtol=1e-12).check()
Exemplo n.º 39
0
def Cgw_sec(model, alpha=-2.0/3.0, fL=1.0/500, approx_ksum=False, inc_cor=True):
    """ Compute the residual covariance matrix for an hc = 1 x (f year)^alpha GW background.
        Result is in units of (100 ns)^2.
        Modified from Michele Vallisneri's mc3pta (https://github.com/vallis/mc3pta)

        @param: list of libstempo pulsar objects
                (as returned by readRealisations)
        @param: the H&D correlation matrix
        @param: the TOAs
        @param: the GWB spectral index
        @param: the low-frequency cut-off
        @param: approx_ksum
    """
    psrobs = model[6]
    alphaab = model[5]
    times_f = model[0]
    
    day    = 86400.0              # seconds, sidereal (?)
    year   = 3.15581498e7         # seconds, sidereal (?)

    EulerGamma = 0.5772156649015329

    npsrs = alphaab.shape[0]

    t1, t2 = np.meshgrid(times_f,times_f)

    # t1, t2 are in units of days; fL in units of 1/year (sidereal for both?)
    # so typical values here are 10^-6 to 10^-3
    x = 2 * np.pi * (day/year) * fL * np.abs(t1 - t2)

    del t1
    del t2

    # note that the gamma is singular for all half-integer alpha < 1.5
    #
    # for -1 < alpha < 0, the x exponent ranges from 4 to 2 (it's 3.33 for alpha = -2/3)
    # so for the lower alpha values it will be comparable in value to the x**2 term of ksum
    #
    # possible alpha limits for a search could be [-0.95,-0.55] in which case the sign of `power`
    # is always positive, and the x exponent ranges from ~ 3 to 4... no problem with cancellation

    # The tolerance for which to use the Gamma function expansion
    tol = 1e-5

    # the exact solutions for alpha = 0, -1 should be acceptable in a small interval around them...
    if abs(alpha) < 1e-7:
        cosx, sinx = np.cos(x), np.sin(x)

        power = cosx - x * sinx
        sinint, cosint = sl.sici(x)

        corr = (year**2 * fL**-2) / (24 * math.pi**2) * (power + x**2 * cosint)
    elif abs(alpha + 1) < 1e-7:
        cosx, sinx = np.cos(x), np.sin(x)

        power = 6 * cosx - 2 * x * sinx - x**2 * cosx + x**3 * sinx
        sinint, cosint = ss.sici(x)

        corr = (year**2 * fL**-4) / (288 * np.pi**2) * (power - x**4 * cosint)
    else:
        # leading-order expansion of Gamma[-2+2*alpha]*Cos[Pi*alpha] around -0.5 and 0.5
        if   abs(alpha - 0.5) < tol:
            cf =  np.pi/2   + (np.pi - np.pi*EulerGamma)              * (alpha - 0.5)
        elif abs(alpha + 0.5) < tol:
            cf = -np.pi/12  + (-11*np.pi/36 + EulerGamma*math.pi/6)     * (alpha + 0.5)
        elif abs(alpha + 1.5) < tol:
            cf =  np.pi/240 + (137*np.pi/7200 - EulerGamma*np.pi/120) * (alpha + 1.5)
        else:
            cf = ss.gamma(-2+2*alpha) * np.cos(np.pi*alpha)

        power = cf * x**(2-2*alpha)

        # Mathematica solves Sum[(-1)^n x^(2 n)/((2 n)! (2 n + 2 alpha - 2)), {n, 0, Infinity}]
        # as HypergeometricPFQ[{-1+alpha}, {1/2,alpha}, -(x^2/4)]/(2 alpha - 2)
        # the corresponding scipy.special function is hyp1f2 (which returns value and error)
        # TO DO, for speed: could replace with the first few terms of the sum!
        if approx_ksum:
            ksum = 1.0 / (2*alpha - 2) - x**2 / (4*alpha) + x**4 / (24 * (2 + 2*alpha))
        else:
            ksum = ss.hyp1f2(alpha-1,0.5,alpha,-0.25*x**2)[0]/(2*alpha-2)

        del x

        # this form follows from Eq. (A31) of Lee, Jenet, and Price ApJ 684:1304 (2008)
        corr = -(year**2 * fL**(-2+2*alpha)) / (12 * np.pi**2) * (power + ksum)

    if inc_cor:
        # multiply by alphaab; there must be a more numpythonic way to do it
        # npsrs psrobs
        inda, indb = 0, 0
        for a in range(npsrs):
            for b in range(npsrs):
                corr[inda:inda+psrobs[a], indb:indb+psrobs[b]] *= alphaab[a, b]
                indb += psrobs[b]
            indb = 0
            inda += psrobs[a]
        
    return corr
Exemplo n.º 40
0
 def _p(self, K):
     bs, bc = sp.sici(K)
     return 0.5 * ((np.pi - 2 * bs) * np.sin(K) - 2 * np.cos(K) * bc)
Exemplo n.º 41
0
def pl_cov(t, Si=4.33, fL=1.0/20, approx_ksum=False):
    """
    Analytically calculate the covariance matrix for a stochastic signal with a
    power spectral density given by pl_psd.

    @param t:   Time-series timestamps
    @param Si:  Spectral index of power-law spectrum
    @param fL:  Low-frequency cut-off
    @param approx_ksum:     Whether we approximate the infinite sum

    @return:    Covariance matrix
    """

    EulerGamma = 0.5772156649015329
    alpha = 0.5*(3.0-Si)

    # Make a mesh-grid for the covariance matrix elements
    t1, t2 = np.meshgrid(t,t)
    x = 2 * np.pi * fL * np.abs(t1 - t2)
    del t1
    del t2

    # The tolerance for which to use the Gamma function expansion
    tol = 1e-5

    # the exact solutions for alpha = 0, -1 should be acceptable in a small
    # interval around them...
    if abs(alpha) < 1e-7:
        cosx, sinx = np.cos(x), np.sin(x)

        power = cosx - x * sinx
        sinint, cosint = sl.sici(x)

        corr = (fL**-2) / (24 * math.pi**2) * (power + x**2 * cosint)
    elif abs(alpha + 1) < 1e-7:
        cosx, sinx = np.cos(x), np.sin(x)

        power = 6 * cosx - 2 * x * sinx - x**2 * cosx + x**3 * sinx
        sinint, cosint = ss.sici(x)

        corr = (fL**-4) / (288 * np.pi**2) * (power - x**4 * cosint)
    else:
        # leading-order expansion of Gamma[-2+2*alpha]*Cos[Pi*alpha] around -0.5
        # and 0.5
        if   abs(alpha - 0.5) < tol:
            cf =  np.pi/2   + (np.pi - np.pi*EulerGamma) * (alpha - 0.5)
        elif abs(alpha + 0.5) < tol:
            cf = -np.pi/12  + (-11*np.pi/36 + EulerGamma*math.pi/6) * (alpha + 0.5)
        elif abs(alpha + 1.5) < tol:
            cf =  np.pi/240 + (137*np.pi/7200 - EulerGamma*np.pi/120) * (alpha + 1.5)
        else:
            cf = ss.gamma(-2+2*alpha) * np.cos(np.pi*alpha)

        power = cf * x**(2-2*alpha)

        # Mathematica solves Sum[(-1)^n x^(2 n)/((2 n)! (2 n + 2 alpha - 2)),
        # {n, 0, Infinity}] as HypergeometricPFQ[{-1+alpha}, {1/2,alpha},
        # -(x^2/4)]/(2 alpha - 2) the corresponding scipy.special function is
        # hyp1f2 (which returns value and error)
        if approx_ksum:
            ksum = 1.0 / (2*alpha - 2) - x**2 / (4*alpha) + x**4 / (24 * (2 + 2*alpha))
        else:
            ksum = ss.hyp1f2(alpha-1,0.5,alpha,-0.25*x**2)[0]/(2*alpha-2)

        del x

        corr = -(fL**(-2+2*alpha)) * (power + ksum)
        
    return corr
Exemplo n.º 42
0
def sine_integral_func(x):
    tmp = special.sici(x)
    tmp2 = -np.pi/2.0 + tmp[0]
    return tmp2
Exemplo n.º 43
0
 def ci(x):
     return sc.sici(x)[1]
Exemplo n.º 44
0
 def si(x):
     return sc.sici(x)[0]
Exemplo n.º 45
0
    def _p(self, K):
        si, ci = sp.sici(K)

        return 0.25 * (2 - K * (2 * ci * np.sin(K) + np.cos(K) * (np.pi - 2 * si)))
Exemplo n.º 46
0
 def sici(x):
     si, ci = sc.sici(x + 0j)
     return si.real, ci.real
Exemplo n.º 47
0
    def calculate_rkernel(self):

        gd = self.gd
        ng_c = gd.N_c
        cell_cv = gd.cell_cv
        icell_cv = 2 * np.pi * np.linalg.inv(cell_cv)
        vol = np.linalg.det(cell_cv)

        ns = self.calc.wfs.nspins
        n_g = self.n_g   # density on rough grid

        fx_g = ns * self.get_fxc_g(n_g)   # local exchange kernel
        qc_g = (-4 * np.pi * ns / fx_g)**0.5   # cutoff functional
        flocal_g = qc_g**3 * fx_g / (6 * np.pi**2)   # ren. x-kernel for r=r'
        Vlocal_g = 2 * qc_g / np.pi   # ren. Hartree kernel for r=r'

        ng = np.prod(ng_c)   # number of grid points
        r_vg = gd.get_grid_point_coordinates()
        rx_g = r_vg[0].flatten()
        ry_g = r_vg[1].flatten()
        rz_g = r_vg[2].flatten()

        prnt('    %d grid points and %d plane waves at the Gamma point' %
             (ng, self.pd.ngmax), file=self.fd)

        # Unit cells
        R_Rv = []
        weight_R = []
        nR_v = self.unit_cells
        nR = np.prod(nR_v)
        for i in range(-nR_v[0] + 1, nR_v[0]):
            for j in range(-nR_v[1] + 1, nR_v[1]):
                for h in range(-nR_v[2] + 1, nR_v[2]):
                    R_Rv.append(i * cell_cv[0] +
                                j * cell_cv[1] +
                                h * cell_cv[2])
                    weight_R.append((nR_v[0] - abs(i)) *
                                    (nR_v[1] - abs(j)) *
                                    (nR_v[2] - abs(h)) / float(nR))
        if nR > 1:
            # with more than one unit cell only the exchange kernel is
            # calculated on the grid. The bare Coulomb kernel is added
            # in PW basis and Vlocal_g only the exchange part
            dv = self.calc.density.gd.dv
            gc = (3 * dv / 4 / np.pi)**(1 / 3.)
            Vlocal_g -= 2 * np.pi * gc**2 / dv
            prnt('    Lattice point sampling: ' +
                 '(%s x %s x %s)^2 ' % (nR_v[0], nR_v[1], nR_v[2]) +
                 ' Reduced to %s lattice points' % len(R_Rv), file=self.fd)

        l_g_size = -(-ng // mpi.world.size)
        l_g_range = range(mpi.world.rank * l_g_size,
                          min((mpi.world.rank+1) * l_g_size, ng))

        fhxc_qsGr = {}
        for iq in range(len(self.ibzq_qc)):
            fhxc_qsGr[iq] = np.zeros((ns, len(self.pd.G2_qG[iq]),
                                      len(l_g_range)), dtype=complex)

        inv_error = np.seterr()
        np.seterr(invalid='ignore')
        np.seterr(divide='ignore')

        t0 = time()
        # Loop over Lattice points
        for i, R_v in enumerate(R_Rv):
            # Loop over r'. f_rr and V_rr are functions of r (dim. as r_vg[0])
            if i == 1:
                prnt('      Finished 1 cell in %s seconds' % int(time() - t0) +
                     ' - estimated %s seconds left' %
                     int((len(R_Rv) - 1) * (time() - t0)), 
                     file=self.fd)
                self.fd.flush()
            if len(R_Rv) > 5:
                if (i+1) % (len(R_Rv) / 5 + 1) == 0:
                    prnt('      Finished %s cells in %s seconds'
                         % (i, int(time() - t0))
                         + ' - estimated %s seconds left'
                         % int((len(R_Rv) - i) * (time() - t0) / i), 
                         file=self.fd)
                    self.fd.flush()
            for g in l_g_range:
                rx = rx_g[g] + R_v[0]
                ry = ry_g[g] + R_v[1]
                rz = rz_g[g] + R_v[2]

                # |r-r'-R_i|
                rr = ((r_vg[0] - rx)**2 +
                      (r_vg[1] - ry)**2 +
                      (r_vg[2] - rz)**2)**0.5

                n_av = (n_g + n_g.flatten()[g]) / 2.
                fx_g = ns * self.get_fxc_g(n_av, index=g)
                qc_g = (-4 * np.pi * ns / fx_g)**0.5
                x = qc_g * rr
                osc_x = np.sin(x) - x*np.cos(x)
                f_rr = fx_g * osc_x / (2 * np.pi**2 * rr**3)
                if nR > 1:   # include only exchange part of the kernel here
                    V_rr = (sici(x)[0] * 2 / np.pi - 1) / rr
                else:        # include the full kernel (also hartree part)
                    V_rr = (sici(x)[0] * 2 / np.pi) / rr

                # Terms with r = r'
                if (np.abs(R_v) < 0.001).all():
                    tmp_flat = f_rr.flatten()
                    tmp_flat[g] = flocal_g.flatten()[g]
                    f_rr = tmp_flat.reshape(ng_c)
                    tmp_flat = V_rr.flatten()
                    tmp_flat[g] = Vlocal_g.flatten()[g]
                    V_rr = tmp_flat.reshape(ng_c)
                    del tmp_flat

                f_rr[np.where(n_av < self.density_cut)] = 0.0
                V_rr[np.where(n_av < self.density_cut)] = 0.0

                f_rr *= weight_R[i]
                V_rr *= weight_R[i]

                # r-r'-R_i
                r_r = np.array([r_vg[0] - rx, r_vg[1] - ry, r_vg[2] - rz])

                # Fourier transform of r
                for iq, q in enumerate(self.ibzq_qc):
                    q_v = np.dot(q, icell_cv)
                    e_q = np.exp(-1j * gemmdot(q_v, r_r, beta=0.0))
                    f_q = self.pd.fft((f_rr + V_rr) * e_q, iq) * vol / ng
                    fhxc_qsGr[iq][0, :, g - l_g_range[0]] += f_q
                    if ns == 2:
                        f_q = self.pd.fft(V_rr * e_q, iq) * vol / ng
                        fhxc_qsGr[iq][1, :, g - l_g_range[0]] += f_q

        mpi.world.barrier()

        np.seterr(**inv_error)

        for iq, q in enumerate(self.ibzq_qc):
            npw = len(self.pd.G2_qG[iq])
            fhxc_sGsG = np.zeros((ns * npw, ns * npw), complex)
            l_pw_size = -(-npw // mpi.world.size)  # parallelize over PW below
            l_pw_range = range(mpi.world.rank * l_pw_size,
                               min((mpi.world.rank + 1) * l_pw_size, npw))

            if mpi.world.size > 1:
                # redistribute grid and plane waves in fhxc_qsGr[iq]
                bg1 = BlacsGrid(mpi.world, 1, mpi.world.size)
                bg2 = BlacsGrid(mpi.world, mpi.world.size, 1)
                bd1 = bg1.new_descriptor(npw, ng, npw, - (-ng / mpi.world.size))
                bd2 = bg2.new_descriptor(npw, ng, -(-npw / mpi.world.size), ng)

                fhxc_Glr = np.zeros((len(l_pw_range), ng), dtype=complex)
                if ns == 2:
                    Koff_Glr = np.zeros((len(l_pw_range), ng), dtype=complex)

                r = Redistributor(bg1.comm, bd1, bd2)
                r.redistribute(fhxc_qsGr[iq][0], fhxc_Glr, npw, ng)
                if ns == 2:
                    r.redistribute(fhxc_qsGr[iq][1], Koff_Glr, npw, ng)
            else:
                fhxc_Glr = fhxc_qsGr[iq][0]
                if ns == 2:
                    Koff_Glr = fhxc_qsGr[iq][1]

            # Fourier transform of r'
            for iG in range(len(l_pw_range)):
                f_g = fhxc_Glr[iG].reshape(ng_c)
                f_G = self.pd.fft(f_g.conj(), iq) * vol / ng
                fhxc_sGsG[l_pw_range[0] + iG, :npw] = f_G.conj()
                if ns == 2:
                    v_g = Koff_Glr[iG].reshape(ng_c)
                    v_G = self.pd.fft(v_g.conj(), iq) * vol / ng
                    fhxc_sGsG[npw + l_pw_range[0] + iG, :npw] = v_G.conj()

            if ns == 2:  # f_00 = f_11 and f_01 = f_10
                fhxc_sGsG[:npw, npw:] = fhxc_sGsG[npw:, :npw]
                fhxc_sGsG[npw:, npw:] = fhxc_sGsG[:npw, :npw]

            mpi.world.sum(fhxc_sGsG)
            fhxc_sGsG /= vol

            if mpi.rank == 0:
                w = Writer('fhxc_%s_%s_%s_%s.gpw' %
                           (self.tag, self.xc, self.ecut, iq))
                w.dimension('sG', ns * npw)
                w.add('fhxc_sGsG', ('sG', 'sG'), dtype=complex)
                if nR > 1:  # add Hartree kernel evaluated in PW basis
                    Gq2_G = self.pd.G2_qG[iq]
                    if (q == 0).all():
                        Gq2_G[0] = 1.
                    vq_G = 4 * np.pi / Gq2_G
                    fhxc_sGsG += np.tile(np.eye(npw) * vq_G, (ns, ns))
                w.fill(fhxc_sGsG)
                w.close()
            mpi.world.barrier()
        prnt(file=self.fd)
Exemplo n.º 48
0
 def _p(self, K, c=None):
     bs, bc = sp.sici(K)
     asi, ac = sp.sici((1 + c) * K)
     return (np.sin(K) * (asi - bs) - np.sin(c * K) / ((1 + c) * K) + np.cos(K) * (ac - bc))
Exemplo n.º 49
0
def Cred_sec(toas, alpha=-2.0/3.0, fL=1.0/20, approx_ksum=False):
    day    = 86400.0
    year   = 3.15581498e7
    EulerGamma = 0.5772156649015329

    psrobs = [len(toas)]
    alphaab = np.array([[1.0]])
    times_f = toas / day
    
    npsrs = alphaab.shape[0]

    t1, t2 = np.meshgrid(times_f,times_f)

    # t1, t2 are in units of days; fL in units of 1/year (sidereal for both?)
    # so typical values here are 10^-6 to 10^-3
    x = 2 * np.pi * (day/year) * fL * np.abs(t1 - t2)

    del t1
    del t2

    # note that the gamma is singular for all half-integer alpha < 1.5
    #
    # for -1 < alpha < 0, the x exponent ranges from 4 to 2 (it's 3.33 for alpha = -2/3)
    # so for the lower alpha values it will be comparable in value to the x**2 term of ksum
    #
    # possible alpha limits for a search could be [-0.95,-0.55] in which case the sign of `power`
    # is always positive, and the x exponent ranges from ~ 3 to 4... no problem with cancellation

    # The tolerance for which to use the Gamma function expansion
    tol = 1e-5

    # the exact solutions for alpha = 0, -1 should be acceptable in a small interval around them...
    if abs(alpha) < 1e-7:
        cosx, sinx = np.cos(x), np.sin(x)

        power = cosx - x * sinx
        sinint, cosint = sl.sici(x)

        corr = (year**2 * fL**-2) / (24 * math.pi**2) * (power + x**2 * cosint)
    elif abs(alpha + 1) < 1e-7:
        cosx, sinx = np.cos(x), np.sin(x)

        power = 6 * cosx - 2 * x * sinx - x**2 * cosx + x**3 * sinx
        sinint, cosint = ss.sici(x)

        corr = (year**2 * fL**-4) / (288 * np.pi**2) * (power - x**4 * cosint)
    else:
        # leading-order expansion of Gamma[-2+2*alpha]*Cos[Pi*alpha] around -0.5 and 0.5
        if   abs(alpha - 0.5) < tol:
            cf =  np.pi/2   + (np.pi - np.pi*EulerGamma)              * (alpha - 0.5)
        elif abs(alpha + 0.5) < tol:
            cf = -np.pi/12  + (-11*np.pi/36 + EulerGamma*math.pi/6)     * (alpha + 0.5)
        elif abs(alpha + 1.5) < tol:
            cf =  np.pi/240 + (137*np.pi/7200 - EulerGamma*np.pi/120) * (alpha + 1.5)
        else:
            cf = ss.gamma(-2+2*alpha) * np.cos(np.pi*alpha)

        power = cf * x**(2-2*alpha)

        # Mathematica solves Sum[(-1)^n x^(2 n)/((2 n)! (2 n + 2 alpha - 2)), {n, 0, Infinity}]
        # as HypergeometricPFQ[{-1+alpha}, {1/2,alpha}, -(x^2/4)]/(2 alpha - 2)
        # the corresponding scipy.special function is hyp1f2 (which returns value and error)
        # TO DO, for speed: could replace with the first few terms of the sum!
        if approx_ksum:
            ksum = 1.0 / (2*alpha - 2) - x**2 / (4*alpha) + x**4 / (24 * (2 + 2*alpha))
        else:
            ksum = ss.hyp1f2(alpha-1,0.5,alpha,-0.25*x**2)[0]/(2*alpha-2)

        del x

        # this form follows from Eq. (A31) of Lee, Jenet, and Price ApJ 684:1304 (2008)
        corr = -(year**2 * fL**(-2+2*alpha)) / (12 * np.pi**2) * (power + ksum)
        
    return corr