Example #1
0
def parse_obscuration(config):
    typ = config.pop('type')
    if typ in ['ObscCircle', 'ObscAnnulus', 'ObscRay', 'ObscRectangle']:
        evalstr = "batoid.{}(**config)".format(typ)
        return eval(evalstr)
    if typ == 'ObscNegation':
        original = parse_obscuration(config['original'])
        return batoid.ObscNegation(original)
    if typ in ['ObscUnion', 'ObscIntersection']:
        items = [parse_obscuration(c) for c in config['items']]  # noqa
        evalstr = "batoid.{}(items)".format(typ)
        return eval(evalstr)
    if typ.startswith('Clear'):  # triggers negation
        # put type back into config, but with Clear->Obsc
        config['type'] = typ.replace("Clear", "Obsc")
        return batoid.ObscNegation(parse_obscuration(config))
Example #2
0
def test_prescreen():
    """Add an OPDScreen in front of LSST entrance pupil.  The OPD that comes out
    should be _negative_ the added phase delay by convention.
    """
    lsst = batoid.Optic.fromYaml("LSST_r.yaml")
    wavelength = 620e-9

    z_ref = batoid.zernikeGQ(
        lsst, 0, 0, wavelength, rings=10, reference='chief', jmax=37, eps=0.61
    )
    rng = np.random.default_rng(577)

    for i in range(4, 38):
        amplitude = rng.uniform(0.1, 0.2)
        zern = batoid.Zernike(
            np.array([0]*i+[amplitude])*wavelength,
            R_outer=4.18, R_inner=0.61*4.18
        )

        tel = batoid.CompoundOptic(
            (
                batoid.optic.OPDScreen(
                    batoid.Plane(),
                    zern,
                    name='PS',
                    obscuration=batoid.ObscNegation(batoid.ObscCircle(5.0)),
                    coordSys=lsst.stopSurface.coordSys
                ),
                *lsst.items
            ),
            name='PS0',
            backDist=lsst.backDist,
            pupilSize=lsst.pupilSize,
            inMedium=lsst.inMedium,
            stopSurface=lsst.stopSurface,
            sphereRadius=lsst.sphereRadius,
            pupilObscuration=lsst.pupilObscuration
        )
        do_pickle(tel)

        zGQ = batoid.zernikeGQ(
            tel, 0, 0, wavelength, rings=10, reference='chief', jmax=37, eps=0.61
        )
        zTA = batoid.zernikeTA(
            tel, 0, 0, wavelength, nrad=10, naz=60, reference='chief', jmax=37, eps=0.61
        )

        z_expect = np.zeros_like(zGQ)
        z_expect[i] = -amplitude  # Longer OPL => negative OPD
        np.testing.assert_allclose(
            (zGQ-z_ref)[4:], z_expect[4:],
            rtol=0, atol=5e-4
        )
        # Distortion makes this comparison less precise
        np.testing.assert_allclose(
            zGQ[4:], zTA[4:],
            rtol=0, atol=5e-3
        )
Example #3
0
def test_ne():
    objs = [
        batoid.ObscCircle(1.0),
        batoid.ObscCircle(2.0),
        batoid.ObscCircle(1.0, 0.1, 0.1),
        batoid.ObscAnnulus(0.0, 1.0),
        batoid.ObscAnnulus(0.1, 1.0),
        batoid.ObscAnnulus(0.1, 1.0, 0.1, 0.1),
        batoid.ObscRectangle(1.0, 2.0),
        batoid.ObscRectangle(1.0, 2.0, 0.1, 0.1),
        batoid.ObscRectangle(1.0, 2.0, 0.1, 0.1, 1.0),
        batoid.ObscRay(1.0, 2.0),
        batoid.ObscRay(1.0, 2.0, 0.1, 0.1),
        batoid.ObscNegation(batoid.ObscCircle(1.0)),
        batoid.ObscPolygon([0,1,1,0],[0,0,1,1]),
        batoid.ObscUnion([batoid.ObscCircle(1.0)]),
        batoid.ObscUnion([
            batoid.ObscCircle(1.0),
            batoid.ObscCircle(2.0)
        ]),
        batoid.ObscUnion([
            batoid.ObscCircle(1.0),
            batoid.ObscCircle(2.2)
        ]),
        batoid.ObscUnion([
            batoid.ObscCircle(1.0),
            batoid.ObscCircle(2.2),
            batoid.ObscAnnulus(1.0, 2.0)
        ]),
        batoid.ObscIntersection([batoid.ObscCircle(1.0)]),
        batoid.ObscIntersection([
            batoid.ObscCircle(1.0),
            batoid.ObscCircle(2.0)
        ]),
        batoid.ObscIntersection([
            batoid.ObscCircle(1.0),
            batoid.ObscCircle(2.2)
        ]),
        batoid.ObscIntersection([
            batoid.ObscCircle(1.0),
            batoid.ObscCircle(2.2),
            batoid.ObscAnnulus(1.0, 2.0)
        ]),
    ]
    all_obj_diff(objs)
Example #4
0
def test_ObscNegation():
    rng = np.random.default_rng(577215)
    size = 10_000

    for i in range(100):
        cx = rng.normal(0.0, 1.0)
        cy = rng.normal(0.0, 1.0)
        r = rng.uniform(0.5, 1.5)

        original = batoid.ObscCircle(r, cx, cy)
        obsc = batoid.ObscNegation(original)
        do_pickle(obsc)
        for i in range(100):
            x = rng.normal(0.0, 1.0)
            y = rng.normal(0.0, 1.0)
            assert obsc.contains(x, y) != original.contains(x, y)

        x = rng.normal(0.0, 1.0, size=size)
        y = rng.normal(0.0, 1.0, size=size)
        np.testing.assert_array_equal(
            obsc.contains(x, y),
            ~original.contains(x, y)
        )

        do_pickle(obsc)

        rv = batoid.RayVector(x, y, 0.0, 0.0, 0.0, 0.0)
        batoid.obscure(obsc, rv)
        np.testing.assert_array_equal(
            obsc.contains(x, y),
            rv.vignetted
        )

        # also test config parsing of "ClearCircle"
        config = {'type':'ClearCircle', 'x':cx, 'y':cy, 'radius':r}
        obsc = batoid.parse.parse_obscuration(config)
        do_pickle(obsc)
        for i in range(100):
            x = rng.normal(0.0, 1.0)
            y = rng.normal(0.0, 1.0)
            assert obsc.contains(x, y) == (np.hypot(x-cx, y-cy) > r)
Example #5
0
def test_ObscNegation():
    import random
    random.seed(5772)

    for i in range(100):
        cx = random.gauss(0.0, 1.0)
        cy = random.gauss(0.0, 1.0)
        r = random.uniform(0.5, 1.5)

        obsc = batoid.ObscCircle(r, cx, cy)
        obsc = batoid.ObscNegation(obsc)
        do_pickle(obsc)
        for i in range(100):
            x = random.gauss(0.0, 1.0)
            y = random.gauss(0.0, 1.0)
            assert obsc.contains(x, y) == (np.hypot(x - cx, y - cy) > r)
        # also test config parsing of "ClearCircle"
        config = {'type': 'ClearCircle', 'x': cx, 'y': cy, 'radius': r}
        obsc = batoid.parse.parse_obscuration(config)
        do_pickle(obsc)
        for i in range(100):
            x = random.gauss(0.0, 1.0)
            y = random.gauss(0.0, 1.0)
            assert obsc.contains(x, y) == (np.hypot(x - cx, y - cy) > r)
Example #6
0
def test_zernikeGQ():
    if __name__ == '__main__':
        nx = 1024
        rings = 10
        tol = 1e-4
    else:
        nx = 128
        rings = 5
        tol = 1e-3
    telescope = batoid.Optic.fromYaml("LSST_r.yaml")
    telescope.clearObscuration()
    telescope['LSST.M1'].obscuration = batoid.ObscNegation(
        batoid.ObscCircle(4.18))
    zSquare = batoid.analysis.zernike(telescope,
                                      0.0,
                                      0.0,
                                      625e-9,
                                      nx=nx,
                                      jmax=28,
                                      reference='chief')
    zGQ = batoid.analysis.zernikeGQ(telescope,
                                    0.0,
                                    0.0,
                                    625e-9,
                                    rings=rings,
                                    jmax=28,
                                    reference='chief')

    np.testing.assert_allclose(zSquare, zGQ, rtol=0, atol=tol)

    # Repeat with annular Zernikes
    telescope['LSST.M1'].obscuration = batoid.ObscNegation(
        batoid.ObscAnnulus(0.61 * 4.18, 4.18))
    zSquare = batoid.analysis.zernike(telescope,
                                      0.0,
                                      0.0,
                                      625e-9,
                                      nx=nx,
                                      jmax=28,
                                      reference='chief',
                                      eps=0.61)
    zGQ = batoid.analysis.zernikeGQ(telescope,
                                    0.0,
                                    0.0,
                                    625e-9,
                                    rings=rings,
                                    jmax=28,
                                    reference='chief',
                                    eps=0.61)

    np.testing.assert_allclose(zSquare, zGQ, rtol=0, atol=tol)

    # Try off-axis
    zSquare = batoid.analysis.zernike(telescope,
                                      np.deg2rad(0.2),
                                      np.deg2rad(0.1),
                                      625e-9,
                                      nx=nx,
                                      jmax=28,
                                      reference='chief',
                                      eps=0.61)
    zGQ = batoid.analysis.zernikeGQ(telescope,
                                    np.deg2rad(0.2),
                                    np.deg2rad(0.1),
                                    625e-9,
                                    rings=rings,
                                    jmax=28,
                                    reference='chief',
                                    eps=0.61)

    np.testing.assert_allclose(zSquare, zGQ, rtol=0, atol=tol)
Example #7
0
def test_huygens_paraboloid(plot=False):
    if __name__ == '__main__':
        obscurations = [0.0, 0.25, 0.5, 0.75]
    else:
        obscurations = [0.25]

    print("Testing HuygensPSF")
    # Just do a single parabolic mirror test
    focalLength = 1.5
    diam = 0.3
    R = 2*focalLength
    for obscuration in obscurations:
        telescope = batoid.CompoundOptic(
            items = [
                batoid.Mirror(
                    batoid.Paraboloid(R),
                    name="Mirror",
                    obscuration=batoid.ObscNegation(
                        batoid.ObscAnnulus(0.5*obscuration*diam, 0.5*diam)
                    )
                ),
                batoid.Detector(
                    batoid.Plane(),
                    name="detector",
                    coordSys=batoid.CoordSys(origin=[0, 0, focalLength])
                )
            ],
            pupilSize=diam,
            backDist=10.0,
            inMedium=batoid.ConstMedium(1.0)
        )

        airy_size = 1.22*500e-9/diam * 206265
        print()
        print("Airy radius: {:4.2f} arcsec".format(airy_size))

        # Start with the HuygensPSF
        npix = 96
        size = 3.0 # arcsec
        dsize = size/npix
        dsize_X = dsize*focalLength/206265  # meters

        psf = batoid.huygensPSF(
            telescope, 0.0, 0.0, 500e-9,
            nx=npix, dx=dsize_X, dy=dsize_X
        )
        psf.array /= np.max(psf.array)

        scale = np.sqrt(np.abs(np.linalg.det(psf.primitiveVectors)))  # meters
        scale *= 206265/focalLength  # arcsec
        obj = galsim.Airy(lam=500, diam=diam, obscuration=obscuration)
        # Need to shift by half a pixel.
        obj = obj.shift(scale/2, scale/2)
        im = obj.drawImage(nx=npix, ny=npix, scale=scale, method='no_pixel')
        arr = im.array/np.max(im.array)
        gs_mom = galsim.hsm.FindAdaptiveMom(im)

        psfim = galsim.Image(psf.array)
        jt_mom = galsim.hsm.FindAdaptiveMom(psfim)

        print("GalSim shape: ", gs_mom.observed_shape)
        print("batoid shape: ", jt_mom.observed_shape)
        print("GalSim centroid:  ", gs_mom.moments_centroid)
        print("batoid centroid:  ", jt_mom.moments_centroid)
        print("GalSim size: ", gs_mom.moments_sigma)
        print("batoid size: ", jt_mom.moments_sigma)
        print("GalSim rho4: ", gs_mom.moments_rho4)
        print("batoid rho4: ", jt_mom.moments_rho4)

        np.testing.assert_allclose(
            gs_mom.observed_shape.g1,
            jt_mom.observed_shape.g1,
            rtol=0.0, atol=3e-3
        )
        np.testing.assert_allclose(
            gs_mom.observed_shape.g2,
            jt_mom.observed_shape.g2,
            rtol=0.0, atol=3e-3
        )
        np.testing.assert_allclose(
            gs_mom.moments_centroid.x,
            jt_mom.moments_centroid.x,
            rtol=0.0, atol=1e-9
        )
        np.testing.assert_allclose(
            gs_mom.moments_centroid.y,
            jt_mom.moments_centroid.y,
            rtol=0.0, atol=1e-9
        )
        np.testing.assert_allclose(
            gs_mom.moments_sigma,
            jt_mom.moments_sigma,
            rtol=1e-2  # why not better?!
        )
        np.testing.assert_allclose(
            gs_mom.moments_rho4,
            jt_mom.moments_rho4,
            rtol=2e-2
        )

        if plot:
            size = scale*npix
            import matplotlib.pyplot as plt
            fig = plt.figure(figsize=(15, 4))
            ax1 = fig.add_subplot(131)
            im1 = ax1.imshow(
                np.log10(arr),
                extent=np.r_[-1,1,-1,1]*size/2,
                vmin=-7, vmax=0
            )
            plt.colorbar(im1, ax=ax1, label=r'$\log_{10}$ flux')
            ax1.set_title('GalSim')
            ax1.set_xlabel("arcsec")
            ax1.set_ylabel("arcsec")

            sizeX = dsize_X * npix * 1e6  # microns
            ax2 = fig.add_subplot(132)
            im2 = ax2.imshow(
                np.log10(psf.array),
                extent=np.r_[-1,1,-1,1]*sizeX/2,
                vmin=-7, vmax=0
            )
            plt.colorbar(im2, ax=ax2, label=r'$\log_{10}$ flux')
            ax2.set_title('batoid')
            ax2.set_xlabel(r"$\mu m$")
            ax2.set_ylabel(r"$\mu m$")

            ax3 = fig.add_subplot(133)
            im3 = ax3.imshow(
                (psf.array-arr)/np.max(arr),
                vmin=-0.01, vmax=0.01,
                cmap='seismic'
            )
            plt.colorbar(im3, ax=ax3, label="(batoid-GalSim)/max(GalSim)")
            ax3.set_title('resid')
            ax3.set_xlabel(r"$\mu m$")
            ax3.set_ylabel(r"$\mu m$")

            fig.tight_layout()

            plt.show()