Example #1
0
def test_reflect():
    rng = np.random.default_rng(57721)
    size = 10_000
    for i in range(100):
        R = 1. / rng.normal(0.0, 0.3)
        conic = rng.uniform(-2.0, 1.0)
        quad = batoid.Quadric(R, conic)
        lim = min(0.7 * abs(R) / np.sqrt(1 + conic) if conic > -1 else 10, 10)
        x = rng.uniform(-lim, lim, size=size)
        y = rng.uniform(-lim, lim, size=size)
        z = np.full_like(x, -100.0)
        vx = rng.uniform(-1e-5, 1e-5, size=size)
        vy = rng.uniform(-1e-5, 1e-5, size=size)
        vz = np.full_like(x, 1)
        rv = batoid.RayVector(x, y, z, vx, vy, vz)
        rvr = batoid.reflect(quad, rv.copy())
        rvr2 = quad.reflect(rv.copy())
        rays_allclose(rvr, rvr2)
        # print(f"{np.sum(rvr.failed)/len(rvr)*100:.2f}% failed")
        normal = quad.normal(rvr.x, rvr.y)

        # Test law of reflection
        a0 = np.einsum("ad,ad->a", normal, rv.v)[~rvr.failed]
        a1 = np.einsum("ad,ad->a", normal, -rvr.v)[~rvr.failed]
        np.testing.assert_allclose(a0, a1, rtol=0, atol=1e-12)

        # Test that rv.v, rvr.v and normal are all in the same plane
        np.testing.assert_allclose(np.einsum("ad,ad->a",
                                             np.cross(normal, rv.v),
                                             rv.v)[~rvr.failed],
                                   0.0,
                                   rtol=0,
                                   atol=1e-12)
Example #2
0
def test_rSplit():
    rng = np.random.default_rng(5)
    for _ in range(100):
        R = rng.normal(0.7, 0.8)
        conic = rng.uniform(-2.0, 1.0)
        ncoef = rng.integers(0, 5)
        coefs = [rng.normal(0, 1e-10) for i in range(ncoef)]
        asphere = batoid.Asphere(R, conic, coefs)

        theta_x = rng.normal(0.0, 1e-8)
        theta_y = rng.normal(0.0, 1e-8)
        rays = batoid.RayVector.asPolar(
            backDist=10.0, medium=batoid.Air(),
            wavelength=500e-9, outer=0.25*R,
            theta_x=theta_x, theta_y=theta_y,
            nrad=10, naz=10
        )
        coating = batoid.SimpleCoating(0.9, 0.1)
        reflectedRays = asphere.reflect(rays.copy(), coating=coating)
        m1 = batoid.Air()
        m2 = batoid.ConstMedium(1.1)
        refractedRays = asphere.refract(rays.copy(), m1, m2, coating=coating)
        refractedRays2, reflectedRays2 = asphere.rSplit(rays, m1, m2, coating)

        rays_allclose(reflectedRays, reflectedRays2)
        rays_allclose(refractedRays, refractedRays2)
Example #3
0
def test_reflect():
    rng = np.random.default_rng(5772)
    size = 10_000
    tanx = rng.uniform(-0.1, 0.1)
    tany = rng.uniform(-0.1, 0.1)
    plane = batoid.Tilted(tanx, tany)
    x = rng.normal(0.0, 1.0, size=size)
    y = rng.normal(0.0, 1.0, size=size)
    z = np.full_like(x, -100.0)
    vx = rng.uniform(-1e-5, 1e-5, size=size)
    vy = rng.uniform(-1e-5, 1e-5, size=size)
    vz = np.ones_like(x)
    rv = batoid.RayVector(x, y, z, vx, vy, vz)
    rvr = batoid.reflect(plane, rv.copy())
    rvr2 = plane.reflect(rv.copy())
    rays_allclose(rvr, rvr2)
    # print(f"{np.sum(rvr.failed)/len(rvr)*100:.2f}% failed")
    normal = plane.normal(rvr.x, rvr.y)

    # Test law of reflection
    a0 = np.einsum("ad,ad->a", normal, rv.v)[~rvr.failed]
    a1 = np.einsum("ad,ad->a", normal, -rvr.v)[~rvr.failed]
    np.testing.assert_allclose(a0, a1, rtol=0, atol=1e-12)

    # Test that rv.v, rvr.v and normal are all in the same plane
    np.testing.assert_allclose(np.einsum("ad,ad->a", np.cross(normal, rv.v),
                                         rv.v)[~rvr.failed],
                               0.0,
                               rtol=0,
                               atol=1e-12)
Example #4
0
def test_reflect():
    rng = np.random.default_rng(57721)
    size = 10_000
    for _ in range(100):
        s1 = batoid.Sphere(1. / rng.normal(0., 0.2))
        s2 = batoid.Paraboloid(rng.uniform(1, 3))
        sum = batoid.Sum([s1, s2])
        x = rng.uniform(-1, 1, size=size)
        y = rng.uniform(-1, 1, size=size)
        z = np.full_like(x, -10.0)
        vx = rng.uniform(-1e-5, 1e-5, size=size)
        vy = rng.uniform(-1e-5, 1e-5, size=size)
        vz = np.full_like(x, 1)
        rv = batoid.RayVector(x, y, z, vx, vy, vz)
        rvr = batoid.reflect(sum, rv.copy())
        rvr2 = sum.reflect(rv.copy())
        rays_allclose(rvr, rvr2)
        # print(f"{np.sum(rvr.failed)/len(rvr)*100:.2f}% failed")
        normal = sum.normal(rvr.x, rvr.y)

        # Test law of reflection
        a0 = np.einsum("ad,ad->a", normal, rv.v)[~rvr.failed]
        a1 = np.einsum("ad,ad->a", normal, -rvr.v)[~rvr.failed]
        np.testing.assert_allclose(a0, a1, rtol=0, atol=1e-12)

        # Test that rv.v, rvr.v and normal are all in the same plane
        np.testing.assert_allclose(np.einsum("ad,ad->a",
                                             np.cross(normal, rv.v),
                                             rv.v)[~rvr.failed],
                                   0.0,
                                   rtol=0,
                                   atol=1e-12)
Example #5
0
def test_refract():
    rng = np.random.default_rng(57721)
    size = 10_000
    tanx = rng.uniform(-0.1, 0.1)
    tany = rng.uniform(-0.1, 0.1)
    plane = batoid.Tilted(tanx, tany)
    m0 = batoid.ConstMedium(rng.normal(1.2, 0.01))
    m1 = batoid.ConstMedium(rng.normal(1.3, 0.01))
    x = rng.normal(0.0, 1.0, size=size)
    y = rng.normal(0.0, 1.0, size=size)
    z = np.full_like(x, -100.0)
    vx = rng.uniform(-1e-5, 1e-5, size=size)
    vy = rng.uniform(-1e-5, 1e-5, size=size)
    vz = np.sqrt(1 - vx * vx - vy * vy) / m0.n
    rv = batoid.RayVector(x, y, z, vx, vy, vz)
    rvr = batoid.refract(plane, rv.copy(), m0, m1)
    rvr2 = plane.refract(rv.copy(), m0, m1)
    rays_allclose(rvr, rvr2)
    # print(f"{np.sum(rvr.failed)/len(rvr)*100:.2f}% failed")
    normal = plane.normal(rvr.x, rvr.y)

    # Test Snell's law
    s0 = np.sum(np.cross(normal, rv.v * m0.n)[~rvr.failed], axis=-1)
    s1 = np.sum(np.cross(normal, rvr.v * m1.n)[~rvr.failed], axis=-1)
    np.testing.assert_allclose(m0.n * s0, m1.n * s1, rtol=0, atol=1e-9)

    # Test that rv.v, rvr.v and normal are all in the same plane
    np.testing.assert_allclose(np.einsum("ad,ad->a", np.cross(normal, rv.v),
                                         rv.v)[~rvr.failed],
                               0.0,
                               rtol=0,
                               atol=1e-12)
Example #6
0
def test_refract():
    rng = np.random.default_rng(577215)
    size = 10_000
    for _ in range(100):
        s1 = batoid.Sphere(1. / rng.normal(0., 0.2))
        s2 = batoid.Paraboloid(rng.uniform(1, 3))
        sum = batoid.Sum([s1, s2])
        m0 = batoid.ConstMedium(rng.normal(1.2, 0.01))
        m1 = batoid.ConstMedium(rng.normal(1.3, 0.01))
        x = rng.uniform(-1, 1, size=size)
        y = rng.uniform(-1, 1, size=size)
        z = np.full_like(x, -10.0)
        vx = rng.uniform(-1e-5, 1e-5, size=size)
        vy = rng.uniform(-1e-5, 1e-5, size=size)
        vz = np.sqrt(1 - vx * vx - vy * vy) / m0.n
        rv = batoid.RayVector(x, y, z, vx, vy, vz)
        rvr = batoid.refract(sum, rv.copy(), m0, m1)
        rvr2 = sum.refract(rv.copy(), m0, m1)
        rays_allclose(rvr, rvr2)
        # print(f"{np.sum(rvr.failed)/len(rvr)*100:.2f}% failed")
        normal = sum.normal(rvr.x, rvr.y)

        # Test Snell's law
        s0 = np.sum(np.cross(normal, rv.v * m0.n)[~rvr.failed], axis=-1)
        s1 = np.sum(np.cross(normal, rvr.v * m1.n)[~rvr.failed], axis=-1)
        np.testing.assert_allclose(m0.n * s0, m1.n * s1, rtol=0, atol=1e-9)

        # Test that rv.v, rvr.v and normal are all in the same plane
        np.testing.assert_allclose(np.einsum("ad,ad->a",
                                             np.cross(normal, rv.v),
                                             rv.v)[~rvr.failed],
                                   0.0,
                                   rtol=0,
                                   atol=1e-12)
Example #7
0
def test_intersect():
    np.random.seed(57721)
    rv0 = batoid.RayVector([
        batoid.Ray(
            np.random.normal(scale=0.1),
            np.random.normal(scale=0.1),
            10,
            np.random.normal(scale=1e-4),
            np.random.normal(scale=1e-4),
            -1
        )
        for _ in range(100)
    ])
    for _ in range(100):
        s1 = batoid.Sphere(np.random.uniform(3, 10))
        s2 = batoid.Paraboloid(np.random.uniform(3, 10))
        sum = batoid.Sum([s1, s2])

        rv = batoid.RayVector(rv0)
        rv1 = sum.intersect(rv)
        rv2 = batoid.RayVector([sum.intersect(r) for r in rv])
        rv3 = batoid.RayVector(rv)
        sum.intersectInPlace(rv)

        for r in rv3:
            sum.intersectInPlace(r)

        assert rays_allclose(rv1, rv)
        assert rays_allclose(rv2, rv)
        assert rays_allclose(rv3, rv)
Example #8
0
def test_roundtrip():
    rng = np.random.default_rng(57)
    size = 10_000

    for i in range(10):
        coordSys1 = randomCoordSys(rng)
        coordSys2 = randomCoordSys(rng)
        transform = batoid.CoordTransform(coordSys1, coordSys2)

        rv0 = randomRayVector(rng, size, coordSys1)
        rv1 = transform.applyForward(rv0.copy())
        rv2 = transform.applyReverse(rv1.copy())
        rv3 = rv0.copy().toCoordSys(coordSys2).toCoordSys(coordSys1)

        assert rv0.coordSys == coordSys1
        assert rv1.coordSys == coordSys2
        assert rv2.coordSys == coordSys1
        assert rv3.coordSys == coordSys1

        rays_allclose(rv0, rv2)
        rays_allclose(rv0, rv3)

        x, y, z = transform.applyForwardArray(rv0.x, rv0.y, rv0.z)
        x, y, z = transform.applyReverseArray(x, y, z)
        np.testing.assert_allclose(x, rv0.x)
        np.testing.assert_allclose(y, rv0.y)
        np.testing.assert_allclose(z, rv0.z)
Example #9
0
def test_reflect():
    rng = np.random.default_rng(57721)
    size = 10_000
    for i in range(100):
        R = 1. / rng.normal(0.0, 0.3)  # negative allowed
        para = batoid.Paraboloid(R)
        x = rng.normal(0.0, 1.0, size=size)
        y = rng.normal(0.0, 1.0, size=size)
        z = np.full_like(x, -100.0)
        vx = rng.uniform(-1e-5, 1e-5, size=size)
        vy = rng.uniform(-1e-5, 1e-5, size=size)
        vz = np.full_like(x, 1)
        rv = batoid.RayVector(x, y, z, vx, vy, vz)
        rvr = batoid.reflect(para, rv.copy())
        rvr2 = para.reflect(rv.copy())
        rays_allclose(rvr, rvr2)
        # print(f"{np.sum(rvr.failed)/len(rvr)*100:.2f}% failed")
        normal = para.normal(rvr.x, rvr.y)

        # Test law of reflection
        a0 = np.einsum("ad,ad->a", normal, rv.v)[~rvr.failed]
        a1 = np.einsum("ad,ad->a", normal, -rvr.v)[~rvr.failed]
        np.testing.assert_allclose(a0, a1, rtol=0, atol=1e-12)

        # Test that rv.v, rvr.v and normal are all in the same plane
        np.testing.assert_allclose(np.einsum("ad,ad->a",
                                             np.cross(normal, rv.v),
                                             rv.v)[~rvr.failed],
                                   0.0,
                                   rtol=0,
                                   atol=1e-12)
Example #10
0
def test_refract():
    rng = np.random.default_rng(577215)
    size = 10_000
    for i in range(100):
        R = 1. / rng.normal(0.0, 0.3)
        conic = rng.uniform(-2.0, 1.0)
        quad = batoid.Quadric(R, conic)
        m0 = batoid.ConstMedium(rng.normal(1.2, 0.01))
        m1 = batoid.ConstMedium(rng.normal(1.3, 0.01))
        lim = min(0.7 * abs(R) / np.sqrt(1 + conic) if conic > -1 else 10, 10)
        x = rng.uniform(-lim, lim, size=size)
        y = rng.uniform(-lim, lim, size=size)
        z = np.full_like(x, -100.0)
        vx = rng.uniform(-1e-5, 1e-5, size=size)
        vy = rng.uniform(-1e-5, 1e-5, size=size)
        vz = np.sqrt(1 - vx * vx - vy * vy) / m0.n
        rv = batoid.RayVector(x, y, z, vx, vy, vz)
        rvr = batoid.refract(quad, rv.copy(), m0, m1)
        rvr2 = quad.refract(rv.copy(), m0, m1)
        rays_allclose(rvr, rvr2)
        # print(f"{np.sum(rvr.failed)/len(rvr)*100:.2f}% failed")
        normal = quad.normal(rvr.x, rvr.y)

        # Test Snell's law
        s0 = np.sum(np.cross(normal, rv.v * m0.n)[~rvr.failed], axis=-1)
        s1 = np.sum(np.cross(normal, rvr.v * m1.n)[~rvr.failed], axis=-1)
        np.testing.assert_allclose(m0.n * s0, m1.n * s1, rtol=0, atol=1e-9)

        # Test that rv.v, rvr.v and normal are all in the same plane
        np.testing.assert_allclose(np.einsum("ad,ad->a",
                                             np.cross(normal, rv.v),
                                             rv.v)[~rvr.failed],
                                   0.0,
                                   rtol=0,
                                   atol=1e-12)
Example #11
0
def test_refract():
    rng = np.random.default_rng(577215)
    size = 10_000

    for _ in range(10):
        def f(x, y):
            a = rng.uniform(size=5)
            return (
                a[0]*x**2*y - a[1]*y**2*x + a[2]*3*x - a[3]
                + a[4]*np.sin(y)*np.cos(x)**2
            )

        xs = np.linspace(0, 1, 1000)
        ys = np.linspace(0, 1, 1000)

        zs = f(*np.meshgrid(xs, ys))
        bc = batoid.Bicubic(xs, ys, zs)

        m0 = batoid.ConstMedium(rng.normal(1.2, 0.01))
        m1 = batoid.ConstMedium(rng.normal(1.3, 0.01))

        x = rng.uniform(0.1, 0.9, size=size)
        y = rng.uniform(0.1, 0.9, size=size)
        z = np.full_like(x, -10.0)
        vx = rng.uniform(-1e-5, 1e-5, size=size)
        vy = rng.uniform(-1e-5, 1e-5, size=size)
        vz = np.sqrt(1-vx*vx-vy*vy)/m0.n
        rv = batoid.RayVector(x, y, z, vx, vy, vz)
        rvr = batoid.refract(bc, rv.copy(), m0, m1)
        rvr2 = bc.refract(rv.copy(), m0, m1)
        np.testing.assert_array_equal(rvr.failed, rvr2.failed)
        rays_allclose(rvr, rvr2)
        # print(f"{np.sum(rvr.failed)/len(rvr)*100:.2f}% failed")
        normal = bc.normal(rvr.x, rvr.y)

        # Test Snell's law
        s0 = np.sum(np.cross(normal, rv.v*m0.n)[~rvr.failed], axis=-1)
        s1 = np.sum(np.cross(normal, rvr.v*m1.n)[~rvr.failed], axis=-1)
        np.testing.assert_allclose(
            m0.n*s0, m1.n*s1,
            rtol=0, atol=1e-9
        )

        # Test that rv.v, rvr.v and normal are all in the same plane
        np.testing.assert_allclose(
            np.einsum(
                "ad,ad->a",
                np.cross(normal, rv.v),
                rv.v
            )[~rvr.failed],
            0.0,
            rtol=0, atol=1e-12
        )
Example #12
0
def test_RV_factory_optic():
    telescope = batoid.Optic.fromYaml("LSST_r.yaml")

    grid1 = batoid.RayVector.asGrid(optic=telescope,
                                    wavelength=500e-9,
                                    theta_x=0.1,
                                    theta_y=0.1,
                                    nx=16)
    grid2 = batoid.RayVector.asGrid(wavelength=500e-9,
                                    theta_x=0.1,
                                    theta_y=0.1,
                                    backDist=telescope.backDist,
                                    stopSurface=telescope.stopSurface,
                                    medium=telescope.inMedium,
                                    lx=telescope.pupilSize,
                                    nx=16)
    assert rays_allclose(grid1, grid2)

    grid1 = batoid.RayVector.asPolar(optic=telescope,
                                     wavelength=500e-9,
                                     theta_x=0.1,
                                     theta_y=0.1,
                                     naz=100,
                                     nrad=20)
    grid2 = batoid.RayVector.asPolar(wavelength=500e-9,
                                     theta_x=0.1,
                                     theta_y=0.1,
                                     backDist=telescope.backDist,
                                     stopSurface=telescope.stopSurface,
                                     medium=telescope.inMedium,
                                     outer=telescope.pupilSize / 2,
                                     naz=100,
                                     nrad=20)
    assert rays_allclose(grid1, grid2)

    grid1 = batoid.RayVector.asSpokes(optic=telescope,
                                      wavelength=500e-9,
                                      theta_x=0.1,
                                      theta_y=0.1,
                                      rings=10,
                                      spokes=21)
    grid2 = batoid.RayVector.asSpokes(wavelength=500e-9,
                                      theta_x=0.1,
                                      theta_y=0.1,
                                      backDist=telescope.backDist,
                                      stopSurface=telescope.stopSurface,
                                      medium=telescope.inMedium,
                                      outer=telescope.pupilSize / 2,
                                      rings=10,
                                      spokes=21)
    assert rays_allclose(grid1, grid2)
Example #13
0
def test_reflect():
    rng = np.random.default_rng(57721)
    size = 10_000

    for _ in range(10):
        def f(x, y):
            a = rng.uniform(size=5)
            return (
                a[0]*x**2*y - a[1]*y**2*x + a[2]*3*x - a[3]
                + a[4]*np.sin(y)*np.cos(x)**2
            )

        xs = np.linspace(0, 1, 1000)
        ys = np.linspace(0, 1, 1000)

        zs = f(*np.meshgrid(xs, ys))
        bc = batoid.Bicubic(xs, ys, zs)

        x = rng.uniform(0.1, 0.9, size=size)
        y = rng.uniform(0.1, 0.9, size=size)
        z = np.full_like(x, -10.0)
        vx = rng.uniform(-1e-5, 1e-5, size=size)
        vy = rng.uniform(-1e-5, 1e-5, size=size)
        vz = np.full_like(x, 1)
        rv = batoid.RayVector(x, y, z, vx, vy, vz)
        rvr = batoid.reflect(bc, rv.copy())
        rvr2 = bc.reflect(rv.copy())
        rays_allclose(rvr, rvr2)
        # print(f"{np.sum(rvr.failed)/len(rvr)*100:.2f}% failed")
        normal = bc.normal(rvr.x, rvr.y)

        # Test law of reflection
        a0 = np.einsum("ad,ad->a", normal, rv.v)[~rvr.failed]
        a1 = np.einsum("ad,ad->a", normal, -rvr.v)[~rvr.failed]
        np.testing.assert_allclose(
            a0, a1,
            rtol=0, atol=1e-12
        )

        # Test that rv.v, rvr.v and normal are all in the same plane
        np.testing.assert_allclose(
            np.einsum(
                "ad,ad->a",
                np.cross(normal, rv.v),
                rv.v
            )[~rvr.failed],
            0.0,
            rtol=0, atol=1e-12
        )
Example #14
0
def test_sum_bicubic():
    import os
    import yaml

    fn = os.path.join(batoid.datadir, "LSST", "LSST_i.yaml")
    config = yaml.safe_load(open(fn))
    telescope = batoid.parse.parse_optic(config['opticalSystem'])
    xcos, ycos, zcos = batoid.utils.gnomonicToDirCos(np.deg2rad(0.8),
                                                     np.deg2rad(0.8))
    rays = batoid.circularGrid(
        telescope.dist, telescope.pupilSize / 2,
        telescope.pupilSize * telescope.pupilObscuration / 2, xcos, ycos,
        -zcos, 50, 50, 750e-9, 1.0, telescope.inMedium)
    out, _ = telescope.trace(rays)

    m2 = telescope.itemDict['LSST.M2']

    xs = np.linspace(-m2.outRadius, m2.outRadius, 200)
    ys = xs
    zs = np.zeros((200, 200), dtype=float)
    bicubic = batoid.Bicubic(xs, ys, zs)

    m2.surface = batoid.Sum([m2.surface, bicubic])
    out2, _ = telescope.trace(rays)

    # Don't expect exact equality, but should be very similar
    assert rays_allclose(out, out2, atol=1e-13)
Example #15
0
def test_sum_bicubic():
    telescope = batoid.Optic.fromYaml("LSST_i.yaml")
    xcos, ycos, zcos = batoid.utils.gnomonicToDirCos(
        np.deg2rad(0.8), np.deg2rad(0.8)
    )
    rays = batoid.circularGrid(
        telescope.backDist,
        telescope.pupilSize/2,
        telescope.pupilSize*telescope.pupilObscuration/2,
        xcos, ycos, -zcos,
        50, 50, 750e-9, 1.0, telescope.inMedium
    )
    out = telescope.trace(rays)

    m2 = telescope['LSST.M2']

    xs = np.linspace(-m2.outRadius, m2.outRadius, 200)
    ys = xs
    zs = np.zeros((200, 200), dtype=float)
    bicubic = batoid.Bicubic(xs, ys, zs)

    m2.surface = batoid.Sum([m2.surface, bicubic])
    out2 = telescope.trace(rays)

    # Don't expect exact equality, but should be very similar
    assert rays_allclose(out, out2, atol=1e-13)
Example #16
0
def test_refract():
    rng = np.random.default_rng(5772156)
    size = 10_000

    for i in range(10):
        jmax = rng.integers(4, 100)
        coef = rng.normal(size=jmax+1)*1e-3
        R_outer = rng.uniform(0.5, 5.0)
        R_inner = rng.uniform(0.0, 0.65*R_outer)

        zernike = batoid.Zernike(coef, R_outer=R_outer, R_inner=R_inner)
        lim = 0.7*R_outer
        m0 = batoid.ConstMedium(rng.normal(1.2, 0.01))
        m1 = batoid.ConstMedium(rng.normal(1.3, 0.01))
        x = rng.uniform(-lim, lim, size=size)
        y = rng.uniform(-lim, lim, size=size)
        z = np.full_like(x, -10.0)
        vx = rng.uniform(-1e-5, 1e-5, size=size)
        vy = rng.uniform(-1e-5, 1e-5, size=size)
        vz = np.sqrt(1-vx*vx-vy*vy)/m0.n
        rv = batoid.RayVector(x, y, z, vx, vy, vz, t=0)
        rvr = batoid.refract(zernike, rv.copy(), m0, m1)
        rvr2 = zernike.refract(rv.copy(), m0, m1)
        rays_allclose(rvr, rvr2, atol=1e-13)
        # print(f"{np.sum(rvr.failed)/len(rvr)*100:.2f}% failed")
        normal = zernike.normal(rvr.x, rvr.y)

        # Test Snell's law
        s0 = np.sum(np.cross(normal, rv.v*m0.n)[~rvr.failed], axis=-1)
        s1 = np.sum(np.cross(normal, rvr.v*m1.n)[~rvr.failed], axis=-1)
        np.testing.assert_allclose(
            m0.n*s0, m1.n*s1,
            rtol=0, atol=1e-9
        )

        # Test that rv.v, rvr.v and normal are all in the same plane
        np.testing.assert_allclose(
            np.einsum(
                "ad,ad->a",
                np.cross(normal, rv.v),
                rv.v
            )[~rvr.failed],
            0.0,
            rtol=0, atol=1e-12
        )
Example #17
0
def test_withSurface():
    telescope = batoid.Optic.fromYaml("HSC.yaml")
    rays = batoid.RayVector.asPolar(telescope,
                                    wavelength=620e-9,
                                    theta_x=np.deg2rad(0.1),
                                    theta_y=0.0,
                                    nrad=10,
                                    naz=60)
    trays = telescope.trace(rays.copy())
    for key, item in telescope.itemDict.items():
        if not isinstance(item, batoid.Interface):
            continue
        # Do a trivial surface replacement
        surf2 = batoid.Sum([batoid.Plane(), item.surface])
        telescope2 = telescope.withSurface(key, surf2)
        assert telescope != telescope2
        trays2 = telescope.trace(rays.copy())
        rays_allclose(trays, trays2)
Example #18
0
def test_reflect():
    rng = np.random.default_rng(577215)
    size = 10_000

    for i in range(10):
        jmax = rng.integers(4, 36+1)
        coef = rng.normal(size=jmax+1)*1e-3
        R_outer = rng.uniform(0.5, 5.0)
        R_inner = rng.uniform(0.0, 0.65*R_outer)

        zernike = batoid.Zernike(coef, R_outer=R_outer, R_inner=R_inner)
        lim = 0.7*R_outer

        x = rng.uniform(-lim, lim, size=size)
        y = rng.uniform(-lim, lim, size=size)
        z = np.full_like(x, -1.0)
        vx = rng.uniform(-1e-5, 1e-5, size=size)
        vy = rng.uniform(-1e-5, 1e-5, size=size)
        vz = np.full_like(x, 1)
        rv = batoid.RayVector(x, y, z, vx, vy, vz)
        rvr = batoid.reflect(zernike, rv.copy())
        rvr2 = zernike.reflect(rv.copy())
        rays_allclose(rvr, rvr2)
        # print(f"{np.sum(rvr.failed)/len(rvr)*100:.2f}% failed")
        normal = zernike.normal(rvr.x, rvr.y)

        # Test law of reflection
        a0 = np.einsum("ad,ad->a", normal, rv.v)[~rvr.failed]
        a1 = np.einsum("ad,ad->a", normal, -rvr.v)[~rvr.failed]
        np.testing.assert_allclose(
            a0, a1,
            rtol=0, atol=1e-12
        )

        # Test that rv.v, rvr.v and normal are all in the same plane
        np.testing.assert_allclose(
            np.einsum(
                "ad,ad->a",
                np.cross(normal, rv.v),
                rv.v
            )[~rvr.failed],
            0.0,
            rtol=0, atol=1e-12
        )
Example #19
0
def test_composition():
    rng = np.random.default_rng(577)
    size = 10_000

    for i in range(10):
        coordSys1 = randomCoordSys(rng)
        coordSys2 = randomCoordSys(rng)
        coordSys3 = randomCoordSys(rng)

        assert coordSys1 != coordSys2
        assert coordSys1 != coordSys3

        do_pickle(coordSys1)

        transform1to2 = batoid.CoordTransform(coordSys1, coordSys2)
        transform1to3 = batoid.CoordTransform(coordSys1, coordSys3)
        transform2to3 = batoid.CoordTransform(coordSys2, coordSys3)

        do_pickle(transform1to2)

        for j in range(10):
            rv = randomRayVector(rng, size, coordSys1)
            rv_a = transform1to3.applyForward(rv.copy())
            assert rv_a != rv
            assert rv_a.coordSys == coordSys3
            rv_b = transform2to3.applyForward(
                transform1to2.applyForward(rv.copy()))
            assert rv_b != rv
            assert rv_b.coordSys == coordSys3
            rays_allclose(rv_a,
                          rv_b), "error with composite transform of RayVector"

            rv = randomRayVector(rng, size, coordSys3)
            rv_a = transform1to3.applyReverse(rv.copy())
            assert rv_a != rv
            assert rv_a.coordSys == coordSys1
            rv_b = transform1to2.applyReverse(
                transform2to3.applyReverse(rv.copy()))
            assert rv_b != rv
            assert rv_b.coordSys == coordSys1
            rays_allclose(
                rv_a,
                rv_b), "error with reverse composite transform of RayVector"
Example #20
0
def test_refract():
    rng = np.random.default_rng(577215)
    size = 10_000
    for i in range(100):
        zmax = np.inf
        while zmax > 3.0:
            R = 0.0
            while abs(R) < 15.0:  # Don't allow too small radius of curvature
                R = 1. / rng.normal(0.0, 0.3)  # negative allowed
            conic = rng.uniform(-2.0, 1.0)
            ncoef = rng.choice(5)
            coefs = [rng.normal(0, 1e-8) for i in range(ncoef)]
            asphere = batoid.Asphere(R, conic, coefs)
            lim = min(0.7 * abs(R) / np.sqrt(1 + conic) if conic > -1 else 5,
                      5)
            zmax = abs(asphere.sag(lim, lim))
        m0 = batoid.ConstMedium(rng.normal(1.2, 0.01))
        m1 = batoid.ConstMedium(rng.normal(1.3, 0.01))
        x = rng.uniform(-lim, lim, size=size)
        y = rng.uniform(-lim, lim, size=size)
        z = np.full_like(x, -10.0)
        vx = rng.uniform(-1e-5, 1e-5, size=size)
        vy = rng.uniform(-1e-5, 1e-5, size=size)
        vz = np.sqrt(1 - vx * vx - vy * vy) / m0.n
        rv = batoid.RayVector(x, y, z, vx, vy, vz, t=0)
        rvr = batoid.refract(asphere, rv.copy(), m0, m1)
        rvr2 = asphere.refract(rv.copy(), m0, m1)
        rays_allclose(rvr, rvr2)
        # print(f"{np.sum(rvr.failed)/len(rvr)*100:.2f}% failed")
        normal = asphere.normal(rvr.x, rvr.y)

        # Test Snell's law
        s0 = np.sum(np.cross(normal, rv.v * m0.n)[~rvr.failed], axis=-1)
        s1 = np.sum(np.cross(normal, rvr.v * m1.n)[~rvr.failed], axis=-1)
        np.testing.assert_allclose(m0.n * s0, m1.n * s1, rtol=0, atol=1e-9)

        # Test that rv.v, rvr.v and normal are all in the same plane
        np.testing.assert_allclose(np.einsum("ad,ad->a",
                                             np.cross(normal, rv.v),
                                             rv.v)[~rvr.failed],
                                   0.0,
                                   rtol=0,
                                   atol=1e-12)
Example #21
0
def test_reflect():
    rng = np.random.default_rng(57721)
    size = 10_000
    for i in range(100):
        zmax = np.inf
        while zmax > 3.0:
            R = 0.0
            while abs(R) < 15.0:  # Don't allow too small radius of curvature
                R = 1. / rng.normal(0.0, 0.3)  # negative allowed
            conic = rng.uniform(-2.0, 1.0)
            ncoef = rng.choice(5)
            coefs = [rng.normal(0, 1e-8) for i in range(ncoef)]
            asphere = batoid.Asphere(R, conic, coefs)
            lim = min(0.7 * abs(R) / np.sqrt(1 + conic) if conic > -1 else 5,
                      5)
            zmax = abs(asphere.sag(lim, lim))
        x = rng.uniform(-lim, lim, size=size)
        y = rng.uniform(-lim, lim, size=size)
        z = np.full_like(x, -10.0)
        vx = rng.uniform(-1e-5, 1e-5, size=size)
        vy = rng.uniform(-1e-5, 1e-5, size=size)
        vz = np.full_like(x, 1)
        rv = batoid.RayVector(x, y, z, vx, vy, vz)
        rvr = batoid.reflect(asphere, rv.copy())
        rvr2 = asphere.reflect(rv.copy())
        rays_allclose(rvr, rvr2)
        # print(f"{np.sum(rvr.failed)/len(rvr)*100:.2f}% failed")
        normal = asphere.normal(rvr.x, rvr.y)

        # Test law of reflection
        a0 = np.einsum("ad,ad->a", normal, rv.v)[~rvr.failed]
        a1 = np.einsum("ad,ad->a", normal, -rvr.v)[~rvr.failed]
        np.testing.assert_allclose(a0, a1, rtol=0, atol=1e-12)

        # Test that rv.v, rvr.v and normal are all in the same plane
        np.testing.assert_allclose(np.einsum("ad,ad->a",
                                             np.cross(normal, rv.v),
                                             rv.v)[~rvr.failed],
                                   0.0,
                                   rtol=0,
                                   atol=1e-12)
Example #22
0
def test_composition():
    import random
    random.seed(5)

    for i in range(10):
        coordSys1 = randomCoordSys()
        coordSys2 = randomCoordSys()
        coordSys3 = randomCoordSys()

        assert coordSys1 != coordSys2
        assert coordSys1 != coordSys3

        do_pickle(coordSys1)

        transform1to2 = batoid.CoordTransform(coordSys1, coordSys2)
        transform1to3 = batoid.CoordTransform(coordSys1, coordSys3)
        transform2to3 = batoid.CoordTransform(coordSys2, coordSys3)

        do_pickle(transform1to2)

        for i in range(10):
            vec3 = randomVec3()
            vec3_a = transform1to3.applyForward(vec3)
            vec3_b = transform2to3.applyForward(transform1to2.applyForward(vec3))
            np.testing.assert_allclose(vec3_a, vec3_b)
            vec3_ra = transform1to3.applyReverse(vec3)
            vec3_rb = transform1to2.applyReverse(transform2to3.applyReverse(vec3))
            np.testing.assert_allclose(vec3_ra, vec3_rb)

            ray = randomRay()
            ray_a = transform1to3.applyForward(ray)
            ray_b = transform2to3.applyForward(transform1to2.applyForward(ray))
            assert ray_isclose(ray_a, ray_b), "error with composite transform of Ray"
            ray_ra = transform1to3.applyReverse(ray)
            ray_rb = transform1to2.applyReverse(transform2to3.applyReverse(ray))
            assert ray_isclose(ray_ra, ray_rb), "error with reverse composite transform of Ray"

            rv = randomRayVector()
            rv_a = transform1to3.applyForward(rv)
            rv_b = transform2to3.applyForward(transform1to2.applyForward(rv))
            assert rays_allclose(rv_a, rv_b), "error with composite transform of RayVector"
            rv_ra = transform1to3.applyReverse(rv)
            rv_rb = transform1to2.applyReverse(transform2to3.applyReverse(rv))
            assert rays_allclose(rv_ra, rv_rb), "error with reverse composite transform of RayVector"

            # Test with numpy arrays
            xyz = rv.x, rv.y, rv.z
            xyz_a = transform1to3.applyForward(*xyz)
            xyz_b = transform2to3.applyForward(*transform1to2.applyForward(*xyz))
            xyz_c = [transform2to3.applyForward(transform1to2.applyForward(r.r)) for r in rv]
            np.testing.assert_allclose(xyz_a, xyz_b)
            np.testing.assert_allclose(xyz_a, np.transpose(xyz_c))
            # Should still work if we reshape.
            xyz2 = rv.x.reshape((2, 5)), rv.y.reshape((2, 5)), rv.z.reshape((2, 5))
            xyz2_a = transform1to3.applyForward(*xyz2)
            xyz2_b = transform2to3.applyForward(*transform1to2.applyForward(*xyz2))
            np.testing.assert_allclose(xyz2_a, xyz2_b)

            # And also work if we reverse
            np.testing.assert_allclose(xyz, transform1to3.applyReverse(*xyz_a))
            np.testing.assert_allclose(xyz, transform1to2.applyReverse(*transform2to3.applyReverse(*xyz_b)))

            # Test in-place on Ray
            ray = randomRay()
            ray_copy = ray.copy()
            transform1to2.applyForwardInPlace(ray)
            transform2to3.applyForwardInPlace(ray)
            transform1to3.applyForwardInPlace(ray_copy)
            assert ray_isclose(ray, ray_copy)

            # in-place reverse on Ray
            ray = randomRay()
            ray_copy = ray.copy()
            transform2to3.applyReverseInPlace(ray)
            transform1to2.applyReverseInPlace(ray)
            transform1to3.applyReverseInPlace(ray_copy)
            assert ray_isclose(ray, ray_copy)

            # Test in-place on RayVector
            rv = randomRayVector()
            rv_copy = rv.copy()
            transform1to2.applyForwardInPlace(rv)
            transform2to3.applyForwardInPlace(rv)
            transform1to3.applyForwardInPlace(rv_copy)
            assert rays_allclose(rv, rv_copy)

            # in-place reverse on RayVector
            rv = randomRayVector()
            rv_copy = rv.copy()
            transform2to3.applyReverseInPlace(rv)
            transform1to2.applyReverseInPlace(rv)
            transform1to3.applyReverseInPlace(rv_copy)
            assert rays_allclose(rv, rv_copy)
Example #23
0
def test_zeroscreen():
    """Add a zero phase OPDScreen in front of LSST entrance pupil.  Should have
    _no_ effect.
    """
    lsst = batoid.Optic.fromYaml("LSST_r.yaml")

    screens = [
        batoid.optic.OPDScreen(
            batoid.Plane(),
            batoid.Plane(),
            name='PS',
            coordSys=lsst.stopSurface.coordSys
        ),
        batoid.optic.OPDScreen(
            batoid.Paraboloid(100.0),
            batoid.Plane(),
            name='PS',
            coordSys=lsst.stopSurface.coordSys
        ),
        batoid.optic.OPDScreen(
            batoid.Quadric(11.0, -0.5),
            batoid.Plane(),
            name='PS',
            coordSys=lsst.stopSurface.coordSys
        ),
        batoid.optic.OPDScreen(
            batoid.Zernike([0, 0, 0, 0, 300e-9, 0, 0, 400e-9, -600e-9]),
            batoid.Zernike([0]*22),
            name='PS',
            coordSys=lsst.stopSurface.coordSys
        )
    ]

    for screen in screens:
        tel = batoid.CompoundOptic(
            (screen, *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)

        rng = np.random.default_rng(57)
        thx = np.deg2rad(rng.uniform(-1, 1))
        thy = np.deg2rad(rng.uniform(-1, 1))
        rays = batoid.RayVector.asPolar(
            optic=tel, wavelength=620e-9,
            theta_x=thx, theta_y=thy,
            nrad=5, naz=60
        )

        tf1 = tel.traceFull(rays)
        tf2 = lsst.traceFull(rays)

        np.testing.assert_allclose(
            tf1['PS']['in'].v,
            tf1['PS']['out'].v,
            rtol=0, atol=1e-14
        )

        for key in tf2:
            rays_allclose(
                tf1[key]['out'],
                tf2[key]['out'],
                atol=1e-13
            )
Example #24
0
def test_asGrid():
    rng = np.random.default_rng(5772156)
    for _ in range(10):
        backDist = rng.uniform(9.0, 11.0)
        wavelength = rng.uniform(300e-9, 1100e-9)
        nx = 1
        while (nx%2) == 1:
            nx = rng.integers(10, 21)
        lx = rng.uniform(1.0, 10.0)
        dx = lx/(nx-2)
        dirCos = np.array([
            rng.uniform(-0.1, 0.1),
            rng.uniform(-0.1, 0.1),
            rng.uniform(-1.2, -0.8),
        ])
        dirCos /= np.sqrt(np.dot(dirCos, dirCos))

        # Some things that should be equivalent
        grid1 = batoid.RayVector.asGrid(
            backDist=backDist, wavelength=wavelength,
            nx=nx, lx=lx, dirCos=dirCos
        )
        grid2 = batoid.RayVector.asGrid(
            backDist=backDist, wavelength=wavelength,
            nx=nx, dx=dx, dirCos=dirCos
        )
        grid3 = batoid.RayVector.asGrid(
            backDist=backDist, wavelength=wavelength,
            dx=dx, lx=lx, dirCos=dirCos
        )
        grid4 = batoid.RayVector.asGrid(
            backDist=backDist, wavelength=wavelength,
            nx=nx, lx=(lx, 0.0), dirCos=dirCos
        )
        theta_x, theta_y = batoid.utils.dirCosToField(*dirCos)
        grid5 = batoid.RayVector.asGrid(
            backDist=backDist, wavelength=wavelength,
            nx=nx, lx=(lx, 0.0), theta_x=theta_x, theta_y=theta_y
        )
        rays_allclose(grid1, grid2)
        rays_allclose(grid1, grid3)
        rays_allclose(grid1, grid4)
        rays_allclose(grid1, grid5)

        # Check distance to chief ray
        cridx = (nx//2)*nx+nx//2
        obs_dist = np.sqrt(np.dot(grid1.r[cridx], grid1.r[cridx]))
        np.testing.assert_allclose(obs_dist, backDist)

        np.testing.assert_allclose(grid1.t, 0)
        np.testing.assert_allclose(grid1.wavelength, wavelength)
        np.testing.assert_allclose(grid1.vignetted, False)
        np.testing.assert_allclose(grid1.failed, False)
        np.testing.assert_allclose(grid1.vx, dirCos[0])
        np.testing.assert_allclose(grid1.vy, dirCos[1])
        np.testing.assert_allclose(grid1.vz, dirCos[2])

        # Check distribution of points propagated to entrance pupil
        pupil = batoid.Plane()
        pupil.intersect(grid1)
        np.testing.assert_allclose(np.diff(grid1.x)[0], dx)
        np.testing.assert_allclose(np.diff(grid1.y)[0], 0, atol=1e-14)
        np.testing.assert_allclose(np.diff(grid1.x)[nx-1], -dx*(nx-1))
        np.testing.assert_allclose(np.diff(grid1.y)[nx-1], dx)

        # Another set, but with odd nx
    for _ in range(10):
        backDist = rng.uniform(9.0, 11.0)
        wavelength = rng.uniform(300e-9, 1100e-9)
        while (nx%2) == 0:
            nx = rng.integers(10, 21)
        lx = rng.uniform(1.0, 10.0)
        dx = lx/(nx-1)
        dirCos = np.array([
            rng.uniform(-0.1, 0.1),
            rng.uniform(-0.1, 0.1),
            rng.uniform(-1.2, -0.8),
        ])
        dirCos /= np.sqrt(np.dot(dirCos, dirCos))

        grid1 = batoid.RayVector.asGrid(
            backDist=backDist, wavelength=wavelength,
            nx=nx, lx=lx, dirCos=dirCos
        )
        grid2 = batoid.RayVector.asGrid(
            backDist=backDist, wavelength=wavelength,
            nx=nx, dx=dx, dirCos=dirCos
        )
        grid3 = batoid.RayVector.asGrid(
            backDist=backDist, wavelength=wavelength,
            nx=nx, lx=(lx, 0), dirCos=dirCos
        )
        # ... but the following is not equivalent, since default is to always
        # infer an even nx and ny
        # grid4 = batoid.RayVector.asGrid(
        #     backDist=backDist, wavelength=wavelength,
        #     dx=1/9, lx=1.0, dirCos=dirCos
        # )

        rays_allclose(grid1, grid2)
        rays_allclose(grid1, grid3)

        cridx = (nx*nx-1)//2
        obs_dist = np.sqrt(np.dot(grid1.r[cridx], grid1.r[cridx]))
        np.testing.assert_allclose(obs_dist, backDist)

        np.testing.assert_allclose(grid1.t, 0)
        np.testing.assert_allclose(grid1.wavelength, wavelength)
        np.testing.assert_allclose(grid1.vignetted, False)
        np.testing.assert_allclose(grid1.failed, False)
        np.testing.assert_allclose(grid1.vx, dirCos[0])
        np.testing.assert_allclose(grid1.vy, dirCos[1])
        np.testing.assert_allclose(grid1.vz, dirCos[2])

        # Check distribution of points propagated to entrance pupil
        pupil = batoid.Plane()
        pupil.intersect(grid1)
        np.testing.assert_allclose(np.diff(grid1.x)[0], dx)
        np.testing.assert_allclose(np.diff(grid1.y)[0], 0, atol=1e-14)
        np.testing.assert_allclose(np.diff(grid1.x)[nx-1], -dx*(nx-1))
        np.testing.assert_allclose(np.diff(grid1.y)[nx-1], dx)

    for _ in range(10):
        # Check nrandom
        rays = batoid.RayVector.asGrid(
            backDist=backDist, wavelength=wavelength,
            lx=1.0, nx=1,
            nrandom=1000, dirCos=dirCos
        )

        np.testing.assert_allclose(rays.t, 0)
        np.testing.assert_allclose(rays.wavelength, wavelength)
        np.testing.assert_allclose(rays.vignetted, False)
        np.testing.assert_allclose(rays.failed, False)
        np.testing.assert_allclose(rays.vx, dirCos[0])
        np.testing.assert_allclose(rays.vy, dirCos[1])
        np.testing.assert_allclose(rays.vz, dirCos[2])

        # Check that projected points are inside region
        pupil = batoid.Plane()
        pupil.intersect(rays)
        np.testing.assert_allclose(rays.z, 0.0)
        np.testing.assert_array_less(rays.x, 0.5)
        np.testing.assert_array_less(rays.y, 0.5)
        np.testing.assert_array_less(-0.5, rays.x)
        np.testing.assert_array_less(-0.5, rays.y)
        assert len(rays) == 1000