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 )
def test_doubleZernike(): telescope = batoid.Optic.fromYaml("LSST_r.yaml") dz = batoid.doubleZernike(telescope, np.deg2rad(1.75), 625e-9, 10, kmax=28, jmax=22) # Now evaluate DZ a few places and compare with zernikeGQ size = 20 js = np.random.randint(4, 22, size=size) thr = np.deg2rad(np.sqrt(np.random.uniform(0, 1.75**2, size=size))) thth = np.random.uniform(0, 2 * np.pi, size=size) thx = thr * np.cos(thth) thy = thr * np.sin(thth) for j in js: Z = galsim.zernike.Zernike(dz[:, j], R_inner=0.0, R_outer=np.deg2rad(1.75)) for thx_, thy_ in zip(thx, thy): zGQ = batoid.zernikeGQ(telescope, thx_, thy_, 625e-9, jmax=22) np.testing.assert_allclose(Z(thx_, thy_), zGQ[j], rtol=0, atol=1e-4) # Check that we get similar results with different number of rings/spokes dz2 = batoid.doubleZernike(telescope, np.deg2rad(1.75), 625e-9, rings=12, spokes=29, kmax=28, jmax=22) np.testing.assert_allclose(dz, dz2, rtol=0, atol=1e-2)
def test_rotation(): rng = np.random.default_rng(57) telescope = batoid.Optic.fromYaml("HSC.yaml") rot = batoid.RotX(rng.uniform(low=0.0, high=2 * np.pi)) rot = rot.dot(batoid.RotY(rng.uniform(low=0.0, high=2 * np.pi))) rot = rot.dot(batoid.RotZ(rng.uniform(low=0.0, high=2 * np.pi))) rotInv = np.linalg.inv(rot) # It's hard to test the two telescopes for equality due to rounding errors, # so we test by comparing zernikes rotTel = telescope.withLocalRotation(rot).withLocalRotation(rotInv) theta_x = rng.uniform(-0.005, 0.005) theta_y = rng.uniform(-0.005, 0.005) wavelength = 750e-9 for k in telescope.itemDict.keys(): np.testing.assert_allclose(telescope[k].coordSys.origin, rotTel[k].coordSys.origin, rtol=0, atol=1e-14) np.testing.assert_allclose(telescope[k].coordSys.rot, rotTel[k].coordSys.rot, rtol=0, atol=1e-14) np.testing.assert_allclose(batoid.zernikeGQ(telescope, theta_x, theta_y, wavelength), batoid.zernikeGQ(rotTel, theta_x, theta_y, wavelength), atol=1e-7) for item in telescope.itemDict: rotTel = telescope.withLocallyRotatedOptic(item, rot) rotTel = rotTel.withLocallyRotatedOptic(item, rotInv) rotTel2 = telescope.withLocallyRotatedOptic(item, np.eye(3)) theta_x = rng.uniform(-0.005, 0.005) theta_y = rng.uniform(-0.005, 0.005) np.testing.assert_allclose(batoid.zernikeGQ(telescope, theta_x, theta_y, wavelength), batoid.zernikeGQ(rotTel, theta_x, theta_y, wavelength), atol=1e-7) np.testing.assert_allclose(batoid.zernikeGQ(telescope, theta_x, theta_y, wavelength), batoid.zernikeGQ(rotTel2, theta_x, theta_y, wavelength), atol=1e-7) # Test with non-fully-qualified name rotTel = telescope.withLocallyRotatedOptic('G1', rot) rotTel = rotTel.withLocallyRotatedOptic('G1', rotInv) rotTel2 = rotTel.withLocallyRotatedOptic('G1', np.eye(3)) np.testing.assert_allclose(batoid.zernikeGQ(telescope, theta_x, theta_y, wavelength), batoid.zernikeGQ(rotTel, theta_x, theta_y, wavelength), atol=1e-7) np.testing.assert_allclose(batoid.zernikeGQ(telescope, theta_x, theta_y, wavelength), batoid.zernikeGQ(rotTel2, theta_x, theta_y, wavelength), atol=1e-7)
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.zernike(telescope, 0.0, 0.0, 625e-9, nx=nx, jmax=28, reference='chief') zGQ = batoid.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.zernike(telescope, 0.0, 0.0, 625e-9, nx=nx, jmax=28, reference='chief', eps=0.61) zGQ = batoid.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.zernike(telescope, np.deg2rad(0.2), np.deg2rad(0.1), 625e-9, nx=nx, jmax=28, reference='chief', eps=0.61) zGQ = batoid.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) # Try reference == mean # Try off-axis zSquare = batoid.zernike(telescope, np.deg2rad(0.2), np.deg2rad(0.1), 625e-9, nx=nx, jmax=28, reference='mean', eps=0.61) zGQ = batoid.zernikeGQ(telescope, np.deg2rad(0.2), np.deg2rad(0.1), 625e-9, rings=rings, jmax=28, reference='mean', eps=0.61) # Z1-3 less reliable, but mostly uninteresting anyway... np.testing.assert_allclose(zSquare[4:], zGQ[4:], rtol=0, atol=tol)