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_propagate(): rng = np.random.default_rng(577) size = 10_000 x = rng.uniform(-1, 1, size=size) y = rng.uniform(-1, 1, size=size) z = rng.uniform(-0.1, 0.1, size=size) vx = rng.uniform(-0.05, 0.05, size=size) vy = rng.uniform(-0.05, 0.05, size=size) vz = np.sqrt(1.0 - vx * vx - vy * vy) # Try with default t=0 first rv = batoid.RayVector(x, y, z, vx, vy, vz) for t1 in [0.0, 1.0, -1.1, 2.5]: rvcopy = rv.copy() r1 = rv.positionAtTime(t1) rvcopy.propagate(t1) np.testing.assert_equal(rvcopy.r, r1) np.testing.assert_equal(rvcopy.v, rv.v) np.testing.assert_equal(rvcopy.t, t1) # Now add some random t's t = rng.uniform(-1.0, 1.0, size=size) rv = batoid.RayVector(x, y, z, vx, vy, vz, t) for t1 in [0.0, 1.0, -1.1, 2.5]: rvcopy = rv.copy() r1 = rv.positionAtTime(t1) rvcopy.propagate(t1) np.testing.assert_equal(rvcopy.r, r1) np.testing.assert_equal(rvcopy.v, rv.v) np.testing.assert_equal(rvcopy.t, t1)
def test_intersect_vectorized(): np.random.seed(57721) jmaxmax = 50 r0s = [ batoid.Ray([ np.random.normal(0.0, 0.1), np.random.normal(0.0, 0.1), np.random.normal(10.0, 0.1) ], [ np.random.normal(0.0, 0.1), np.random.normal(0.0, 0.1), np.random.normal(-1.0, 0.1) ], np.random.normal(0.0, 0.1)) for i in range(100) ] r0s = batoid.RayVector(r0s) for i in range(100): jmax = np.random.randint(1, jmaxmax) coef = np.random.normal(size=jmax + 1) * 1e-3 R_outer = np.random.uniform(0.5, 5.0) R_inner = np.random.uniform(0.0, 0.8 * R_outer) zernike = batoid.Zernike(coef, R_outer=R_outer, R_inner=R_inner) r1s = zernike.intersect(r0s) r2s = batoid.RayVector([zernike.intersect(r0) for r0 in r0s]) assert r1s == r2s
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): R = random.gauss(25.0, 0.2) conic = random.uniform(-2.0, 1.0) ncoefs = random.randint(0, 4) coefs = [random.gauss(0, 1e-10) for i in range(ncoefs)] asphere = batoid.Asphere(R, conic, coefs) r1s = asphere.intersect(r0s) r2s = batoid.RayVector([asphere.intersect(r0) for r0 in r0s]) np.testing.assert_allclose(asphere.sag(r1s.x, r1s.y), r1s.z, rtol=0, atol=1e-12) assert r1s == r2s
def test_ObscCircle(): rng = np.random.default_rng(5) size = 10_000 for i in range(100): cx = rng.normal(0.0, 1.0) cy = rng.normal(0.0, 1.0) r = rng.uniform(0.5, 1.5) obsc = batoid.ObscCircle(r, cx, cy) for i in range(100): x = rng.normal(0.0, 1.0) y = rng.normal(0.0, 1.0) assert obsc.contains(x, y) == (np.hypot(x - cx, y - cy) <= r) x = rng.normal(0.0, 1.0, size=size) y = rng.normal(0.0, 1.0, size=size) np.testing.assert_array_equal(obsc.contains(x, y), np.hypot(x - cx, y - cy) <= r) do_pickle(obsc) rv = batoid.RayVector(x, y, 0.0, 0.0, 0.0, 0.0) batoid.obscure(obsc, rv) np.testing.assert_array_equal(obsc.contains(x, y), rv.vignetted) # Check method syntax too rv = batoid.RayVector(x, y, 0.0, 0.0, 0.0, 0.0) obsc.obscure(rv) np.testing.assert_array_equal(obsc.contains(x, y), rv.vignetted)
def test_ne(): objs = [ batoid.Ray((0, 0, 0), (0, 0, 0)), batoid.Ray((0, 0, 0), (0, 0, 0), coordSys=batoid.CoordSys((0, 0, 1))), batoid.Ray((0, 0, 1), (0, 0, 0)), batoid.Ray((0, 1, 0), (0, 0, 0)), batoid.Ray((0, 0, 0), (0, 0, 0), t=1), batoid.Ray((0, 0, 0), (0, 0, 0), wavelength=500e-9), batoid.Ray((0, 0, 0), (0, 0, 0), wavelength=500e-9, flux=1.2), batoid.Ray((0, 0, 0), (0, 0, 0), vignetted=True), batoid.Ray(failed=True), (0, 0, 0), batoid.RayVector([ batoid.Ray((0, 0, 1), (0, 0, 0)), batoid.Ray((0, 0, 0), (0, 0, 0)) ]), batoid.RayVector([ batoid.Ray((0, 0, 1), (0, 0, 0), coordSys=batoid.CoordSys((0, 0, 1))), batoid.Ray((0, 0, 0), (0, 0, 0), coordSys=batoid.CoordSys((0, 0, 1))) ]), batoid.RayVector([ batoid.Ray((0, 0, 0), (0, 0, 0)), batoid.Ray((0, 0, 1), (0, 0, 0)) ]), batoid.RayVector([batoid.Ray((0, 0, 0), (0, 0, 0))]) ] all_obj_diff(objs)
def test_fail(): quad = batoid.Quadric(1.0, 0.0) rv = batoid.RayVector(0, 10, 0, 0, 0, -1) # Too far to side rv2 = batoid.intersect(quad, 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(quad, rv.copy()) np.testing.assert_equal(rv2.failed, np.array([False]))
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_fail(): asphere = batoid.Asphere(1.0, 0.0, []) rv = batoid.RayVector(0, 10, 0, 0, 0, -1) # Too far to the side rv2 = batoid.intersect(asphere, 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(asphere, rv.copy()) np.testing.assert_equal(rv2.failed, np.array([False]))
def test_fail(): zernike = batoid.Zernike([0,0,0,0,1]) # basically a paraboloid rv = batoid.RayVector(0, 0, -10, 0, 0.99, np.sqrt(1-0.99**2)) # Too shallow rv2 = batoid.intersect(zernike, 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(zernike, rv.copy()) np.testing.assert_equal(rv2.failed, np.array([False]))
def test_equals(): import time rng = np.random.default_rng(577215) size = 10_000 x = rng.uniform(-1, 1, size=size) y = rng.uniform(-1, 1, size=size) z = rng.uniform(-0.1, 0.1, size=size) vx = rng.uniform(-0.05, 0.05, size=size) vy = rng.uniform(-0.05, 0.05, size=size) vz = np.sqrt(1.0 - vx * vx - vy * vy) t = rng.uniform(-1.0, 1.0, size=size) wavelength = rng.uniform(300e-9, 1100e-9, size=size) flux = rng.uniform(0.9, 1.1, size=size) vignetted = rng.choice([True, False], size=size) failed = rng.choice([True, False], size=size) args = x, y, z, vx, vy, vz, t, wavelength, flux, vignetted, failed rv = batoid.RayVector(*args) rv2 = rv.copy() assert rv == rv2 for i in range(len(args)): newargs = [args[i].copy() for i in range(len(args))] ai = newargs[i] if ai.dtype == float: ai[0] = 1.2 + ai[0] * 3.45 elif ai.dtype == bool: ai[0] = not ai[0] # else panic! rv2 = batoid.RayVector(*newargs) assert rv != rv2 # Repeat, but force comparison on device rv2 = rv.copy() rv._rv.x.syncToDevice() rv._rv.y.syncToDevice() rv._rv.z.syncToDevice() rv._rv.vx.syncToDevice() rv._rv.vy.syncToDevice() rv._rv.vz.syncToDevice() rv._rv.t.syncToDevice() rv._rv.wavelength.syncToDevice() rv._rv.flux.syncToDevice() rv._rv.vignetted.syncToDevice() rv._rv.failed.syncToDevice() assert rv == rv2 for i in range(len(args)): newargs = [args[i].copy() for i in range(len(args))] ai = newargs[i] if ai.dtype == float: ai[0] = 1.2 + ai[0] * 3.45 elif ai.dtype == bool: ai[0] = not ai[0] # else panic! rv2 = batoid.RayVector(*newargs) assert rv != rv2
def test_fail(): para = batoid.Paraboloid(1.0) rv = batoid.RayVector(0, 0, -10, 0, 0.99, np.sqrt(1 - 0.99**2)) # Too shallow rv2 = batoid.intersect(para, 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(para, 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(): tilted = batoid.Tilted(0, 0) # Fail if vz == 0 rv = batoid.RayVector(0, 0, 0, 0, 1, 0) rv2 = batoid.intersect(tilted, 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(tilted, rv.copy()) np.testing.assert_equal(rv2.failed, np.array([False]))
def test_fail(): xs = np.linspace(-1, 1, 10) ys = np.linspace(-1, 1, 10) def f(x, y): return x+y zs = f(*np.meshgrid(xs, ys)) bc = batoid.Bicubic(xs, ys, zs) rv = batoid.RayVector(0, 10, 0, 0, 0, -1) # Too far to side rv2 = batoid.intersect(bc, rv.copy()) np.testing.assert_equal(rv2.failed, np.array([True])) # This one passes rv = batoid.RayVector(0, 0, 0, 0, 0, -1) rv2 = batoid.intersect(bc, rv.copy()) np.testing.assert_equal(rv2.failed, np.array([False]))
def test_traceReverse(): if __name__ == '__main__': nside = 128 else: nside = 32 fn = os.path.join(batoid.datadir, "HSC", "HSC.yaml") config = yaml.load(open(fn)) telescope = batoid.parse.parse_optic(config['opticalSystem']) init_rays = batoid.rayGrid(20, 12.0, 0.005, 0.005, -1.0, nside, 500e-9, 1.0, batoid.ConstMedium(1.0)) forward_rays, _ = telescope.trace(init_rays, outCoordSys=batoid.CoordSys()) # Now, turn the result rays around and trace backwards forward_rays = forward_rays.propagatedToTime(40.0) reverse_rays = batoid.RayVector( [batoid.Ray(r.r, -r.v, -r.t, r.wavelength) for r in forward_rays]) final_rays, _ = telescope.traceReverse(reverse_rays, outCoordSys=batoid.CoordSys()) # propagate all the way to t=0 final_rays = final_rays.propagatedToTime(0.0) w = np.where(np.logical_not(final_rays.vignetted))[0] for idx in w: np.testing.assert_allclose(init_rays[idx].x, final_rays[idx].x) np.testing.assert_allclose(init_rays[idx].y, final_rays[idx].y) np.testing.assert_allclose(init_rays[idx].z, final_rays[idx].z) np.testing.assert_allclose(init_rays[idx].vx, -final_rays[idx].vx) np.testing.assert_allclose(init_rays[idx].vy, -final_rays[idx].vy) np.testing.assert_allclose(init_rays[idx].vz, -final_rays[idx].vz) np.testing.assert_allclose(final_rays[idx].t, 0)
def test_optic(): if __name__ == '__main__': nside = 128 else: nside = 32 rays = batoid.rayGrid(20, 12.0, 0.005, 0.005, -1.0, nside, 500e-9, 1.0, batoid.ConstMedium(1.0)) nrays = len(rays) print("Tracing {} rays.".format(nrays)) t_fast = 0.0 t_slow = 0.0 fn = os.path.join(batoid.datadir, "HSC", "HSC.yaml") config = yaml.load(open(fn)) telescope = batoid.parse.parse_optic(config['opticalSystem']) do_pickle(telescope) t0 = time.time() rays_fast, _ = telescope.trace(rays) t1 = time.time() rays_slow = batoid.RayVector([telescope.trace(r)[0] for r in rays]) t2 = time.time() assert rays_fast == rays_slow t_fast = t1 - t0 t_slow = t2 - t1 print("Fast trace: {:5.3f} s".format(t_fast)) print(" {} rays per second".format(int(nrays / t_fast))) print("Slow trace: {:5.3f} s".format(t_slow)) print(" {} rays per second".format(int(nrays / t_slow)))
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_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_sumAmplitude(): import time rng = np.random.default_rng(57721) size = 10_000 for n in [1.0, 1.3]: x = rng.uniform(-1, 1, size=size) y = rng.uniform(-1, 1, size=size) z = rng.uniform(-0.1, 0.1, size=size) vx = rng.uniform(-0.05, 0.05, size=size) vy = rng.uniform(-0.05, 0.05, size=size) vz = np.sqrt(1.0/(n*n) - vx*vx - vy*vy) t = rng.uniform(-1.0, 1.0, size=size) wavelength = rng.uniform(300e-9, 1100e-9, size=size) rv = batoid.RayVector(x, y, z, vx, vy, vz, t, wavelength) satime = 0 atime = 0 for r1, t1 in [ ((0, 0, 0), 0), ((0, 1, 2), 3), ((-1, 2, 4), -1), ((0, 1, -4), -2) ]: at0 = time.time() s1 = rv.sumAmplitude(r1, t1) at1 = time.time() s2 = np.sum(rv.amplitude(r1, t1)) at2 = time.time() np.testing.assert_allclose(s1, s2, rtol=0, atol=1e-11) satime += at1-at0 atime += at2-at1
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_traceReverse(): if __name__ == '__main__': nside = 128 else: nside = 32 telescope = batoid.Optic.fromYaml("HSC.yaml") init_rays = batoid.rayGrid(20, 12.0, 0.005, 0.005, -1.0, nside, 500e-9, 1.0, batoid.ConstMedium(1.0)) forward_rays = telescope.trace(init_rays) # Now, turn the result rays around and trace backwards forward_rays = forward_rays.propagatedToTime(40.0) reverse_rays = batoid.RayVector( [batoid.Ray(r.r, -r.v, -r.t, r.wavelength) for r in forward_rays] ) final_rays = telescope.traceReverse(reverse_rays) # propagate all the way to t=0 final_rays = final_rays.propagatedToTime(0.0) final_rays.toCoordSysInPlace(batoid.globalCoordSys) w = np.where(np.logical_not(final_rays.vignetted))[0] for idx in w: np.testing.assert_allclose(init_rays[idx].x, final_rays[idx].x) np.testing.assert_allclose(init_rays[idx].y, final_rays[idx].y) np.testing.assert_allclose(init_rays[idx].z, final_rays[idx].z) np.testing.assert_allclose(init_rays[idx].vx, -final_rays[idx].vx) np.testing.assert_allclose(init_rays[idx].vy, -final_rays[idx].vy) np.testing.assert_allclose(init_rays[idx].vz, -final_rays[idx].vz) np.testing.assert_allclose(final_rays[idx].t, 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)
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_intersect(): rng = np.random.default_rng(577) tanx = rng.uniform(-0.1, 0.1) tany = rng.uniform(-0.1, 0.1) size = 10_000 tiltedCoordSys = batoid.CoordSys(origin=[0, 0, -1]) tilted = 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) # 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(tilted, rv.copy(), tiltedCoordSys) assert rv2.coordSys == tiltedCoordSys 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, tilted.sag(x, y) - 1, rtol=0, atol=1e-12) # Check default intersect coordTransform rv2 = rv.copy().toCoordSys(tiltedCoordSys) batoid.intersect(tilted, rv2) assert rv2.coordSys == tiltedCoordSys 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, tilted.sag(x, y) - 1, rtol=0, atol=1e-12)
def test_ObscAnnulus(): rng = np.random.default_rng(57) size = 10_000 for i in range(100): cx = rng.normal(0.0, 1.0) cy = rng.normal(0.0, 1.0) inner = rng.uniform(0.5, 1.5) outer = rng.uniform(1.6, 1.9) obsc = batoid.ObscAnnulus(inner, outer, cx, cy) for i in range(100): x = rng.normal(0.0, 1.0) y = rng.normal(0.0, 1.0) assert obsc.contains( x, y) == (inner <= np.hypot(x - cx, y - cy) < outer) x = rng.normal(0.0, 1.0, size=size) y = rng.normal(0.0, 1.0, size=size) r = np.hypot(x - cx, y - cy) np.testing.assert_array_equal(obsc.contains(x, y), (inner <= r) & (r < outer)) do_pickle(obsc) rv = batoid.RayVector(x, y, 0.0, 0.0, 0.0, 0.0) batoid.obscure(obsc, rv) np.testing.assert_array_equal(obsc.contains(x, y), rv.vignetted)
def test_optic(): if __name__ == '__main__': nside = 128 else: nside = 32 rays = batoid.rayGrid(20, 12.0, 0.005, 0.005, -1.0, nside, 500e-9, 1.0, batoid.ConstMedium(1.0)) nrays = len(rays) print("Tracing {} rays.".format(nrays)) t_fast = 0.0 t_slow = 0.0 telescope = batoid.Optic.fromYaml("HSC.yaml") do_pickle(telescope) t0 = time.time() rays_fast = telescope.trace(rays) t1 = time.time() rays_slow = batoid.RayVector([telescope.trace(r) for r in rays]) t2 = time.time() assert rays_fast == rays_slow t_fast = t1 - t0 t_slow = t2 - t1 print("Fast trace: {:5.3f} s".format(t_fast)) print(" {} rays per second".format(int(nrays/t_fast))) print("Slow trace: {:5.3f} s".format(t_slow)) print(" {} rays per second".format(int(nrays/t_slow)))
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)