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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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 )
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)
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 )
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)
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)
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 )
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_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 )
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"
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)
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)
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)
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 )
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