def test_ne(): objs = [ batoid.Plane(), batoid.Plane(allowReverse=True), batoid.Paraboloid(2.0), ] all_obj_diff(objs)
def test_ne(): objs = [ batoid.Sphere(1.0), batoid.Sphere(2.0), batoid.Plane() ] all_obj_diff(objs)
def test_ne(): objs = [ batoid.Paraboloid(1.0), batoid.Paraboloid(2.0), batoid.Plane() ] all_obj_diff(objs)
def test_add_plane(): np.random.seed(577) for _ in range(100): # Adding a plane should have zero effect on sag or normal vector s1 = batoid.Sphere(np.random.uniform(1, 3)) s2 = batoid.Plane() sum = batoid.Sum([s1, s2]) x = np.random.normal(size=5000) y = np.random.normal(size=5000) np.testing.assert_allclose( sum.sag(x, y), s1.sag(x, y), rtol=1e-12, atol=1e-12 ) for _x, _y in zip(x[:100], y[:100]): np.testing.assert_allclose( sum.normal(_x, _y), s1.normal(_x, _y), rtol=1e-12, atol=1e-12 )
def test_intersect(): rng = np.random.default_rng(577) size = 10_000 planeCoordSys = batoid.CoordSys(origin=[0, 0, -1]) plane = batoid.Plane() 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) # If we shoot rays straight up, then it's easy to predict the intersection vx = np.zeros_like(x) vy = np.zeros_like(x) vz = np.ones_like(x) rv = batoid.RayVector(x, y, z, vx, vy, vz) np.testing.assert_allclose(rv.z, -100.0) rv2 = batoid.intersect(plane, rv.copy(), planeCoordSys) assert rv2.coordSys == planeCoordSys rv2 = rv2.toCoordSys(batoid.CoordSys()) np.testing.assert_allclose(rv2.x, x) np.testing.assert_allclose(rv2.y, y) np.testing.assert_allclose(rv2.z, -1, rtol=0, atol=1e-12) # Check default intersect coordTransform rv2 = rv.copy().toCoordSys(planeCoordSys) batoid.intersect(plane, rv2) assert rv2.coordSys == planeCoordSys rv2 = rv2.toCoordSys(batoid.CoordSys()) np.testing.assert_allclose(rv2.x, x) np.testing.assert_allclose(rv2.y, y) np.testing.assert_allclose(rv2.z, -1, rtol=0, atol=1e-12)
def test_plane_reflection_reversal(): import random random.seed(57) plane = batoid.Plane() for i in range(1000): x = random.gauss(0, 1) y = random.gauss(0, 1) vx = random.gauss(0, 1e-1) vy = random.gauss(0, 1e-1) v = np.array([vx, vy, 1]) v / np.linalg.norm(v) ray = batoid.Ray([x, y, -10], v, 0) rray = plane.reflect(ray.copy()) # Invert the reflected ray, and see that it ends back at the starting # point # Keep going a bit before turning around though turn_around = rray.positionAtTime(rray.t+0.1) return_ray = batoid.Ray(turn_around, -rray.v, -(rray.t+0.1)) riray = plane.intersect(return_ray.copy()) np.testing.assert_allclose(rray.r[0], riray.r[0], rtol=0, atol=1e-10) np.testing.assert_allclose(rray.r[1], riray.r[1], rtol=0, atol=1e-10) np.testing.assert_allclose(rray.r[2], riray.r[2], rtol=0, atol=1e-10) # Reflect and propagate back to t=0. cray = plane.reflect(return_ray.copy()) cray = cray.positionAtTime(0) np.testing.assert_allclose(cray[0], x, rtol=0, atol=1e-10) np.testing.assert_allclose(cray[1], y, rtol=0, atol=1e-10) np.testing.assert_allclose(cray[2], -10, rtol=0, atol=1e-10)
def test_plane_refraction_reversal(): import random random.seed(57) wavelength = 500e-9 # arbitrary plane = batoid.Plane() m1 = batoid.ConstMedium(1.5) m2 = batoid.ConstMedium(1.2) for i in range(1000): x = random.gauss(0, 1) y = random.gauss(0, 1) vx = random.gauss(0, 1e-1) vy = random.gauss(0, 1e-1) ray = batoid.Ray([x, y, -10], normalized(np.array([vx, vy, 1]))/m1.getN(wavelength), 0) rray = plane.refract(ray.copy(), m1, m2) np.testing.assert_allclose(np.linalg.norm(rray.v), 1./m2.getN(wavelength), rtol=1e-14) # Invert the refracted ray, and see that it ends back at the starting # point # Keep going a bit before turning around though turn_around = rray.positionAtTime(rray.t+0.1) return_ray = batoid.Ray(turn_around, -rray.v, -(rray.t+0.1)) riray = plane.intersect(return_ray.copy()) np.testing.assert_allclose(rray.r[0], riray.r[0], rtol=0, atol=1e-10) np.testing.assert_allclose(rray.r[1], riray.r[1], rtol=0, atol=1e-10) np.testing.assert_allclose(rray.r[2], riray.r[2], rtol=0, atol=1e-10) # Refract and propagate back to t=0. cray = plane.refract(return_ray.copy(), m2, m1) np.testing.assert_allclose(np.linalg.norm(cray.v), 1./m1.getN(wavelength), rtol=1e-14) cpoint = cray.positionAtTime(0) np.testing.assert_allclose(cpoint[0], x, rtol=0, atol=1e-10) np.testing.assert_allclose(cpoint[1], y, rtol=0, atol=1e-10) np.testing.assert_allclose(cpoint[2], -10, rtol=0, atol=1e-10)
def test_plane_refraction_plane(): import random random.seed(5) wavelength = 500e-9 # arbitrary plane = batoid.Plane() m1 = batoid.ConstMedium(1.1) m2 = batoid.ConstMedium(1.3) for i in range(1000): x = random.gauss(0, 1) y = random.gauss(0, 1) vx = random.gauss(0, 1e-1) vy = random.gauss(0, 1e-1) v = np.array([vx, vy, 1]) v /= np.linalg.norm(v) ray = batoid.Ray([x, y, -10], v/m1.getN(wavelength), 0) rray = plane.refract(ray.copy(), m1, m2) np.testing.assert_allclose(np.linalg.norm(rray.v), 1./m2.getN(wavelength), rtol=1e-14) # ray.v, surfaceNormal, and rray.v should all be in the same plane, and # hence (ray.v x surfaceNormal) . rray.v should have zero magnitude. normal = plane.normal(rray.r[0], rray.r[1]) np.testing.assert_allclose( np.dot(np.cross(ray.v, normal), rray.v), 0.0, rtol=0, atol=1e-15) # Test Snell's law np.testing.assert_allclose( m1.getN(wavelength)*np.linalg.norm(np.cross(normalized(ray.v), normal)), m2.getN(wavelength)*np.linalg.norm(np.cross(normalized(rray.v), normal)), rtol=0, atol=1e-15)
def test_reflect(): rng = np.random.default_rng(5772) size = 10_000 plane = batoid.Plane() 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)
def test_ne(): objs = [ batoid.Plane(), batoid.Tilted(0.0, 0.1), batoid.Tilted(0.1, 0.1), ] all_obj_diff(objs)
def test_plane_reflection_plane(): import random random.seed(5) plane = batoid.Plane() for i in range(1000): x = random.gauss(0, 1) y = random.gauss(0, 1) vx = random.gauss(0, 1e-1) vy = random.gauss(0, 1e-1) v = np.array([vx, vy, 1]) v / np.linalg.norm(v) ray = batoid.Ray([x, y, -10], v, 0) rray = plane.reflect(ray.copy()) # ray.v, surfaceNormal, and rray.v should all be in the same plane, and # hence (ray.v x surfaceNormal) . rray.v should have zero magnitude. # magnitude zero. np.testing.assert_allclose( np.dot(np.cross(ray.v, plane.normal(rray.r[0], rray.r[1])), rray.v), 0.0, rtol=0, atol=1e-15) # Actually, reflection off a plane is pretty straigtforward to test # directly. np.testing.assert_allclose(ray.v[0], rray.v[0]) np.testing.assert_allclose(ray.v[1], rray.v[1]) np.testing.assert_allclose(ray.v[2], -rray.v[2])
def test_refract(): rng = np.random.default_rng(57721) size = 10_000 plane = batoid.Plane() 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)
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_fail(): sum = batoid.Sum([batoid.Plane(), batoid.Sphere(1.0)]) ray = batoid.Ray([0,0,sum.sag(0,0)-1], [0,0,-1]) ray = sum.intersect(ray) assert ray.failed ray = batoid.Ray([0,0,sum.sag(0,0)-1], [0,0,-1]) sum.intersectInPlace(ray) assert ray.failed
def test_fail(): plane = batoid.Plane() ray = batoid.Ray([0, 0, -1], [0, 0, -1]) ray = plane.intersect(ray) assert ray.failed ray = batoid.Ray([0, 0, -1], [0, 0, -1]) plane.intersectInPlace(ray) assert ray.failed
def test_fail(): sum = batoid.Sum([batoid.Plane(), batoid.Sphere(1.0)]) rv = batoid.RayVector(0, 10, 0, 0, 0, -1) # Too far to side rv2 = batoid.intersect(sum, rv.copy()) np.testing.assert_equal(rv2.failed, np.array([True])) # This one passes rv = batoid.RayVector(0, 0, -1, 0, 0, +1) rv2 = batoid.intersect(sum, rv.copy()) np.testing.assert_equal(rv2.failed, np.array([False]))
def test_refraction_chromatic(): import random random.seed(577215664) wavelength1 = 500e-9 wavelength2 = 600e-9 flux = 1.0 plane = batoid.Plane() filename = os.path.join(batoid.datadir, "media", "silica_dispersion.txt") wave, n = np.genfromtxt(filename).T wave *= 1e-6 # micron -> meters table = batoid.Table(wave, n, batoid.Table.Interpolant.linear) silica = batoid.TableMedium(table) air = batoid.Air() thx, thy = 0.001, 0.0001 dirCos = batoid.utils.gnomonicToDirCos(thx, thy) rv1 = batoid.rayGrid(10.0, 1., dirCos[0], dirCos[1], -dirCos[2], 2, wavelength1, flux, silica) rv2 = batoid.rayGrid(10.0, 1., dirCos[0], dirCos[1], -dirCos[2], 2, wavelength2, flux, silica) rays = [] for ray in rv1: rays.append(ray) for ray in rv2: rays.append(ray) rvCombined = batoid.RayVector(rays) rv1r = plane.refract(rv1, silica, air) rv2r = plane.refract(rv2, silica, air) assert rv1r != rv2r rays = [] for ray in rv1r: rays.append(ray) for ray in rv2r: rays.append(ray) rvrCombined1 = batoid.RayVector(rays) rvrCombined2 = plane.refract(rvCombined, silica, air) assert rvrCombined1 == rvrCombined2 # Check in-place plane.refractInPlace(rv1, silica, air) plane.refractInPlace(rv2, silica, air) assert rv1 != rv2 plane.refractInPlace(rvCombined, silica, air) rays = [] for ray in rv1: rays.append(ray) for ray in rv2: rays.append(ray) rvCombined2 = batoid.RayVector(rays) assert rvCombined == rvCombined2
def test_fail(): plane = batoid.Plane() # Only fail if vz == 0 rv = batoid.RayVector(0, 0, 0, 0, 1, 0) rv2 = batoid.intersect(plane, rv.copy()) np.testing.assert_equal(rv2.failed, np.array([True])) # Otherwise, succeeds rv = batoid.RayVector(0, 0, 0, 0, 0, -1) rv2 = batoid.intersect(plane, rv.copy()) np.testing.assert_equal(rv2.failed, np.array([False]))
def test_fail(): plane = batoid.Plane() assert plane.allowReverse == False ray = batoid.Ray([0, 0, -1], [0, 0, -1]) ray = plane.intersect(ray) assert ray.failed ray = batoid.Ray([0, 0, -1], [0, 0, -1]) plane.intersectInPlace(ray) assert ray.failed # These should succeed though if allowReverse is True plane = batoid.Plane(allowReverse=True) assert plane.allowReverse == True ray = batoid.Ray([0, 0, -1], [0, 0, -1]) ray = plane.intersect(ray) assert not ray.failed ray = batoid.Ray([0, 0, -1], [0, 0, -1]) plane.intersectInPlace(ray) assert not ray.failed
def test_asPolar(): rng = np.random.default_rng(5772156) for _ in range(10): backDist = rng.uniform(9.0, 11.0) wavelength = rng.uniform(300e-9, 1100e-9) inner = rng.uniform(1.0, 3.0) outer = inner + rng.uniform(1.0, 3.0) nrad = rng.integers(1, 11) naz = rng.integers(10, 21) 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)) rays = batoid.RayVector.asPolar(backDist=backDist, wavelength=wavelength, outer=outer, inner=inner, nrad=nrad, naz=naz, 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]) assert len(rays) % 6 == 0 # If we set inner=0, then last ray should # intersect the center of the pupil inner = 0.0 rays = batoid.RayVector.asPolar(backDist=backDist, wavelength=wavelength, outer=outer, inner=inner, nrad=nrad, naz=naz, dirCos=dirCos) assert len(rays) % 6 == 1 pupil = batoid.Plane() pupil.intersect(rays) np.testing.assert_allclose(rays.x[-1], 0, atol=1e-14) np.testing.assert_allclose(rays.y[-1], 0, atol=1e-14) np.testing.assert_allclose(rays.z[-1], 0, atol=1e-14)
def test_RVasPolar(): for _ in range(10): backDist = np.random.uniform(9.0, 11.0) wavelength = np.random.uniform(300e-9, 1100e-9) inner = np.random.uniform(1.0, 3.0) outer = inner + np.random.uniform(1.0, 3.0) nrad = np.random.randint(1, 10) naz = np.random.randint(10, 20) dirCos = np.array([ np.random.uniform(-0.1, 0.1), np.random.uniform(-0.1, 0.1), np.random.uniform(-1.2, -0.8), ]) dirCos /= np.sqrt(np.dot(dirCos, dirCos)) rays = batoid.RayVector.asPolar(backDist=backDist, wavelength=wavelength, outer=outer, inner=inner, nrad=nrad, naz=naz, 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]) assert len(rays) % 6 == 0 # If we set inner=0, then last ray should # intersect the center of the pupil inner = 0.0 rays = batoid.RayVector.asPolar(backDist=backDist, wavelength=wavelength, outer=outer, inner=inner, nrad=nrad, naz=naz, dirCos=dirCos) assert len(rays) % 6 == 1 # Check distribution of points propagated to entrance pupil pupil = batoid.Plane() pupil.intersect(rays) np.testing.assert_allclose(rays[len(rays) - 1].x, 0, atol=1e-14) np.testing.assert_allclose(rays[len(rays) - 1].y, 0, atol=1e-14) np.testing.assert_allclose(rays[len(rays) - 1].z, 0, atol=1e-14)
def test_sag(): rng = np.random.default_rng(5) for i in range(100): plane = batoid.Plane() for j in range(10): x = rng.normal() y = rng.normal() result = plane.sag(x, y) np.testing.assert_equal(result, 0.0) # Check that it returned a scalar float and not an array assert isinstance(result, float) # Check vectorization x = rng.normal(size=(10, 10)) y = rng.normal(size=(10, 10)) np.testing.assert_allclose(plane.sag(x, y), 0.0) # Make sure non-unit stride arrays also work np.testing.assert_allclose(plane.sag(x[::5, ::2], y[::5, ::2]), 0.0) do_pickle(plane)
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)
def test_normal(): rng = np.random.default_rng(57) for i in range(100): plane = batoid.Plane() for j in range(10): x = rng.normal() y = rng.normal() result = plane.normal(x, y) np.testing.assert_equal(result, np.array([0., 0., 1.])) # Check vectorization x = rng.normal(size=(10, 10)) y = rng.normal(size=(10, 10)) np.testing.assert_allclose( plane.normal(x, y), np.broadcast_to(np.array([0., 0., 1.]), (10, 10, 3))) # Make sure non-unit stride arrays also work np.testing.assert_allclose( plane.normal(x[::5, ::2], y[::5, ::2]), np.broadcast_to(np.array([0., 0., 1.]), (2, 5, 3)))
def test_intersect(): import random random.seed(577) for i in range(100): plane = batoid.Plane() for j in range(10): x = random.gauss(0.0, 1.0) y = random.gauss(0.0, 1.0) # If we shoot rays straight up, then it's easy to predict the # intersection points. r0 = batoid.Ray((x, y, -1000), (0, 0, 1), 0) r = plane.intersect(r0) np.testing.assert_allclose(r.r[0], x) np.testing.assert_allclose(r.r[1], y) np.testing.assert_allclose(r.r[2], plane.sag(x, y), rtol=0, atol=1e-9)
def test_sag(): import random random.seed(57) for i in range(100): plane = batoid.Plane() for j in range(10): x = random.gauss(0.0, 1.0) y = random.gauss(0.0, 1.0) result = plane.sag(x, y) np.testing.assert_allclose(result, 0.0) # Check that it returned a scalar float and not an array assert isinstance(result, float) # Check vectorization x = np.random.normal(0.0, 1.0, size=(10, 10)) y = np.random.normal(0.0, 1.0, size=(10, 10)) np.testing.assert_allclose(plane.sag(x, y), 0.0) # Make sure non-unit stride arrays also work np.testing.assert_allclose(plane.sag(x[::5, ::2], y[::5, ::2]), 0.0) do_pickle(plane)
def test_intersect_vectorized(): import random random.seed(5772) r0s = [ batoid.Ray([ random.gauss(0.0, 0.1), random.gauss(0.0, 0.1), random.gauss(10.0, 0.1) ], [ random.gauss(0.0, 0.1), random.gauss(0.0, 0.1), random.gauss(-1.0, 0.1) ], random.gauss(0.0, 0.1)) for i in range(1000) ] r0s = batoid.RayVector(r0s) for i in range(100): plane = batoid.Plane() r1s = plane.intersect(r0s) r2s = batoid.RayVector([plane.intersect(r0) for r0 in r0s]) assert r1s == r2s
def test_add_plane(): rng = np.random.default_rng(5772156) for _ in range(100): # Adding a plane should have zero effect on sag or normal vector s1 = batoid.Sphere(rng.uniform(1, 3)) s2 = batoid.Plane() sum = batoid.Sum([s1, s2]) x = rng.normal(size=5000) y = rng.normal(size=5000) np.testing.assert_allclose(sum.sag(x, y), s1.sag(x, y), rtol=0, atol=1e-12) np.testing.assert_allclose( sum.normal(x, y), s1.normal(x, y), rtol=0, atol=1e-12, )
def test_ne(): objs = [ batoid.Mirror(batoid.Plane()), batoid.Detector(batoid.Plane()), batoid.Baffle(batoid.Plane()), batoid.RefractiveInterface(batoid.Plane()), batoid.Mirror(batoid.Paraboloid(0.1)), batoid.Detector(batoid.Paraboloid(0.1)), batoid.Baffle(batoid.Paraboloid(0.1)), batoid.RefractiveInterface(batoid.Paraboloid(0.1)), batoid.Mirror(batoid.Plane(), obscuration=batoid.ObscCircle(0.1)), batoid.Mirror(batoid.Plane(), inMedium=batoid.ConstMedium(1.1)), batoid.Mirror(batoid.Plane(), outMedium=batoid.ConstMedium(1.1)), batoid.Mirror(batoid.Plane(), coordSys=batoid.CoordSys([0, 0, 1])), batoid.CompoundOptic( [batoid.Mirror(batoid.Plane()), batoid.Mirror(batoid.Plane())]), batoid.CompoundOptic( [batoid.Mirror(batoid.Plane()), batoid.Baffle(batoid.Plane())]), batoid.CompoundOptic([ batoid.RefractiveInterface(batoid.Plane()), batoid.RefractiveInterface(batoid.Plane()) ]), batoid.Lens([ batoid.RefractiveInterface(batoid.Plane()), batoid.RefractiveInterface(batoid.Plane()) ]), ] all_obj_diff(objs)
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()