Esempio n. 1
0
def _accR(R, z, dens, sigma, qintr, bhMass, soft):

    mgepot = np.empty_like(R)
    pot = np.empty_like(dens)
    e2 = 1. - qintr**2
    s2 = sigma**2
    r2 = R**2
    z2 = z**2
    d2 = r2 + z2

    for k in range(R.size):
        for j in range(dens.size):
            if (d2[k] < s2[j] / 240.**2):
                e = np.sqrt(
                    e2[j]
                )  # pot is Integral in {u,0,1} of -D[H[R,z,u],R]/R at (R,z)=0
                pot[j] = (np.arcsin(e) / e - qintr[j]) / (
                    2 * e2[j] * s2[j])  # Cfr. equation (A5)
            elif (d2[k] < s2[j] * 245**2):
                pot[j] = quadva(_accelerationR_dRRcapitalh, [0., 1.],
                                args=(r2[k], z2[k], e2[j], s2[j]))[0]
            else:  # R acceleration in Keplerian limit (Cappellari et al. 2002)
                pot[j] = np.sqrt(
                    np.pi / 2) * sigma[j] / d2[k]**1.5  # Cfr. equation (A4)
        mgepot[k] = np.sum(s2 * qintr * dens * pot)

    G = 0.00430237  # (km/s)**2 pc/Msun [6.674e-11 SI units (CODATA-10)]

    return -R * (4 * np.pi * G * mgepot + G * bhMass / (d2 + soft**2)**1.5)
Esempio n. 2
0
def _accR(R, z, dens, sigma, qintr, bhMass, soft):

    mgepot = np.empty_like(R)
    pot = np.empty_like(dens)
    e2 = 1. - qintr**2
    s2 = sigma**2
    r2 = R**2
    z2 = z**2
    d2 = r2 + z2
    
    for k in range(R.size):
        for j in range(dens.size):
            if (d2[k] < s2[j]/240.**2):
                e = np.sqrt(e2[j]) # pot is Integral in {u,0,1} of -D[H[R,z,u],R]/R at (R,z)=0
                pot[j] = (np.arcsin(e)/e - qintr[j])/(2*e2[j]*s2[j]) # Cfr. equation (A5)
            elif (d2[k] < s2[j]*245**2):
                pot[j] = quadva(_accelerationR_dRRcapitalh, [0.,1.], 
                                args=(r2[k], z2[k], e2[j], s2[j]))[0]
            else: # R acceleration in Keplerian limit (Cappellari et al. 2002)
               pot[j] = np.sqrt(np.pi/2)*sigma[j]/d2[k]**1.5 # Cfr. equation (A4)
        mgepot[k] = np.sum(s2*qintr*dens*pot)
    
    G = 0.00430237    # (km/s)**2 pc/Msun [6.674e-11 SI units (CODATA-10)]
    
    return -R*(4*np.pi*G*mgepot + G*bhMass/(d2 + soft**2)**1.5)
Esempio n. 3
0
def _wvrms2(x_pc, y_pc, inc, lum3d_pc, pot3d_pc, beta, tensor):
    '''
    Calculate the surface brightness weighted vrms2 for all the points x, y
    --input parameters
    x_pc, y_pc: 1d array for x, y position in pc
    inc: inclination in rad
    lum3d_pc, pot3d_pc: N*3 array for mge coefficients, L in L_solar/pc^2,
                        sigma in pc
    beta: Anisotropy paramter vactor for each luminous Gaussian component
    tensor: moments to be calculated, usually zz (LOS)
    --output paramters:
    wvrms2: surface brightness weighted vrms2 (1d array with the same
            length as x_pc)
    '''
    wvrms2 = np.empty_like(x_pc)
    for i in range(len(x_pc)):
        '''
        lhy: Change _integrand function to enable scipy integration!
        wvrms2[i] = quad(_integrand, 0.0, 1.0,
                         args=(lum3d_pc[:, 0], lum3d_pc[:, 1],
                               lum3d_pc[:, 2], pot3d_pc[:, 0],
                               pot3d_pc[:, 1], pot3d_pc[:, 2],
                               x_pc[i], y_pc[i], inc, beta, tensor))
        '''
        wvrms2[i] = quadva(_integrand, [0., 1.],
                           args=(lum3d_pc[:, 0], lum3d_pc[:, 1], lum3d_pc[:, 2],
                                 pot3d_pc[:, 0], pot3d_pc[:, 1], pot3d_pc[:, 2],
                                 x_pc[i], y_pc[i], inc, beta, tensor))[0]
    # lhy This could be translated to c
    return wvrms2
Esempio n. 4
0
def _surf_vlos(x1, y1, inc_deg,
               surf_lum, sigma_lum, qobs_lum,
               surf_pot, sigma_pot, qobs_pot,
               beta, kappa, gamma, component, nsteps):
    """
    This routine gives the projected weighted first moment Sigma*<V_los>

    """
    # Axisymmetric deprojection of both luminous and total mass.
    # See equation (12)-(14) of Cappellari (2008)
    #
    inc = np.radians(inc_deg)

    qintr_lum = qobs_lum**2 - np.cos(inc)**2
    if np.any(qintr_lum <= 0):
        raise RuntimeError('Inclination too low q < 0')
    qintr_lum = np.sqrt(qintr_lum)/np.sin(inc)
    if np.any(qintr_lum < 0.05):
        raise RuntimeError('q < 0.05 components')
    dens_lum = surf_lum*qobs_lum / (sigma_lum*qintr_lum*np.sqrt(2*np.pi))

    qintr_pot = qobs_pot**2 - np.cos(inc)**2
    if np.any(qintr_pot <= 0):
        raise RuntimeError('Inclination too low q < 0')
    qintr_pot = np.sqrt(qintr_pot)/np.sin(inc)
    if np.any(qintr_pot < 0.05):
        raise RuntimeError('q < 0.05 components')
    dens_pot = surf_pot*qobs_pot / (sigma_pot*qintr_pot*np.sqrt(2*np.pi))

    s2_lum = sigma_lum**2
    q2_lum = qintr_lum**2
    s2_pot = sigma_pot**2
    q2_pot = qintr_pot**2
    bani = 1./(1. - beta) # Anisotropy ratio b = (sig_R/sig_z)**2

    dens_lum = dens_lum[:, None, None, None]
    s2_lum = s2_lum[:, None, None, None]
    q2_lum = q2_lum[:, None, None, None]
    bani = bani[:, None, None, None]
    kappa = kappa[:, None, None, None]
    gamma = gamma[:, None, None, None]

    dens_pot = dens_pot[None, :, None, None]
    s2_pot = s2_pot[None, :, None, None]
    q2_pot = q2_pot[None, :, None, None]

    # Restrict LOS integration to the range where the luminosity density
    # is above 1/1000 of the peak value along that LOS
    #
    ms = 3*np.max(sigma_lum)
    sh = np.sinh(np.linspace(0, 3, 50))
    z1 = ms/sh[-1]*sh  # sinh sampling of z1 in [0, ms]
    R2 = (z1*np.sin(inc) - y1*np.cos(inc))**2 + x1**2 # R^2 Equation (25)
    z2 = (z1*np.cos(inc) + y1*np.sin(inc))**2
    nu = dens_lum * np.exp(-0.5/s2_lum * (R2 + z2/q2_lum))
    nu = np.sum(nu, axis=(0, 1, 2))
    j = np.argmax(nu)
    w = (nu < nu[j]/1e3) & (z1 > z1[j])  # allow for negative Gaussians
    if np.any(w):
        ms = np.min(z1[w])

    sb_mu1 = quadva(_los_integrand, [-ms, ms], epsrel=1e-3,
                    args=(dens_lum, s2_lum, q2_lum, dens_pot, s2_pot, q2_pot,
                          x1, y1, inc, bani, kappa, gamma, component, nsteps))

    return sb_mu1[0] # Ignore the integral error
Esempio n. 5
0
def _vrms2(x, y, inc_deg, surf_lum, sigma_lum, qobs_lum, surf_pot, sigma_pot,
           qobs_pot, beta, tensor, sigmaPsf, normPsf, pixSize, pixAng, step,
           nrad, nang):
    """
    This routine gives the second V moment after convolution with a PSF.
    The convolution is done using interpolation of the model on a
    polar grid, as described in Appendix A of Cappellari (2008).

    """
    # Axisymmetric deprojection of both luminous and total mass.
    # See equation (12)-(14) of Cappellari (2008)
    #
    inc = np.radians(inc_deg)

    qintr_lum = qobs_lum**2 - np.cos(inc)**2
    if np.any(qintr_lum <= 0):
        raise RuntimeError('Inclination too low q < 0')
    qintr_lum = np.sqrt(qintr_lum) / np.sin(inc)
    if np.any(qintr_lum < 0.05):
        raise RuntimeError('q < 0.05 components')
    dens_lum = surf_lum * qobs_lum / (sigma_lum * qintr_lum *
                                      np.sqrt(2 * np.pi))

    qintr_pot = qobs_pot**2 - np.cos(inc)**2
    if np.any(qintr_pot <= 0):
        raise RuntimeError('Inclination too low q < 0')
    qintr_pot = np.sqrt(qintr_pot) / np.sin(inc)
    if np.any(qintr_pot < 0.05):
        raise RuntimeError('q < 0.05 components')
    dens_pot = surf_pot * qobs_pot / (sigma_pot * qintr_pot *
                                      np.sqrt(2 * np.pi))

    # Define parameters of polar grid for interpolation
    #
    w = sigma_lum < np.max(
        np.abs(x))  # Characteristic MGE axial ratio in observed range

    if w.sum() < 3:
        qmed = np.median(qobs_lum)
    else:
        qmed = np.median(qobs_lum[w])

    rell = np.sqrt(x**2 + (y / qmed)**2)  # Elliptical radius of input (x, y)

    psfConvolution = (np.max(sigmaPsf) > 0) and (pixSize > 0)

    # Kernel step is 1/4 of largest value between sigma(min) and 1/2 pixel side.
    # Kernel half size is the sum of 3*sigma(max) and 1/2 pixel diagonal.
    #
    if (nrad * nang >
            x.size) and (not psfConvolution):  # Just calculate values

        xPol = x
        yPol = y

    else:  # Interpolate values on polar grid

        if psfConvolution:  # PSF convolution
            if step == 0:
                step = max(pixSize / 2., np.min(sigmaPsf)) / 4.
            mx = 3 * np.max(sigmaPsf) + pixSize / np.sqrt(2)
        else:  # No convolution
            step = np.min(rell.clip(1))  # Minimum radius of 1pc
            mx = 0

        # Make linear grid in log of elliptical radius RAD and eccentric anomaly ANG
        # See Appendix A
        #
        rmax = np.max(
            rell
        ) + mx  # Major axis of ellipse containing all data + convolution
        logRad = np.linspace(np.log(step), np.log(rmax),
                             nrad)  # Linear grid in np.log(rell)
        ang = np.linspace(0, np.pi / 2,
                          nang)  # Linear grid in eccentric anomaly
        radGrid, angGrid = map(np.ravel, np.meshgrid(np.exp(logRad), ang))
        xPol = radGrid * np.cos(angGrid)
        yPol = radGrid * np.sin(angGrid) * qmed

    # The model Vrms computation is only performed on the polar grid
    # which is then used to interpolate the values at any other location
    #
    wm2Pol = np.empty_like(xPol)
    mgePol = np.empty_like(xPol)
    for j in range(xPol.size):
        wm2Pol[j] = quadva(_integrand, [0., 1.],
                           args=(dens_lum, sigma_lum, qintr_lum, dens_pot,
                                 sigma_pot, qintr_pot, xPol[j], yPol[j], inc,
                                 beta, tensor))[0]
        mgePol[j] = np.sum(surf_lum * np.exp(-0.5 / sigma_lum**2 *
                                             (xPol[j]**2 +
                                              (yPol[j] / qobs_lum)**2)))

    if psfConvolution:  # PSF convolution

        nx = np.ceil(rmax / step)
        ny = np.ceil(rmax * qmed / step)
        x1 = np.linspace(-nx, nx, 2 * nx) * step
        y1 = np.linspace(-ny, ny, 2 * ny) * step
        xCar, yCar = np.meshgrid(x1, y1)  # Cartesian grid for convolution

        # Interpolate MGE model and Vrms over cartesian grid
        #
        r1 = 0.5 * np.log(
            xCar**2 +
            (yCar / qmed)**2)  # Log elliptical radius of cartesian grid
        e1 = np.arctan2(np.abs(yCar / qmed),
                        np.abs(xCar))  # Eccentric anomaly of cartesian grid

        wm2Car = bilinear_interpolate(logRad, ang, wm2Pol.reshape(nang, nrad),
                                      r1, e1)
        mgeCar = bilinear_interpolate(logRad, ang, mgePol.reshape(nang, nrad),
                                      r1, e1)

        nk = np.ceil(mx / step)
        kgrid = np.linspace(-nk, nk, 2 * nk) * step
        xgrid, ygrid = np.meshgrid(kgrid, kgrid)  # Kernel is square
        if pixAng != 0:
            xgrid, ygrid = rotate_points(xgrid, ygrid, pixAng)

        # Compute kernel with equation (A6) of Cappellari (2008).
        # Normaliztion is irrelevant here as it cancels out.
        #
        kernel = np.zeros_like(xgrid)
        dx = pixSize / 2
        sp = np.sqrt(2) * sigmaPsf
        for j in range(len(sigmaPsf)):
            kernel += normPsf[j] \
                * (special.erf((dx-xgrid)/sp[j]) + special.erf((dx+xgrid)/sp[j])) \
                * (special.erf((dx-ygrid)/sp[j]) + special.erf((dx+ygrid)/sp[j]))
        kernel /= np.sum(kernel)

        # Seeing and aperture convolution with equation (A3)
        #
        muCar = signal.fftconvolve(wm2Car, kernel, mode='same') \
              / signal.fftconvolve(mgeCar, kernel, mode='same')

        # Interpolate convolved image at observed apertures.
        # Aperture integration was already included in the kernel.
        #
        mu = bilinear_interpolate(x1, y1, muCar, x, y)

    else:  # No PSF convolution

        muPol = wm2Pol / mgePol

        if nrad * nang > x.size:  # Just returns values
            mu = muPol
        else:  # Interpolate values
            r1 = 0.5 * np.log(
                x**2 + (y / qmed)**2)  # Log elliptical radius of input (x,y)
            e1 = np.arctan2(np.abs(y / qmed),
                            np.abs(x))  # Eccentric anomaly of input (x,y)
            mu = bilinear_interpolate(logRad, ang, muPol.reshape(nang, nrad),
                                      r1, e1)

    return mu
Esempio n. 6
0
def _surf_vlos(x1, y1, inc_deg, surf_lum, sigma_lum, qobs_lum, surf_pot,
               sigma_pot, qobs_pot, beta, kappa, gamma, component, nsteps):
    """
    This routine gives the projected weighted first moment Sigma*<V_los>

    """
    # Axisymmetric deprojection of both luminous and total mass.
    # See equation (12)-(14) of Cappellari (2008)
    #
    inc = np.radians(inc_deg)

    qintr_lum = qobs_lum**2 - np.cos(inc)**2
    if np.any(qintr_lum <= 0):
        raise RuntimeError('Inclination too low q < 0')
    qintr_lum = np.sqrt(qintr_lum) / np.sin(inc)
    if np.any(qintr_lum < 0.05):
        raise RuntimeError('q < 0.05 components')
    dens_lum = surf_lum * qobs_lum / (sigma_lum * qintr_lum *
                                      np.sqrt(2 * np.pi))

    qintr_pot = qobs_pot**2 - np.cos(inc)**2
    if np.any(qintr_pot <= 0):
        raise RuntimeError('Inclination too low q < 0')
    qintr_pot = np.sqrt(qintr_pot) / np.sin(inc)
    if np.any(qintr_pot < 0.05):
        raise RuntimeError('q < 0.05 components')
    dens_pot = surf_pot * qobs_pot / (sigma_pot * qintr_pot *
                                      np.sqrt(2 * np.pi))

    s2_lum = sigma_lum**2
    q2_lum = qintr_lum**2
    s2_pot = sigma_pot**2
    q2_pot = qintr_pot**2
    bani = 1. / (1. - beta)  # Anisotropy ratio b = (sig_R/sig_z)**2

    dens_lum = dens_lum[:, None, None, None]
    s2_lum = s2_lum[:, None, None, None]
    q2_lum = q2_lum[:, None, None, None]
    bani = bani[:, None, None, None]
    kappa = kappa[:, None, None, None]
    gamma = gamma[:, None, None, None]

    dens_pot = dens_pot[None, :, None, None]
    s2_pot = s2_pot[None, :, None, None]
    q2_pot = q2_pot[None, :, None, None]

    # Restrict LOS integration to the range where the luminosity density
    # is above 1/1000 of the peak value along that LOS
    #
    ms = 3 * np.max(sigma_lum)
    sh = np.sinh(np.linspace(0, 3, 50))
    z1 = ms / sh[-1] * sh  # sinh sampling of z1 in [0, ms]
    R2 = (z1 * np.sin(inc) - y1 * np.cos(inc))**2 + x1**2  # R^2 Equation (25)
    z2 = (z1 * np.cos(inc) + y1 * np.sin(inc))**2
    nu = dens_lum * np.exp(-0.5 / s2_lum * (R2 + z2 / q2_lum))
    nu = np.sum(nu, axis=(0, 1, 2))
    j = np.argmax(nu)
    w = (nu < nu[j] / 1e3) & (z1 > z1[j])  # allow for negative Gaussians
    if np.any(w):
        ms = np.min(z1[w])

    sb_mu1 = quadva(_los_integrand, [-ms, ms],
                    epsrel=1e-3,
                    args=(dens_lum, s2_lum, q2_lum, dens_pot, s2_pot, q2_pot,
                          x1, y1, inc, bani, kappa, gamma, component, nsteps))

    return sb_mu1[0]  # Ignore the integral error
Esempio n. 7
0
def _second_moment(R, sig_l, sig_m, lum, mass, Mbh, beta, tensor,
                     sigmaPsf, normPsf, step, nrad, surf_l, pixSize):
    """
    This routine gives the second V moment after convolution with a PSF.
    The convolution is done using interpolation of the model on a
    polar grid, as described in Appendix A of Cappellari (2008).

    """
    if (max(sigmaPsf) > 0) and (pixSize > 0): # PSF convolution

        # Kernel step is 1/4 of largest value between sigma(min) and 1/2 pixel side.
        # Kernel half size is the sum of 3*sigma(max) and 1/2 pixel diagonal.
        #
        if step == 0:
            step = max(pixSize/2., np.min(sigmaPsf))/4.
        mx = 3*np.max(sigmaPsf) + pixSize/np.sqrt(2)

        # Make grid linear in log of radius RR
        #
        rmax = np.max(R) + mx # Radius of circle containing all data + convolution
        logRad = np.linspace(np.log(step), np.log(rmax), nrad) # Linear grid in log(RR)
        rr = np.exp(logRad)

        # The model Vrms computation is only performed on the radial grid
        # which is then used to interpolate the values at any other location
        #
        wm2Pol = np.empty_like(rr)
        mgePol = np.empty_like(rr)
        rup = 3*np.max(sig_l)
        for j in range(rr.size): # Integration of equation (50)
            wm2Pol[j] = quadva(_integrand, [rr[j], rup],
                               args=(sig_l, sig_m, lum, mass, Mbh, rr[j], beta, tensor))[0]
            mgePol[j] = np.sum(surf_l * np.exp(-0.5*(rr[j]/sig_l)**2))

        nx = np.ceil(rmax/step)
        x1 = np.linspace(-nx, nx, 2*nx)*step
        xCar, yCar = np.meshgrid(x1, x1)  # Cartesian grid for convolution

        # Interpolate MGE model and Vrms over cartesian grid
        #
        r1 = 0.5*np.log(xCar**2 + yCar**2) # Log radius of cartesian grid
        wm2Car = np.interp(r1, logRad, wm2Pol)
        mgeCar = np.interp(r1, logRad, mgePol)

        nk = np.ceil(mx/step)
        kgrid = np.linspace(-nk, nk, 2*nk)*step
        xgrid, ygrid = np.meshgrid(kgrid, kgrid) # Kernel is square

        # Compute kernel with equation (A6) of Cappellari (2008).
        # Normalization is irrelevant here as it cancels out.
        #
        kernel = np.zeros_like(xgrid)
        dx = pixSize/2
        sp = np.sqrt(2)*sigmaPsf
        for j in range(len(sigmaPsf)):
            kernel += normPsf[j] \
                * (special.erf((dx-xgrid)/sp[j]) + special.erf((dx+xgrid)/sp[j])) \
                * (special.erf((dx-ygrid)/sp[j]) + special.erf((dx+ygrid)/sp[j]))
        kernel /= np.sum(kernel)

        # Seeing and aperture convolution with equation (A3)
        #
        muCar = np.sqrt(signal.fftconvolve(wm2Car, kernel, mode='same')
                      / signal.fftconvolve(mgeCar, kernel, mode='same'))

        # Interpolate convolved image at observed apertures.
        # Aperture integration was already included in the kernel.
        #
        mu = bilinear_interpolate(x1, x1, muCar, R/np.sqrt(2), R/np.sqrt(2))

    else: # No PSF convolution: just compute values

        mu = np.empty_like(R)
        rmax = 3*np.max(sig_l)
        for j in range(R.size):
            wm2Pol = quadva(_integrand, [R[j], rmax],
                            args=(sig_l, sig_m, lum, mass, Mbh, R[j], beta, tensor))[0]
            mgePol = np.sum( surf_l * np.exp(-0.5*(R[j]/sig_l)**2) )
            mu[j] = np.sqrt(wm2Pol/mgePol)

    return mu