示例#1
0
    def _compute_zenith_angle(self, latitude, lst, ra, dec):
        """
        Compute the zenith angle for one or many ra/dec.

        Parameters
        ----------
        latitude : `float`
           Observatory latitude (degrees)
        lst : `float`
           Local sidereal time (degrees)
        ra : `np.ndarray`
           Right ascension
        dec : `np.ndarray`
           Declination

        Returns
        -------
        zenith : `np.ndarray`
           Zenith angle(s) in radians
        """
        c_ra = ra * coord.degrees
        c_dec = dec * coord.degrees
        c_ha = (lst - ra) * coord.degrees
        c_lat = latitude * coord.degrees
        c_zenith = coord.CelestialCoord(c_ha + c_ra, c_lat)
        c_pointing = coord.CelestialCoord(c_ra, c_dec)
        zenith_angle = c_pointing.distanceTo(c_zenith).rad

        return zenith_angle
示例#2
0
    def _compute_zenith_and_par_angles(self, lst, ra, dec):
        """
        Compute the zenith angle for a given ra/dec

        Parameters
        ----------
        lst : `float`
           Local sidereal time (degrees)
        ra : `float`
           RA in degrees
        dec : `float`
           Dec in degrees

        Returns
        -------
        zenith_angle : `float`
           Zenith angle in radians.
        parallactic_angle : `float`, optional
           Parallactic angle in radians.
        """
        c_ra = ra * coord.degrees
        c_dec = dec * coord.degrees
        c_ha = (lst - ra) * coord.degrees
        c_lat = self.config.latitude * coord.degrees
        c_zenith = coord.CelestialCoord(c_ha + c_ra, c_lat)
        c_pointing = coord.CelestialCoord(c_ra, c_dec)
        zenith_angle = c_pointing.distanceTo(c_zenith).rad

        c_NCP = coord.CelestialCoord(0.0 * coord.degrees, 90.0 * coord.degrees)
        parallactic_angle = c_pointing.angleBetween(c_NCP, c_zenith).rad

        return zenith_angle, parallactic_angle
示例#3
0
def test_coord():
    pole = coord.CelestialCoord(0. * coord.degrees, 90. * coord.degrees)
    u = np.random.uniform(-0.5, 0.5, size=10000)
    v = np.random.uniform(-0.5, 0.5, size=10000)

    for projection in ['gnomonic', 'stereographic', 'postel', 'lambert']:
        ra, dec = pole.deproject_rad(u, v, projection=projection)
        xcos, ycos, zcos = batoid.utils.fieldToDirCos(u,
                                                      v,
                                                      projection=projection)
        np.testing.assert_allclose(-np.sin(dec), zcos, rtol=0, atol=1e-13)
        np.testing.assert_allclose(np.abs((np.pi / 2 - ra) -
                                          np.arctan2(ycos, xcos)),
                                   np.pi,
                                   rtol=0,
                                   atol=1e-13)
示例#4
0
def bestPA(world_pos, date):
    """
    This routine determines the best position angle for the observatory for a given observation date
    and position on the sky.

    The best/optimal position angle is determined by the fact that the solar panels are at 90
    degrees to the position being observed, and it is best to have those facing the Sun as directly
    as possible.  Note that if a given ``world_pos`` is not actually observable on the given
    ``date``, then this routine will return None.

    Parameters:
        world_pos:      A galsim.CelestialCoord indicating the position at which the observer
                        wishes to look.
        date:           A python datetime object indicating the desired date of observation.

    Returns:
        the best position angle for the observatory, as a galsim.Angle, or None if the position
        is not observable.
    """
    # First check for observability.
    if not allowedPos(world_pos, date):
        return None

    # Find the location of the sun on this date.  +X_observatory points out into the sky, towards
    # world_pos, while +Z is in the plane of the sky pointing towards the sun as much as possible.
    lam = coord.util.sun_position_ecliptic(date)
    sun = coord.CelestialCoord.from_ecliptic(lam, 0 * coord.radians, date.year)
    # Now we do a projection onto the sky centered at world_pos to find the (u, v) for the Sun.
    sun_tp_x, sun_tp_y = world_pos.project(sun, 'gnomonic')

    # We want to rotate around by 90 degrees to find the +Y obs direction.  Specifically, we want
    # (+X, +Y, +Z)_obs to form a right-handed coordinate system.
    y_obs_tp_x, y_obs_tp_y = -sun_tp_y, sun_tp_x
    y_obs = world_pos.deproject(y_obs_tp_x, y_obs_tp_y, 'gnomonic')

    # Finally the observatory position angle is defined by the angle between +Y_observatory and the
    # celestial north pole.  It is defined as position angle east of north.
    north = coord.CelestialCoord(y_obs.ra, 90. * coord.degrees)
    obs_pa = world_pos.angleBetween(y_obs, north)
    return obs_pa
示例#5
0
    def toSky(self, x, y):
        '''
        Convert xy coordinates in the gnomonic project (in degrees) into ra, dec.
        '''
        try:
            import coord
            pole = coord.CelestialCoord(self.pole_ra * coord.degrees,
                                        self.pole_dec * coord.degrees)
            deg_per_radian = coord.radians / coord.degrees
            # Coord wants these in radians, not degrees
            # Also, a - sign for x, since astropy uses +ra as +x direction.
            x /= -deg_per_radian
            y /= deg_per_radian
            # apply rotation
            if self.rotation != 0.:
                # TODO: I'm not sure if I have the sense of the rotation correct here.
                #       The "complex wcs" test has PA = 0, so I wasn't able to test it.
                #       There may be a sign error on the s terms.
                s, c = (self.rotation * coord.degrees).sincos()
                x, y = x * c - y * s, x * s + y * c
            # apply projection
            ra, dec = pole.deproject_rad(x, y, projection='gnomonic')
            ra *= deg_per_radian
            dec *= deg_per_radian
            return ra, dec

        except ImportError:
            if self.frame is None: self._set_frame()

            # Get the y and z components of unit-sphere coords, x on pole axis
            y, z = x, y
            y *= np.pi / 180.
            z *= np.pi / 180.
            temp = np.sqrt(1 + y * y + z * z)
            y /= temp
            z /= temp
            dec = np.arcsin(z)
            ra = np.arcsin(y / np.cos(dec))
            coord = co.SkyCoord(ra, dec, unit='rad', frame=self.frame)
            return coord.icrs.ra.deg, coord.icrs.dec.deg
示例#6
0
def test_coord():
    rng = np.random.default_rng(57721566)
    import coord
    pole = coord.CelestialCoord(0. * coord.degrees, 90. * coord.degrees)
    u = rng.uniform(-0.5, 0.5, size=10000)
    v = rng.uniform(-0.5, 0.5, size=10000)

    for projection in ['gnomonic', 'stereographic', 'postel', 'lambert']:
        ra, dec = pole.deproject_rad(u, v, projection=projection)
        xcos, ycos, zcos = batoid.utils.fieldToDirCos(u,
                                                      v,
                                                      projection=projection)
        np.testing.assert_allclose(-np.sin(dec), zcos, rtol=0, atol=1e-13)
        np.testing.assert_allclose(np.abs((np.pi / 2 - ra) -
                                          np.arctan2(ycos, xcos)),
                                   np.pi,
                                   rtol=0,
                                   atol=1e-13)

    # Check invalid input
    with np.testing.assert_raises(ValueError):
        batoid.utils.fieldToDirCos(u, v, projection="banana")
    with np.testing.assert_raises(ValueError):
        batoid.utils.dirCosToField(u, v, v, projection="banana")
示例#7
0
def test_direct_spherical():
    # Repeat in spherical coords

    ngal = 50
    s = 10.
    rng = np.random.RandomState(8675309)
    x = rng.normal(0,s, (ngal,) )
    y = rng.normal(0,s, (ngal,) ) + 200  # Put everything at large y, so small angle on sky
    z = rng.normal(0,s, (ngal,) )
    w = rng.random_sample(ngal)
    g1 = rng.normal(0,0.2, (ngal,) )
    g2 = rng.normal(0,0.2, (ngal,) )
    w = np.ones_like(w)

    ra, dec = coord.CelestialCoord.xyz_to_radec(x,y,z)

    cat = treecorr.Catalog(ra=ra, dec=dec, ra_units='rad', dec_units='rad', w=w, g1=g1, g2=g2)

    min_sep = 1.
    bin_size = 0.2
    nrbins = 10
    nubins = 5
    nvbins = 5
    max_sep = min_sep * np.exp(nrbins * bin_size)
    ggg = treecorr.GGGCorrelation(min_sep=min_sep, bin_size=bin_size, nbins=nrbins,
                                  sep_units='deg', brute=True)
    ggg.process(cat)

    r = np.sqrt(x**2 + y**2 + z**2)
    x /= r;  y /= r;  z /= r
    north_pole = coord.CelestialCoord(0*coord.radians, 90*coord.degrees)

    true_ntri = np.zeros((nrbins, nubins, 2*nvbins), dtype=int)
    true_weight = np.zeros((nrbins, nubins, 2*nvbins), dtype=float)
    true_gam0 = np.zeros((nrbins, nubins, 2*nvbins), dtype=complex)
    true_gam1 = np.zeros((nrbins, nubins, 2*nvbins), dtype=complex)
    true_gam2 = np.zeros((nrbins, nubins, 2*nvbins), dtype=complex)
    true_gam3 = np.zeros((nrbins, nubins, 2*nvbins), dtype=complex)

    rad_min_sep = min_sep * coord.degrees / coord.radians
    rad_max_sep = max_sep * coord.degrees / coord.radians
    c = [coord.CelestialCoord(r*coord.radians, d*coord.radians) for (r,d) in zip(ra, dec)]
    for i in range(ngal):
        for j in range(i+1,ngal):
            for k in range(j+1,ngal):
                d12 = np.sqrt((x[i]-x[j])**2 + (y[i]-y[j])**2 + (z[i]-z[j])**2)
                d23 = np.sqrt((x[j]-x[k])**2 + (y[j]-y[k])**2 + (z[j]-z[k])**2)
                d31 = np.sqrt((x[k]-x[i])**2 + (y[k]-y[i])**2 + (z[k]-z[i])**2)

                d3, d2, d1 = sorted([d12, d23, d31])
                rindex = np.floor(np.log(d2/rad_min_sep) / bin_size).astype(int)
                if rindex < 0 or rindex >= nrbins: continue

                if [d1, d2, d3] == [d23, d31, d12]: ii,jj,kk = i,j,k
                elif [d1, d2, d3] == [d23, d12, d31]: ii,jj,kk = i,k,j
                elif [d1, d2, d3] == [d31, d12, d23]: ii,jj,kk = j,k,i
                elif [d1, d2, d3] == [d31, d23, d12]: ii,jj,kk = j,i,k
                elif [d1, d2, d3] == [d12, d23, d31]: ii,jj,kk = k,i,j
                elif [d1, d2, d3] == [d12, d31, d23]: ii,jj,kk = k,j,i
                else: assert False
                # Now use ii, jj, kk rather than i,j,k, to get the indices
                # that correspond to the points in the right order.

                u = d3/d2
                v = (d1-d2)/d3
                if ( ((x[jj]-x[ii])*(y[kk]-y[ii]) - (x[kk]-x[ii])*(y[jj]-y[ii])) * z[ii] +
                     ((y[jj]-y[ii])*(z[kk]-z[ii]) - (y[kk]-y[ii])*(z[jj]-z[ii])) * x[ii] +
                     ((z[jj]-z[ii])*(x[kk]-x[ii]) - (z[kk]-z[ii])*(x[jj]-x[ii])) * y[ii] ) > 0:
                    v = -v

                uindex = np.floor(u / bin_size).astype(int)
                assert 0 <= uindex < nubins
                vindex = np.floor((v+1) / bin_size).astype(int)
                assert 0 <= vindex < 2*nvbins

                # Rotate shears to coordinates where line connecting to center is horizontal.
                # Original orientation is where north is up.
                cenx = (x[i] + x[j] + x[k])/3.
                ceny = (y[i] + y[j] + y[k])/3.
                cenz = (z[i] + z[j] + z[k])/3.
                cen = coord.CelestialCoord.from_xyz(cenx,ceny,cenz)

                theta1 = 90*coord.degrees - c[ii].angleBetween(north_pole, cen)
                theta2 = 90*coord.degrees - c[jj].angleBetween(north_pole, cen)
                theta3 = 90*coord.degrees - c[kk].angleBetween(north_pole, cen)
                exp2theta1 = np.cos(2*theta1) + 1j * np.sin(2*theta1)
                exp2theta2 = np.cos(2*theta2) + 1j * np.sin(2*theta2)
                exp2theta3 = np.cos(2*theta3) + 1j * np.sin(2*theta3)

                www = w[i] * w[j] * w[k]
                g1p = (g1[ii] + 1j*g2[ii]) * exp2theta1
                g2p = (g1[jj] + 1j*g2[jj]) * exp2theta2
                g3p = (g1[kk] + 1j*g2[kk]) * exp2theta3
                gam0 = www * g1p * g2p * g3p
                gam1 = www * np.conjugate(g1p) * g2p * g3p
                gam2 = www * g1p * np.conjugate(g2p) * g3p
                gam3 = www * g1p * g2p * np.conjugate(g3p)

                true_ntri[rindex,uindex,vindex] += 1
                true_weight[rindex,uindex,vindex] += www
                true_gam0[rindex,uindex,vindex] += gam0
                true_gam1[rindex,uindex,vindex] += gam1
                true_gam2[rindex,uindex,vindex] += gam2
                true_gam3[rindex,uindex,vindex] += gam3

    pos = true_weight > 0
    true_gam0[pos] /= true_weight[pos]
    true_gam1[pos] /= true_weight[pos]
    true_gam2[pos] /= true_weight[pos]
    true_gam3[pos] /= true_weight[pos]

    np.testing.assert_array_equal(ggg.ntri, true_ntri)
    np.testing.assert_allclose(ggg.weight, true_weight, rtol=1.e-5, atol=1.e-8)
    np.testing.assert_allclose(ggg.gam0r, true_gam0.real, rtol=1.e-5, atol=1.e-8)
    np.testing.assert_allclose(ggg.gam0i, true_gam0.imag, rtol=1.e-5, atol=1.e-8)
    np.testing.assert_allclose(ggg.gam1r, true_gam1.real, rtol=1.e-5, atol=1.e-8)
    np.testing.assert_allclose(ggg.gam1i, true_gam1.imag, rtol=1.e-5, atol=1.e-8)
    np.testing.assert_allclose(ggg.gam2r, true_gam2.real, rtol=1.e-5, atol=1.e-8)
    np.testing.assert_allclose(ggg.gam2i, true_gam2.imag, rtol=1.e-5, atol=1.e-8)
    np.testing.assert_allclose(ggg.gam3r, true_gam3.real, rtol=1.e-5, atol=1.e-8)
    np.testing.assert_allclose(ggg.gam3i, true_gam3.imag, rtol=1.e-5, atol=1.e-8)

    try:
        import fitsio
    except ImportError:
        print('Skipping FITS tests, since fitsio is not installed')
        return

    # Check that running via the corr3 script works correctly.
    config = treecorr.config.read_config('configs/ggg_direct_spherical.yaml')
    cat.write(config['file_name'])
    treecorr.corr3(config)
    data = fitsio.read(config['ggg_file_name'])
    np.testing.assert_allclose(data['r_nom'], ggg.rnom.flatten())
    np.testing.assert_allclose(data['u_nom'], ggg.u.flatten())
    np.testing.assert_allclose(data['v_nom'], ggg.v.flatten())
    np.testing.assert_allclose(data['ntri'], ggg.ntri.flatten())
    np.testing.assert_allclose(data['weight'], ggg.weight.flatten())
    np.testing.assert_allclose(data['gam0r'], ggg.gam0r.flatten(), rtol=1.e-3)
    np.testing.assert_allclose(data['gam0i'], ggg.gam0i.flatten(), rtol=1.e-3)
    np.testing.assert_allclose(data['gam1r'], ggg.gam1r.flatten(), rtol=1.e-3)
    np.testing.assert_allclose(data['gam1i'], ggg.gam1i.flatten(), rtol=1.e-3)
    np.testing.assert_allclose(data['gam2r'], ggg.gam2r.flatten(), rtol=1.e-3)
    np.testing.assert_allclose(data['gam2i'], ggg.gam2i.flatten(), rtol=1.e-3)
    np.testing.assert_allclose(data['gam3r'], ggg.gam3r.flatten(), rtol=1.e-3)
    np.testing.assert_allclose(data['gam3i'], ggg.gam3i.flatten(), rtol=1.e-3)

    # Repeat with binslop = 0
    # And don't do any top-level recursion so we actually test not going to the leaves.
    ggg = treecorr.GGGCorrelation(min_sep=min_sep, bin_size=bin_size, nbins=nrbins,
                                  sep_units='deg', bin_slop=0, max_top=0)
    ggg.process(cat)
    np.testing.assert_array_equal(ggg.ntri, true_ntri)
    np.testing.assert_allclose(ggg.weight, true_weight, rtol=1.e-5, atol=1.e-8)
    np.testing.assert_allclose(ggg.gam0r, true_gam0.real, rtol=1.e-5, atol=1.e-8)
    np.testing.assert_allclose(ggg.gam0i, true_gam0.imag, rtol=1.e-5, atol=1.e-4)
    np.testing.assert_allclose(ggg.gam1r, true_gam1.real, rtol=1.e-3, atol=1.e-4)
    np.testing.assert_allclose(ggg.gam1i, true_gam1.imag, rtol=1.e-3, atol=1.e-4)
    np.testing.assert_allclose(ggg.gam2r, true_gam2.real, rtol=1.e-3, atol=1.e-4)
    np.testing.assert_allclose(ggg.gam2i, true_gam2.imag, rtol=1.e-3, atol=1.e-4)
    np.testing.assert_allclose(ggg.gam3r, true_gam3.real, rtol=1.e-3, atol=1.e-4)
    np.testing.assert_allclose(ggg.gam3i, true_gam3.imag, rtol=1.e-3, atol=1.e-4)
示例#8
0
def test_direct_spherical():
    # Repeat in spherical coords

    ngal = 100
    s = 10.
    rng = np.random.RandomState(8675309)
    x1 = rng.normal(0,s, (ngal,) )
    y1 = rng.normal(0,s, (ngal,) ) + 200  # Put everything at large y, so small angle on sky
    z1 = rng.normal(0,s, (ngal,) )
    w1 = rng.random_sample(ngal)
    k1 = rng.normal(5,1, (ngal,) )

    x2 = rng.normal(0,s, (ngal,) )
    y2 = rng.normal(0,s, (ngal,) ) + 200
    z2 = rng.normal(0,s, (ngal,) )
    w2 = rng.random_sample(ngal)
    g12 = rng.normal(0,0.2, (ngal,) )
    g22 = rng.normal(0,0.2, (ngal,) )

    ra1, dec1 = coord.CelestialCoord.xyz_to_radec(x1,y1,z1)
    ra2, dec2 = coord.CelestialCoord.xyz_to_radec(x2,y2,z2)

    cat1 = treecorr.Catalog(ra=ra1, dec=dec1, ra_units='rad', dec_units='rad', w=w1, k=k1)
    cat2 = treecorr.Catalog(ra=ra2, dec=dec2, ra_units='rad', dec_units='rad', w=w2, g1=g12, g2=g22)

    min_sep = 1.
    max_sep = 10.
    nbins = 50
    bin_size = np.log(max_sep/min_sep) / nbins
    kg = treecorr.KGCorrelation(min_sep=min_sep, max_sep=max_sep, nbins=nbins,
                                sep_units='deg', brute=True)
    kg.process(cat1, cat2)

    r1 = np.sqrt(x1**2 + y1**2 + z1**2)
    r2 = np.sqrt(x2**2 + y2**2 + z2**2)
    x1 /= r1;  y1 /= r1;  z1 /= r1
    x2 /= r2;  y2 /= r2;  z2 /= r2

    north_pole = coord.CelestialCoord(0*coord.radians, 90*coord.degrees)

    true_npairs = np.zeros(nbins, dtype=int)
    true_weight = np.zeros(nbins, dtype=float)
    true_xi = np.zeros(nbins, dtype=complex)

    c1 = [coord.CelestialCoord(r*coord.radians, d*coord.radians) for (r,d) in zip(ra1, dec1)]
    c2 = [coord.CelestialCoord(r*coord.radians, d*coord.radians) for (r,d) in zip(ra2, dec2)]
    for i in range(ngal):
        for j in range(ngal):
            rsq = (x1[i]-x2[j])**2 + (y1[i]-y2[j])**2 + (z1[i]-z2[j])**2
            r = np.sqrt(rsq)
            r *= coord.radians / coord.degrees
            logr = np.log(r)

            index = np.floor(np.log(r/min_sep) / bin_size).astype(int)
            if index < 0 or index >= nbins:
                continue

            # Rotate shears to coordinates where line connecting is horizontal.
            # Original orientation is where north is up.
            theta2 = 90*coord.degrees - c2[j].angleBetween(c1[i], north_pole)
            expm2theta2 = np.cos(2*theta2) - 1j * np.sin(2*theta2)

            g2 = g12[j] + 1j * g22[j]
            g2 *= expm2theta2

            ww = w1[i] * w2[j]
            xi = -ww * k1[i] * g2

            true_npairs[index] += 1
            true_weight[index] += ww
            true_xi[index] += xi

    true_xi /= true_weight

    print('true_npairs = ',true_npairs)
    print('diff = ',kg.npairs - true_npairs)
    np.testing.assert_array_equal(kg.npairs, true_npairs)

    print('true_weight = ',true_weight)
    print('diff = ',kg.weight - true_weight)
    np.testing.assert_allclose(kg.weight, true_weight, rtol=1.e-5, atol=1.e-8)

    print('true_xi = ',true_xi)
    print('kg.xi = ',kg.xi)
    np.testing.assert_allclose(kg.xi, true_xi.real, rtol=1.e-4, atol=1.e-8)
    np.testing.assert_allclose(kg.xi_im, true_xi.imag, rtol=1.e-4, atol=1.e-8)

    try:
        import fitsio
    except ImportError:
        print('Skipping FITS tests, since fitsio is not installed')
        return

    # Check that running via the corr2 script works correctly.
    config = treecorr.config.read_config('configs/kg_direct_spherical.yaml')
    cat1.write(config['file_name'])
    cat2.write(config['file_name2'])
    treecorr.corr2(config)
    data = fitsio.read(config['kg_file_name'])
    np.testing.assert_allclose(data['r_nom'], kg.rnom)
    np.testing.assert_allclose(data['npairs'], kg.npairs)
    np.testing.assert_allclose(data['weight'], kg.weight)
    np.testing.assert_allclose(data['kgamT'], kg.xi, rtol=1.e-3)
    np.testing.assert_allclose(data['kgamX'], kg.xi_im, rtol=1.e-3)

    # Repeat with binslop = 0
    # And don't do any top-level recursion so we actually test not going to the leaves.
    kg = treecorr.KGCorrelation(min_sep=min_sep, max_sep=max_sep, nbins=nbins,
                                sep_units='deg', bin_slop=0, max_top=0)
    kg.process(cat1, cat2)
    np.testing.assert_array_equal(kg.npairs, true_npairs)
    np.testing.assert_allclose(kg.weight, true_weight, rtol=1.e-5, atol=1.e-8)
    np.testing.assert_allclose(kg.xi, true_xi.real, rtol=1.e-3, atol=1.e-3)
    np.testing.assert_allclose(kg.xi_im, true_xi.imag, rtol=1.e-3, atol=1.e-3)
示例#9
0
def parse_xyzsep(args, kwargs, _coords):
    """Parse the different options for passing a coordinate and separation.

    The allowed parameters are:

    1. If _coords == Flat:

        :param x:       The x coordinate of the location for which to count nearby points.
        :param y:       The y coordinate of the location for which to count nearby points.
        :param sep:     The separation distance

    2. If _coords == ThreeD:

    Either
        :param x:       The x coordinate of the location for which to count nearby points.
        :param y:       The y coordinate of the location for which to count nearby points.
        :param z:       The z coordinate of the location for which to count nearby points.
        :param sep:     The separation distance

    Or
        :param ra:      The right ascension of the location for which to count nearby points.
        :param dec:     The declination of the location for which to count nearby points.
        :param r:       The distance to the location for which to count nearby points.
        :param sep:     The separation distance

    3. If _coords == Sphere:

        :param ra:      The right ascension of the location for which to count nearby points.
        :param dec:     The declination of the location for which to count nearby points.
        :param sep:     The separation distance as an angle

    For all angle parameters (ra, dec, sep), this quantity may be a coord.Angle instance, or
    units maybe be provided as ra_units, dec_units or sep_units respectively.

    Finally, in cases where ra, dec are allowed, a coord.CelestialCoord instance may be
    provided as the first argument.

    :returns: The effective (x, y, z, sep) as a tuple.
    """
    radec = False
    if _coords == treecorr._lib.Flat:
        if len(args) == 0:
            if 'x' not in kwargs:
                raise TypeError("Missing required argument x")
            if 'y' not in kwargs:
                raise TypeError("Missing required argument y")
            if 'sep' not in kwargs:
                raise TypeError("Missing required argument sep")
            x = kwargs.pop('x')
            y = kwargs.pop('y')
            sep = kwargs.pop('sep')
        elif len(args) == 1:
            raise TypeError(
                "x,y should be given as either args or kwargs, not mixed.")
        elif len(args) == 2:
            if 'sep' not in kwargs:
                raise TypeError("Missing required argument sep")
            x, y = args
            sep = kwargs.pop('sep')
        elif len(args) == 3:
            x, y, sep = args
        else:
            raise TypeError("Too many positional args")
        z = 0

    elif _coords == treecorr._lib.ThreeD:
        if len(args) == 0:
            if 'x' in kwargs:
                if 'y' not in kwargs:
                    raise TypeError("Missing required argument y")
                if 'z' not in kwargs:
                    raise TypeError("Missing required argument z")
                x = kwargs.pop('x')
                y = kwargs.pop('y')
                z = kwargs.pop('z')
            else:
                if 'ra' not in kwargs:
                    raise TypeError("Missing required argument ra")
                if 'dec' not in kwargs:
                    raise TypeError("Missing required argument dec")
                ra = kwargs.pop('ra')
                dec = kwargs.pop('dec')
                radec = True
                if 'r' not in kwargs:
                    raise TypeError("Missing required argument r")
                r = kwargs.pop('r')
            if 'sep' not in kwargs:
                raise TypeError("Missing required argument sep")
            sep = kwargs.pop('sep')
        elif len(args) == 1:
            if not isinstance(args[0], coord.CelestialCoord):
                raise TypeError("Invalid unnamed argument %r" % args[0])
            ra = args[0].ra
            dec = args[0].dec
            radec = True
            if 'r' not in kwargs:
                raise TypeError("Missing required argument r")
            r = kwargs.pop('r')
            if 'sep' not in kwargs:
                raise TypeError("Missing required argument sep")
            sep = kwargs.pop('sep')
        elif len(args) == 2:
            if isinstance(args[0], coord.CelestialCoord):
                ra = args[0].ra
                dec = args[0].dec
                radec = True
                r = args[1]
            else:
                ra, dec = args
                radec = True
                if 'r' not in kwargs:
                    raise TypeError("Missing required argument r")
                r = kwargs.pop('r')
            if 'sep' not in kwargs:
                raise TypeError("Missing required argument sep")
            sep = kwargs.pop('sep')
        elif len(args) == 3:
            if isinstance(args[0], coord.CelestialCoord):
                ra = args[0].ra
                dec = args[0].dec
                radec = True
                r = args[1]
                sep = args[2]
            elif isinstance(args[0], coord.Angle):
                ra, dec, r = args
                radec = True
                if 'sep' not in kwargs:
                    raise TypeError("Missing required argument sep")
                sep = kwargs.pop('sep')
            elif 'ra_units' in kwargs or 'dec_units' in kwargs:
                ra, dec, r = args
                radec = True
                if 'sep' not in kwargs:
                    raise TypeError("Missing required argument sep")
                sep = kwargs.pop('sep')
            else:
                x, y, z = args
                if 'sep' not in kwargs:
                    raise TypeError("Missing required argument sep")
                sep = kwargs.pop('sep')
        elif len(args) == 4:
            if isinstance(args[0], coord.Angle):
                ra, dec, r, sep = args
                radec = True
            elif 'ra_units' in kwargs or 'dec_units' in kwargs:
                ra, dec, r, sep = args
                radec = True
            else:
                x, y, z, sep = args
        else:
            raise TypeError("Too many positional args")

    else:  # Sphere
        if len(args) == 0:
            if 'ra' not in kwargs:
                raise TypeError("Missing required argument ra")
            if 'dec' not in kwargs:
                raise TypeError("Missing required argument dec")
            ra = kwargs.pop('ra')
            dec = kwargs.pop('dec')
            radec = True
            if 'sep' not in kwargs:
                raise TypeError("Missing required argument sep")
            sep = kwargs.pop('sep')
        elif len(args) == 1:
            if not isinstance(args[0], coord.CelestialCoord):
                raise TypeError("Invalid unnamed argument %r" % args[0])
            ra = args[0].ra
            dec = args[0].dec
            radec = True
            if 'sep' not in kwargs:
                raise TypeError("Missing required argument sep")
            sep = kwargs.pop('sep')
        elif len(args) == 2:
            if isinstance(args[0], coord.CelestialCoord):
                ra = args[0].ra
                dec = args[0].dec
                radec = True
                sep = args[1]
            else:
                ra, dec = args
                radec = True
                if 'sep' not in kwargs:
                    raise TypeError("Missing required argument sep")
                sep = kwargs.pop('sep')
        elif len(args) == 3:
            ra, dec, sep = args
            radec = True
        else:
            raise TypeError("Too many positional args")
        if not isinstance(sep, coord.Angle):
            if 'sep_units' not in kwargs:
                raise TypeError("Missing required argument sep_units")
            sep = sep * coord.AngleUnit.from_name(kwargs.pop('sep_units'))
        # We actually want the chord distance for this angle.
        sep = 2. * np.sin(sep / 2.)

    if radec:
        if not isinstance(ra, coord.Angle):
            if 'ra_units' not in kwargs:
                raise TypeError("Missing required argument ra_units")
            ra = ra * coord.AngleUnit.from_name(kwargs.pop('ra_units'))
        if not isinstance(dec, coord.Angle):
            if 'dec_units' not in kwargs:
                raise TypeError("Missing required argument dec_units")
            dec = dec * coord.AngleUnit.from_name(kwargs.pop('dec_units'))
        x, y, z = coord.CelestialCoord(ra, dec).get_xyz()
        if _coords == treecorr._lib.ThreeD:
            x *= r
            y *= r
            z *= r
    if len(kwargs) > 0:
        raise TypeError("Invalid kwargs: %s" % (kwargs))

    return float(x), float(y), float(z), float(sep)
示例#10
0
def test_sample_pairs():

    nobj = 10000
    rng = np.random.RandomState(8675309)
    x1 = rng.random_sample(nobj)   # All from 0..1
    y1 = rng.random_sample(nobj)
    z1 = rng.random_sample(nobj)
    w1 = rng.random_sample(nobj)
    use = rng.randint(30, size=nobj).astype(float)
    w1[use == 0] = 0
    g11 = rng.random_sample(nobj)
    g21 = rng.random_sample(nobj)
    k1 = rng.random_sample(nobj)

    x2 = rng.random_sample(nobj)   # All from 0..1
    y2 = rng.random_sample(nobj)
    z2 = rng.random_sample(nobj)
    w2 = rng.random_sample(nobj)
    use = rng.randint(30, size=nobj).astype(float)
    w2[use == 0] = 0
    g12 = rng.random_sample(nobj)
    g22 = rng.random_sample(nobj)
    k2 = rng.random_sample(nobj)

    # Start with flat coords

    cat1 = treecorr.Catalog(x=x1, y=y1, w=w1, g1=g11, g2=g21, k=k1, keep_zero_weight=True)
    cat2 = treecorr.Catalog(x=x2, y=y2, w=w2, g1=g12, g2=g22, k=k2, keep_zero_weight=True)

    # Note: extend range low enough that some bins have < 100 pairs.
    nn = treecorr.NNCorrelation(min_sep=0.001, max_sep=0.01, bin_size=0.1, max_top=0)
    nn.process(cat1, cat2)
    print('rnom = ',nn.rnom)
    print('npairs = ',nn.npairs.astype(int))

    # Start with a bin near the bottom with < 100 pairs
    # This only exercises case 1 in the sampleFrom function.
    b = 1
    i1, i2, sep = nn.sample_pairs(100, cat1, cat2,
                                  min_sep=nn.left_edges[b], max_sep=nn.right_edges[b])

    print('i1 = ',i1)
    print('i2 = ',i2)
    print('sep = ',sep)
    assert nn.npairs[b] <= 100  # i.e. make sure these next tests are what we want to do.
    assert len(i1) == nn.npairs[b]
    assert len(i2) == nn.npairs[b]
    assert len(sep) == nn.npairs[b]
    actual_sep = ((x1[i1]-x2[i2])**2 + (y1[i1]-y2[i2])**2)**0.5
    np.testing.assert_allclose(sep, actual_sep, rtol=0.1)  # half bin size with slop.
    np.testing.assert_array_less(sep, nn.right_edges[b])
    np.testing.assert_array_less(nn.left_edges[b], sep)

    # Next one that still isn't too many pairs, but more than 100
    # This exercises cases 1,2 in the sampleFrom function.
    b = 10
    i1, i2, sep = nn.sample_pairs(100, cat1, cat2,
                                  min_sep=nn.left_edges[b], max_sep=nn.right_edges[b])

    print('i1 = ',i1)
    print('i2 = ',i2)
    print('sep = ',sep)
    assert nn.npairs[b] > 100
    assert len(i1) == 100
    assert len(i2) == 100
    assert len(sep) == 100
    actual_sep = ((x1[i1]-x2[i2])**2 + (y1[i1]-y2[i2])**2)**0.5
    np.testing.assert_allclose(sep, actual_sep, rtol=0.1)
    np.testing.assert_array_less(sep, nn.right_edges[b])
    np.testing.assert_array_less(nn.left_edges[b], sep)

    # To exercise case 3, we need to go to larger separations, so the recursion
    # more often stops before getting to the leaves.
    # Also switch to 3d coordinates.

    cat1 = treecorr.Catalog(x=x1, y=y1, z=z1, w=w1, g1=g11, g2=g21, k=k1, keep_zero_weight=True)
    cat2 = treecorr.Catalog(x=x2, y=y2, z=z2, w=w2, g1=g12, g2=g22, k=k2, keep_zero_weight=True)

    gg = treecorr.GGCorrelation(min_sep=0.4, nbins=10, bin_size=0.1, max_top=0)
    gg.process(cat1, cat2)
    print('rnom = ',gg.rnom)
    print('npairs = ',gg.npairs.astype(int))
    for b in [0,5]:
        i1, i2, sep = gg.sample_pairs(100, cat1, cat2,
                                      min_sep=gg.left_edges[b], max_sep=gg.right_edges[b])

        print('len(npairs) = ',len(gg.npairs))
        print('npairs = ',gg.npairs)
        print('i1 = ',i1)
        print('i2 = ',i2)
        print('sep = ',sep)
        assert len(i1) == 100
        assert len(i2) == 100
        assert len(sep) == 100
        actual_sep = ((x1[i1]-x2[i2])**2 + (y1[i1]-y2[i2])**2 + (z1[i1]-z2[i2])**2)**0.5
        np.testing.assert_allclose(sep, actual_sep, rtol=0.2)
        np.testing.assert_array_less(sep, gg.right_edges[b])
        np.testing.assert_array_less(gg.left_edges[b], sep)

    # Check a different metric.
    # Also ability to generate the field automatically.
    cat1.clear_cache()  # Clears the previously made cat1.field
    cat2.clear_cache()  # and cat2.field

    b = 3
    with CaptureLog() as cl:
        nk = treecorr.NKCorrelation(min_sep=0.4, max_sep=1.0, bin_size=0.1, max_top=0,
                                    logger=cl.logger)
        i1, i2, sep = nk.sample_pairs(100, cat1, cat2, metric='Arc',
                                      min_sep=nk.left_edges[b], max_sep=nk.right_edges[b])
    print(cl.output)
    nk.process(cat1, cat2, metric='Arc')
    print('len(npairs) = ',len(nk.npairs))
    print('npairs = ',nk.npairs)
    assert "Sampled %d pairs out of a total of %d"%(100, nk.npairs[b]) in cl.output
    print('i1 = ',i1)
    print('i2 = ',i2)
    print('sep = ',sep)
    assert len(i1) == 100
    assert len(i2) == 100
    assert len(sep) == 100
    r1 = (x1**2 + y1**2 + z1**2)**0.5
    r2 = (x2**2 + y2**2 + z2**2)**0.5
    xx1 = x1/r1
    yy1 = y1/r1
    zz1 = z1/r1
    xx2 = x2/r2
    yy2 = y2/r2
    zz2 = z2/r2
    chord_sep = ((xx1[i1]-xx2[i2])**2 + (yy1[i1]-yy2[i2])**2 + (zz1[i1]-zz2[i2])**2)**0.5
    arc_sep = np.arcsin(chord_sep/2.)*2.
    print('arc_sep = ',arc_sep)
    np.testing.assert_allclose(sep, arc_sep, rtol=0.1)
    np.testing.assert_array_less(sep, nk.right_edges[b])
    np.testing.assert_array_less(nk.left_edges[b], sep)

    # Finally, check spherical coords with non-default units.
    ra1, dec1 = coord.CelestialCoord.xyz_to_radec(x1,y1,z1)
    ra2, dec2 = coord.CelestialCoord.xyz_to_radec(x2,y2,z2)
    cat1 = treecorr.Catalog(ra=ra1, dec=dec1, ra_units='rad', dec_units='rad')
    cat2 = treecorr.Catalog(ra=ra2, dec=dec2, ra_units='rad', dec_units='rad')

    nn = treecorr.NNCorrelation(min_sep=1., max_sep=60., nbins=50, sep_units='deg', metric='Arc')
    nn.process(cat1, cat2)
    print('rnom = ',nn.rnom)
    print('npairs = ',nn.npairs.astype(int))

    b = 5
    n = 50
    i1, i2, sep = nn.sample_pairs(n, cat1, cat2,
                                  min_sep=nn.left_edges[b], max_sep=nn.right_edges[b])

    print('i1 = ',i1)
    print('i2 = ',i2)
    print('sep = ',sep)
    assert nn.npairs[b] > n
    assert len(i1) == n
    assert len(i2) == n
    assert len(sep) == n

    c1 = [coord.CelestialCoord(r*coord.radians, d*coord.radians) for (r,d) in zip(ra1,dec1)]
    c2 = [coord.CelestialCoord(r*coord.radians, d*coord.radians) for (r,d) in zip(ra2,dec2)]
    actual_sep = np.array([c1[i1[k]].distanceTo(c2[i2[k]]) / coord.degrees for k in range(n)])
    print('actual_sep = ',actual_sep)
    np.testing.assert_allclose(sep, actual_sep, rtol=0.1)
    np.testing.assert_array_less(sep, nn.right_edges[b])
    np.testing.assert_array_less(nn.left_edges[b], sep)
示例#11
0
def getWCS(world_pos, PA=None, date=None, SCAs=None, PA_is_FPA=False):
    """
    This routine returns a dict containing a WCS for each of the WFIRST SCAs (Sensor Chip Array, the
    equivalent of a chip in an optical CCD).  The WFIRST SCAs are labeled 1-18, so these numbers are
    used as the keys in the dict.  Alternatively the user can request a subset of the SCAs using the
    ``SCAs`` option.  The basic instrument parameters used to create the WCS correspond to those in
    Cycle 6, which includes some significant updates from Cycle 5, including a 90 degree rotation of
    the focal plane axes relative to the payload axes, and two rows of SCAs are swapped.

    The user must specify a position for observation, at which the center of the focal plane array
    will point.  This must be supplied as a CelestialCoord ``world_pos``.  In general, only certain
    positions are observable on certain dates, and for a given position there is an optimal position
    angle for the observatory (with the solar panels pointed as directly towards the sun as
    possible).  Users who are knowledgable about these details may choose to supply a position angle
    as ``PA``, either for the observatory or for the focal plane (using ``PA_is_FPA`` to indicate
    this).  But otherwise, the routine will simply choose the optimal position angle for a given
    date.

    To fully understand all possible inputs and outputs to this routine, users may wish to consult
    the diagram on the GalSim wiki,
    https://github.com/GalSim-developers/GalSim/wiki/GalSim-WFIRST-module-diagrams

    Parameters:
        world_pos:      A `galsim.CelestialCoord` indicating the position to observe at the center
                        of the focal plane array (FPA).  Note that if the given position is not
                        observable on the given date, then the routine will raise an exception.
        PA:             A `galsim.Angle` representing the position angle of the observatory +Y
                        axis, unless ``PA_is_FPA=True``, in which case it's the position angle of
                        the FPA.  For users to do not care about this, then leaving this as None
                        will result in the routine using the supplied ``date`` and ``world_pos`` to
                        select the optimal orientation for the observatory.  Note that if a user
                        supplies a ``PA`` value, the routine does not check whether this orientation
                        is actually allowed.  [default: None]
        date:           The date of the observation, as a python datetime object.  If None, then the
                        vernal equinox in 2025 will be used.  [default: None]
        PA_is_FPA:      If True, then the position angle that was provided was the PA of the focal
                        plane array, not the observatory. [default: False]
        SCAs:           A single number or iterable giving the SCAs for which the WCS should be
                        obtained.  If None, then the WCS is calculated for all SCAs.
                        [default: None]

    Returns:
        A dict of WCS objects for each SCA.
    """
    from .. import GSFitsWCS, FitsHeader

    # First just parse the input quantities.
    date, SCAs, pa_fpa, pa_obsy = _parse_WCS_inputs(world_pos, PA, date,
                                                    PA_is_FPA, SCAs)

    # Further gory details on coordinate systems, for developers: Observatory coordinate system is
    # defined such that +X_obs points along the boresight into the sky, +Z_obs points towards the
    # Sun in the absence of a roll offset (i.e., roll offset = 0 defines the optimal position angle
    # for the observatory), +Y_obs makes a right-handed system.
    #
    # Payload coordinate system: +X_pl points along -Y_obs, +Y_pl points along +Z_obs, +Z_pl points
    # along -X_obs (back towards observer).
    #
    # Wide field imager (WFI) focal plane assembly (FPA) coordinate system: This is defined by a
    # left-handed system f1, f2, that is rotated by an angle `theta_fpa` with respect to the payload
    # axes.  +f1 points along the long axis of the focal plane, transverse to the radius from the
    # telescope optic axis.  +f2 points radially out from the telescope optic axis, along the narrow
    # dimension of the focal plane.  If +f2 points North, then +f1 points East.  `theta_fpa` is a
    # positive CCW rotation of the f2 axis relative to -Y_pl, and of f1 relative to +X_pl.  In terms
    # of focal plane geometry, if +Y_fp is pointing North, then SCAs 3 and 12 will be at highest
    # declination, 8 and 17 at the lowest.  +Y_fp is aligned with the short axis of the focal plane
    # array.
    #
    # There is also a detector coordinate system (P1, P2).  +P1 and +P2 point along the fast- and
    # slow-scan directions of the pixel readout, respectively.
    #
    # So, for reference, if the boresight is pointed at RA=90, DEC=0 on March 21st (Sun at vernal
    # equinox), then +X_obs points at (RA,DEC)=(90,0), +Y_obs points North, and +Z_obs points at the
    # Sun.  The payload coordinates are +X_pl points South, -Y_pl points East.  Finally, the FPA
    # coordinate system is defined by +f2 being at a position angle 90+theta_fpa east of North.  If
    # the observatory +Y axis is at a position angle `pa_obsy` East of North, then the focal plane
    # (+f2) is at a position angle pa_fpa = pa_obsy + 90 + theta_fpa.

    # Figure out tangent-plane positions for FPA center:
    # Distortion function is zero there (so we could've passed this through _det_to_tangplane
    # routine, but we do not need to)
    xc_fpa_tp, yc_fpa_tp = xc_fpa, yc_fpa

    # Note, this routine reads in the coeffs.  We don't use them until later, but read them in for
    # all SCAs at once.
    a_sip, b_sip = _parse_sip_file(sip_filename)

    # Loop over SCAs:
    wcs_dict = {}
    for i_sca in SCAs:
        # Set up the header.
        header = []
        # Populate some necessary variables in the FITS header that are always the same, regardless of
        # input and SCA number.
        _populate_required_fields(header)

        # And populate some things that just depend on the overall locations or other input, not on
        # the SCA.
        header.extend([
            ('RA_TARG', world_pos.ra / coord.degrees,
             "right ascension of the target (deg) (J2000)"),
            ('DEC_TARG', world_pos.dec / coord.degrees,
             "declination of the target (deg) (J2000)"),
            ('PA_OBSY', pa_obsy / coord.degrees,
             "position angle of observatory Y axis (deg)"),
            ('PA_FPA', pa_fpa / coord.degrees,
             "position angle of FPA Y axis (deg)"),
            ('SCA_NUM', i_sca, "SCA number (1 - 18)"),
        ])

        # Leave phi_p at 180 (0 if dec_targ==-90), so that tangent plane axes remain oriented along
        # celestial coordinates. In other words, phi_p is the angle of the +Y axis in the tangent
        # plane, which is of course pi if we're measuring these phi angles clockwise from the -Y
        # axis.  Note that this quantity is not used in any calculations at all, but for consistency
        # with the WCS code that comes from the WFIRST project office, we calculate this quantity
        # and put it in the FITS header.
        if world_pos.dec / coord.degrees > -90.:
            phi_p = np.pi * coord.radians
        else:
            phi_p = 0. * coord.radians

        # Get position of SCA center given the center of the FPA and the orientation angle of the
        # focal plane.
        crval, u, v = _get_sca_center_pos(i_sca, world_pos, pa_fpa)

        # Compute the position angle of the local pixel Y axis.
        # This requires projecting local North onto the detector axes.
        # Start by adding any SCA-unique rotation relative to FPA axes:
        sca_tp_rot = pa_fpa + sca_rot[i_sca] * coord.degrees

        # Go some reasonable distance from crval in the +y direction.  Say, 1 degree.
        plus_y = world_pos.deproject(u,
                                     v + 1 * coord.degrees,
                                     projection='gnomonic')
        # Find the angle between this point, crval and due north.
        north = coord.CelestialCoord(0. * coord.degrees, 90. * coord.degrees)
        pa_sca = sca_tp_rot - crval.angleBetween(plus_y, north)

        # Compute CD coefficients: extract the linear terms from the a_sip, b_sip arrays.  These
        # linear terms are stored in the SIP arrays for convenience, but are defined differently.
        # The other terms have been divided by the linear terms, so that these become pure
        # multiplicative factors. There is no need to change signs of the SIP coefficents associated
        # with odd powers of X! Change sign of a10, b10 because the tangent-plane X pixel coordinate
        # has sign opposite to the detector pixel X coordinate, and this transformation maps pixels
        # to tangent plane.
        a10 = -a_sip[i_sca, 1, 0]
        a11 = a_sip[i_sca, 0, 1]
        b10 = -b_sip[i_sca, 1, 0]
        b11 = b_sip[i_sca, 0, 1]

        # Rotate by pa_fpa.
        cos_pa_sca = np.cos(pa_sca)
        sin_pa_sca = np.sin(pa_sca)

        header.extend([
            ('CRVAL1', crval.ra / coord.degrees,
             "first axis value at reference pixel"),
            ('CRVAL2', crval.dec / coord.degrees,
             "second axis value at reference pixel"),
            ('CD1_1', cos_pa_sca * a10 + sin_pa_sca * b10,
             "partial of first axis coordinate w.r.t. x"),
            ('CD1_2', cos_pa_sca * a11 + sin_pa_sca * b11,
             "partial of first axis coordinate w.r.t. y"),
            ('CD2_1', -sin_pa_sca * a10 + cos_pa_sca * b10,
             "partial of second axis coordinate w.r.t. x"),
            ('CD2_2', -sin_pa_sca * a11 + cos_pa_sca * b11,
             "partial of second axis coordinate w.r.t. y"),
            ('ORIENTAT', pa_sca / coord.degrees,
             "position angle of image y axis (deg. e of n)"),
            ('LONPOLE', phi_p / coord.degrees,
             "Native longitude of celestial pole"),
        ])
        for i in range(n_sip):
            for j in range(n_sip):
                if i + j >= 2 and i + j < n_sip:
                    sipstr = "A_%d_%d" % (i, j)
                    header.append((sipstr, a_sip[i_sca, i, j]))
                    sipstr = "B_%d_%d" % (i, j)
                    header.append((sipstr, b_sip[i_sca, i, j]))

        header = FitsHeader(header)
        wcs = GSFitsWCS(header=header)
        # Store the original header as an attribute of the WCS.  This ensures that we have all the
        # extra keywords for whenever an image with this WCS is written to file.
        wcs.header = header
        wcs_dict[i_sca] = wcs

    return wcs_dict
示例#12
0
def convertCenter(world_pos,
                  SCA,
                  PA=None,
                  date=None,
                  PA_is_FPA=False,
                  tol=0.5 * coord.arcsec):
    """
    This is a simple helper routine that takes an input position ``world_pos`` that is meant to
    correspond to the position of the center of an SCA, and tells where the center of the focal
    plane array should be.  The goal is to provide a position that can be used as an input to
    getWCS(), which wants the center of the focal plane array.

    The results of the calculation are deterministic if given a fixed position angle (PA).  If it's
    not given one, it will try to determine the best one for this location and date, like getWCS()
    does.

    Because of distortions varying across the focal plane, this routine has to iteratively correct
    its initial result based on empirical tests.  The ``tol`` kwarg can be used to adjust how
    careful it will be, but it always does at least one iteration.

    To fully understand all possible inputs and outputs to this routine, users may wish to consult
    the diagram on the GalSim wiki,
    https://github.com/GalSim-developers/GalSim/wiki/GalSim-WFIRST-module-diagrams

    Parameters:
        world_pos:  A galsim.CelestialCoord indicating the position to observe at the center of the
                    given SCA.  Note that if the given position is not observable on
                    the given date, then the routine will raise an exception.
        SCA:        A single number giving the SCA for which the center should be located at
                    ``world_pos``.
        PA:         galsim.Angle representing the position angle of the observatory +Y axis, unless
                    ``PA_is_FPA=True``, in which case it's the position angle of the FPA.  For
                    users to do not care about this, then leaving this as None will result in the
                    routine using the supplied ``date`` and ``world_pos`` to select the optimal
                    orientation for the observatory.  Note that if a user supplies a ``PA`` value,
                    the routine does not check whether this orientation is actually allowed.
                    [default: None]
        date:       The date of the observation, as a python datetime object.  If None, then the
                    vernal equinox in 2025 will be used.  [default: None]
        PA_is_FPA:  If True, then the position angle that was provided was the PA of the focal
                    plane array, not the observatory. [default: False]
        tol:        Tolerance for errors due to distortions, as a galsim.Angle.
                    [default: 0.5*galsim.arcsec]

    Returns:
        A CelestialCoord object indicating the center of the focal plane array.
    """
    from .. import PositionD
    from . import n_pix

    if not isinstance(SCA, int):
        raise TypeError("Must pass in an int corresponding to the SCA")
    if not isinstance(tol, coord.Angle):
        raise TypeError("tol must be a galsim.Angle")
    use_SCA = SCA
    # Parse inputs appropriately.
    _, _, pa_fpa, _ = _parse_WCS_inputs(world_pos, PA, date, PA_is_FPA, [SCA])

    # Now pretend world_pos was the FPA center and we want to find the location of this SCA:
    _, u, v = _get_sca_center_pos(use_SCA, world_pos, pa_fpa)
    # The (u, v) values give an offset, and we can invert this.
    fpa_cent = world_pos.deproject(-u, -v, projection='gnomonic')
    # This is only approximately correct, especially for detectors that are far from the center of
    # the FPA, because of distortions etc.  We can do an iterative correction.
    # For the default value of 'tol', typically just 1-2 iterations are needed.
    shift_val = 1000.0  # arcsec
    while shift_val > tol / coord.arcsec:
        test_wcs = getWCS(fpa_cent, PA, date, use_SCA, PA_is_FPA)[use_SCA]
        im_cent_pos = PositionD(n_pix / 2, n_pix / 2)
        test_sca_pos = test_wcs.toWorld(im_cent_pos)
        delta_ra = np.cos(world_pos.dec) * (world_pos.ra - test_sca_pos.ra)
        delta_dec = world_pos.dec - test_sca_pos.dec
        shift_val = np.abs(world_pos.distanceTo(test_sca_pos) / coord.arcsec)
        fpa_cent = coord.CelestialCoord(fpa_cent.ra + delta_ra,
                                        fpa_cent.dec + delta_dec)

    return fpa_cent
示例#13
0
def test_direct_spherical():
    # Repeat in spherical coords

    ngal = 50
    s = 10.
    rng = np.random.RandomState(8675309)
    x = rng.normal(0, s, (ngal, ))
    y = rng.normal(
        0, s,
        (ngal, )) + 200  # Put everything at large y, so small angle on sky
    z = rng.normal(0, s, (ngal, ))
    w = rng.random_sample(ngal)
    kap = rng.normal(0, 3, (ngal, ))
    w = np.ones_like(w)

    ra, dec = coord.CelestialCoord.xyz_to_radec(x, y, z)

    cat = treecorr.Catalog(ra=ra,
                           dec=dec,
                           ra_units='rad',
                           dec_units='rad',
                           w=w,
                           k=kap)

    min_sep = 1.
    bin_size = 0.2
    nrbins = 10
    nubins = 5
    nvbins = 5
    max_sep = min_sep * np.exp(nrbins * bin_size)
    kkk = treecorr.KKKCorrelation(min_sep=min_sep,
                                  bin_size=bin_size,
                                  nbins=nrbins,
                                  sep_units='deg',
                                  brute=True)
    kkk.process(cat)

    r = np.sqrt(x**2 + y**2 + z**2)
    x /= r
    y /= r
    z /= r
    north_pole = coord.CelestialCoord(0 * coord.radians, 90 * coord.degrees)

    true_ntri = np.zeros((nrbins, nubins, 2 * nvbins), dtype=int)
    true_weight = np.zeros((nrbins, nubins, 2 * nvbins), dtype=float)
    true_zeta = np.zeros((nrbins, nubins, 2 * nvbins), dtype=float)

    rad_min_sep = min_sep * coord.degrees / coord.radians
    rad_max_sep = max_sep * coord.degrees / coord.radians
    c = [
        coord.CelestialCoord(r * coord.radians, d * coord.radians)
        for (r, d) in zip(ra, dec)
    ]
    for i in range(ngal):
        for j in range(i + 1, ngal):
            for k in range(j + 1, ngal):
                d12 = np.sqrt((x[i] - x[j])**2 + (y[i] - y[j])**2 +
                              (z[i] - z[j])**2)
                d23 = np.sqrt((x[j] - x[k])**2 + (y[j] - y[k])**2 +
                              (z[j] - z[k])**2)
                d31 = np.sqrt((x[k] - x[i])**2 + (y[k] - y[i])**2 +
                              (z[k] - z[i])**2)

                d3, d2, d1 = sorted([d12, d23, d31])
                rindex = np.floor(np.log(d2 / rad_min_sep) /
                                  bin_size).astype(int)
                if rindex < 0 or rindex >= nrbins: continue

                if [d1, d2, d3] == [d23, d31, d12]: ii, jj, kk = i, j, k
                elif [d1, d2, d3] == [d23, d12, d31]: ii, jj, kk = i, k, j
                elif [d1, d2, d3] == [d31, d12, d23]: ii, jj, kk = j, k, i
                elif [d1, d2, d3] == [d31, d23, d12]: ii, jj, kk = j, i, k
                elif [d1, d2, d3] == [d12, d23, d31]: ii, jj, kk = k, i, j
                elif [d1, d2, d3] == [d12, d31, d23]: ii, jj, kk = k, j, i
                else: assert False
                # Now use ii, jj, kk rather than i,j,k, to get the indices
                # that correspond to the points in the right order.

                u = d3 / d2
                v = (d1 - d2) / d3
                if (((x[jj] - x[ii]) * (y[kk] - y[ii]) - (x[kk] - x[ii]) *
                     (y[jj] - y[ii])) * z[ii] +
                    ((y[jj] - y[ii]) * (z[kk] - z[ii]) - (y[kk] - y[ii]) *
                     (z[jj] - z[ii])) * x[ii] +
                    ((z[jj] - z[ii]) * (x[kk] - x[ii]) - (z[kk] - z[ii]) *
                     (x[jj] - x[ii])) * y[ii]) > 0:
                    v = -v

                uindex = np.floor(u / bin_size).astype(int)
                assert 0 <= uindex < nubins
                vindex = np.floor((v + 1) / bin_size).astype(int)
                assert 0 <= vindex < 2 * nvbins

                www = w[i] * w[j] * w[k]
                zeta = www * kap[i] * kap[j] * kap[k]

                true_ntri[rindex, uindex, vindex] += 1
                true_weight[rindex, uindex, vindex] += www
                true_zeta[rindex, uindex, vindex] += zeta

    pos = true_weight > 0
    true_zeta[pos] /= true_weight[pos]

    np.testing.assert_array_equal(kkk.ntri, true_ntri)
    np.testing.assert_allclose(kkk.weight, true_weight, rtol=1.e-5, atol=1.e-8)
    np.testing.assert_allclose(kkk.zeta, true_zeta, rtol=1.e-4, atol=1.e-6)

    try:
        import fitsio
    except ImportError:
        print('Skipping FITS tests, since fitsio is not installed')
        return

    # Check that running via the corr3 script works correctly.
    config = treecorr.config.read_config('configs/kkk_direct_spherical.yaml')
    cat.write(config['file_name'])
    treecorr.corr3(config)
    data = fitsio.read(config['kkk_file_name'])
    np.testing.assert_allclose(data['r_nom'], kkk.rnom.flatten())
    np.testing.assert_allclose(data['u_nom'], kkk.u.flatten())
    np.testing.assert_allclose(data['v_nom'], kkk.v.flatten())
    np.testing.assert_allclose(data['ntri'], kkk.ntri.flatten())
    np.testing.assert_allclose(data['weight'], kkk.weight.flatten())
    np.testing.assert_allclose(data['zeta'], kkk.zeta.flatten(), rtol=1.e-3)

    # Repeat with binslop = 0
    # And don't do any top-level recursion so we actually test not going to the leaves.
    kkk = treecorr.KKKCorrelation(min_sep=min_sep,
                                  bin_size=bin_size,
                                  nbins=nrbins,
                                  sep_units='deg',
                                  bin_slop=0,
                                  max_top=0)
    kkk.process(cat)
    np.testing.assert_array_equal(kkk.ntri, true_ntri)
    np.testing.assert_allclose(kkk.weight, true_weight, rtol=1.e-5, atol=1.e-8)
    np.testing.assert_allclose(kkk.zeta, true_zeta, rtol=1.e-4, atol=1.e-6)
示例#14
0
文件: test_wcs.py 项目: rmjarvis/Piff
def test_des_wcs():
    """Test the get_nominal_wcs function.
    """
    # Read a random DES image
    image_file = 'input/DECam_00241238_01.fits.fz'
    print('read DES image ', image_file)
    im = galsim.fits.read(image_file, read_header=True)
    print(list(im.header.keys()))
    print('ra = ', im.header['TELRA'])
    print('dec = ', im.header['TELDEC'])
    ra = coord.Angle.from_hms(im.header['TELRA'])
    dec = coord.Angle.from_dms(im.header['TELDEC'])
    pointing = coord.CelestialCoord(ra, dec)

    print('raw wcs = ', im.wcs)
    print('world coord at center = ', im.wcs.toWorld(im.center))
    print('pointing = ', pointing)
    print('dist = ', pointing.distanceTo(im.wcs.toWorld(im.center)).deg)

    # This chip is near the edge, but check that we're at least close to the nominal pointing.
    assert pointing.distanceTo(im.wcs.toWorld(im.center)) < 1.2 * coord.degrees

    # Get the local affine approximation relative to the pointing center
    wcs1 = im.wcs.affine(world_pos=pointing)
    print('wcs1 = ', wcs1)
    print('u,v at image center = ', wcs1.toWorld(im.center))

    # A different approach.  Get the local wcs at the image center, and adjust
    # the origin to the pointing center.
    wcs2 = im.wcs.local(im.center).withOrigin(im.wcs.toImage(pointing))
    print('wcs2 = ', wcs2)
    print('u,v at image center = ', wcs2.toWorld(im.center))

    # Finally, compare with the mock up approximate version we have in piff
    wcs3 = piff.des.DECamInfo().get_nominal_wcs(chipnum=1)
    print('wcs3 = ', wcs3)
    print('u,v at image center = ', wcs3.toWorld(im.center))

    # Check that these are all vaguley similar.
    # wcs2 is probably the most accurate of these, since it Taylor expands the nonlinear
    # stuff at the image center.  wcs1 expands around a point way off the chip, so there
    # are expected to be some errors in this extrapolation.
    # And of course wcs3 is just an approximation, so it's only expected to be good to a
    # few arcsec or so.
    np.testing.assert_allclose(wcs3.jacobian().getMatrix(),
                               wcs2.jacobian().getMatrix(),
                               rtol=0.02,
                               atol=0.003)
    np.testing.assert_allclose(wcs3.toWorld(im.center).x,
                               wcs2.toWorld(im.center).x,
                               rtol=0.02)
    np.testing.assert_allclose(wcs3.toWorld(im.center).y,
                               wcs2.toWorld(im.center).y,
                               rtol=0.02)

    # As mentioned, wcs1 is not as close, but that's ok.
    np.testing.assert_allclose(wcs3.jacobian().getMatrix(),
                               wcs1.jacobian().getMatrix(),
                               rtol=0.04,
                               atol=0.002)
    np.testing.assert_allclose(wcs3.toWorld(im.center).x,
                               wcs1.toWorld(im.center).x,
                               rtol=0.04)
    np.testing.assert_allclose(wcs3.toWorld(im.center).y,
                               wcs1.toWorld(im.center).y,
                               rtol=0.04)
示例#15
0
def test_direct_spherical():
    # Repeat in spherical coords

    ngal = 100
    s = 10.
    rng = np.random.RandomState(8675309)
    x1 = rng.normal(0, s, (ngal, ))
    y1 = rng.normal(
        0, s,
        (ngal, )) + 200  # Put everything at large y, so small angle on sky
    z1 = rng.normal(0, s, (ngal, ))
    w1 = rng.random_sample(ngal)
    k1 = rng.normal(10, 1, (ngal, ))

    x2 = rng.normal(0, s, (ngal, ))
    y2 = rng.normal(0, s, (ngal, )) + 200
    z2 = rng.normal(0, s, (ngal, ))
    w2 = rng.random_sample(ngal)
    k2 = rng.normal(0, 3, (ngal, ))

    ra1, dec1 = coord.CelestialCoord.xyz_to_radec(x1, y1, z1)
    ra2, dec2 = coord.CelestialCoord.xyz_to_radec(x2, y2, z2)

    cat1 = treecorr.Catalog(ra=ra1,
                            dec=dec1,
                            ra_units='rad',
                            dec_units='rad',
                            w=w1,
                            k=k1)
    cat2 = treecorr.Catalog(ra=ra2,
                            dec=dec2,
                            ra_units='rad',
                            dec_units='rad',
                            w=w2,
                            k=k2)

    min_sep = 1.
    max_sep = 10.
    nbins = 50
    bin_size = np.log(max_sep / min_sep) / nbins
    kk = treecorr.KKCorrelation(min_sep=min_sep,
                                max_sep=max_sep,
                                nbins=nbins,
                                sep_units='deg',
                                brute=True)
    kk.process(cat1, cat2)

    r1 = np.sqrt(x1**2 + y1**2 + z1**2)
    r2 = np.sqrt(x2**2 + y2**2 + z2**2)
    x1 /= r1
    y1 /= r1
    z1 /= r1
    x2 /= r2
    y2 /= r2
    z2 /= r2

    north_pole = coord.CelestialCoord(0 * coord.radians, 90 * coord.degrees)

    true_npairs = np.zeros(nbins, dtype=int)
    true_weight = np.zeros(nbins, dtype=float)
    true_xi = np.zeros(nbins, dtype=float)

    c1 = [
        coord.CelestialCoord(r * coord.radians, d * coord.radians)
        for (r, d) in zip(ra1, dec1)
    ]
    c2 = [
        coord.CelestialCoord(r * coord.radians, d * coord.radians)
        for (r, d) in zip(ra2, dec2)
    ]
    for i in range(ngal):
        for j in range(ngal):
            rsq = (x1[i] - x2[j])**2 + (y1[i] - y2[j])**2 + (z1[i] - z2[j])**2
            r = np.sqrt(rsq)
            r *= coord.radians / coord.degrees
            logr = np.log(r)

            index = np.floor(np.log(r / min_sep) / bin_size).astype(int)
            if index < 0 or index >= nbins:
                continue

            ww = w1[i] * w2[j]
            xi = ww * k1[i] * k2[j]

            true_npairs[index] += 1
            true_weight[index] += ww
            true_xi[index] += xi

    true_xi /= true_weight

    print('true_npairs = ', true_npairs)
    print('diff = ', kk.npairs - true_npairs)
    np.testing.assert_array_equal(kk.npairs, true_npairs)

    print('true_weight = ', true_weight)
    print('diff = ', kk.weight - true_weight)
    np.testing.assert_allclose(kk.weight, true_weight, rtol=1.e-5, atol=1.e-8)

    print('true_xi = ', true_xi)
    print('kk.xi = ', kk.xi)
    np.testing.assert_allclose(kk.xi, true_xi, rtol=1.e-4, atol=1.e-8)

    try:
        import fitsio
    except ImportError:
        print('Skipping FITS tests, since fitsio is not installed')
        return

    # Check that running via the corr2 script works correctly.
    config = treecorr.config.read_config('configs/kk_direct_spherical.yaml')
    cat1.write(config['file_name'])
    cat2.write(config['file_name2'])
    treecorr.corr2(config)
    data = fitsio.read(config['kk_file_name'])
    np.testing.assert_allclose(data['r_nom'], kk.rnom)
    np.testing.assert_allclose(data['npairs'], kk.npairs)
    np.testing.assert_allclose(data['weight'], kk.weight)
    np.testing.assert_allclose(data['xi'], kk.xi, rtol=1.e-3)

    # Repeat with binslop = 0
    # And don't do any top-level recursion so we actually test not going to the leaves.
    kk = treecorr.KKCorrelation(min_sep=min_sep,
                                max_sep=max_sep,
                                nbins=nbins,
                                sep_units='deg',
                                bin_slop=0,
                                max_top=0)
    kk.process(cat1, cat2)
    np.testing.assert_array_equal(kk.npairs, true_npairs)
    np.testing.assert_allclose(kk.weight, true_weight, rtol=1.e-5, atol=1.e-8)
    np.testing.assert_allclose(kk.xi, true_xi, rtol=1.e-3, atol=1.e-6)