def test_ne(): objs = [ batoid.Sphere(1.0), batoid.Sphere(2.0), batoid.Plane() ] all_obj_diff(objs)
def test_sag(): np.random.seed(57) for _ in range(100): s1 = batoid.Sphere(np.random.uniform(1, 3)) s2 = batoid.Paraboloid(np.random.uniform(1, 3)) 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) + s2.sag(x, y), rtol=1e-12, atol=1e-12 ) s3 = batoid.Quadric(np.random.uniform(3, 5), np.random.uniform(-0.1, 0.1)) sum2 = batoid.Sum([s1, s2, s3]) np.testing.assert_allclose( sum2.sag(x, y), s1.sag(x, y) + s2.sag(x, y) + s3.sag(x, y), rtol=1e-12, 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_fail(): surface = batoid.Sphere(1.0) ray = batoid.Ray([0, 10, -1], [0, 0, 1]) ray = surface.intersect(ray) assert ray.failed do_pickle(ray)
def test_properties(): np.random.seed(5) for _ in range(100): s1 = batoid.Sphere(np.random.uniform(1, 3)) s2 = batoid.Paraboloid(np.random.uniform(1, 3)) sum = batoid.Sum([s1, s2]) do_pickle(sum) # check commutativity assert sum == batoid.Sum([s2, s1]) # order of sum.surfaces is not guaranteed assert s1 in sum.surfaces assert s2 in sum.surfaces s3 = batoid.Quadric(np.random.uniform(3, 5), np.random.uniform(-0.1, 0.1)) sum2 = batoid.Sum([s1, s2, s3]) do_pickle(sum2) # check commutativity assert sum2 == batoid.Sum([s2, s3, s1]) assert sum2 == batoid.Sum([s3, s1, s2]) assert sum2 == batoid.Sum([s3, s2, s1]) assert sum2 == batoid.Sum([s2, s1, s3]) assert sum2 == batoid.Sum([s1, s3, s2]) assert s1 in sum2.surfaces assert s2 in sum2.surfaces assert s3 in sum2.surfaces do_pickle(sum)
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_properties(): rng = np.random.default_rng(5) for i in range(100): R = rng.normal(0.0, 0.3) # negative allowed sphere = batoid.Sphere(R) assert sphere.R == R do_pickle(sphere)
def test_normal(): rng = np.random.default_rng(577) for i in range(100): R = 1. / rng.normal(0.0, 0.3) sphere = batoid.Sphere(R) for j in range(10): x = rng.uniform(-0.7 * abs(R), 0.7 * abs(R)) y = rng.uniform(-0.7 * abs(R), 0.7 * abs(R)) result = sphere.normal(x, y) r = np.hypot(x, y) rat = r / R dzdr = rat / np.sqrt(1 - rat * rat) nz = 1 / np.sqrt(1 + dzdr * dzdr) normal = np.array([-x / r * dzdr * nz, -y / r * dzdr * nz, nz]) np.testing.assert_allclose(result, normal) # Check 0,0 np.testing.assert_equal(sphere.normal(0, 0), np.array([0, 0, 1])) # Check vectorization x = rng.uniform(-0.7 * abs(R), 0.7 * abs(R), size=(10, 10)) y = rng.uniform(-0.7 * abs(R), 0.7 * abs(R), size=(10, 10)) r = np.hypot(x, y) rat = r / R dzdr = rat / np.sqrt(1 - rat * rat) nz = 1 / np.sqrt(1 + dzdr * dzdr) normal = np.dstack([-x / r * dzdr * nz, -y / r * dzdr * nz, nz]) np.testing.assert_allclose(sphere.normal(x, y), normal) # Make sure non-unit stride arrays also work np.testing.assert_allclose(sphere.normal(x[::5, ::2], y[::5, ::2]), normal[::5, ::2])
def test_sag(): rng = np.random.default_rng(57) for i in range(100): R = 1. / rng.normal(0.0, 0.3) sphere = batoid.Sphere(R) for j in range(10): x = rng.uniform(-0.7 * abs(R), 0.7 * abs(R)) y = rng.uniform(-0.7 * abs(R), 0.7 * abs(R)) result = sphere.sag(x, y) np.testing.assert_allclose( result, R * (1 - np.sqrt(1.0 - (x * x + y * y) / R / R))) # Check that it returned a scalar float and not an array assert isinstance(result, float) # Check 0,0 np.testing.assert_allclose(sphere.sag(0, 0), 0.0, rtol=0, atol=1e-17) # Check vectorization x = rng.uniform(-0.7 * abs(R), 0.7 * abs(R), size=(10, 10)) y = rng.uniform(-0.7 * abs(R), 0.7 * abs(R), size=(10, 10)) np.testing.assert_allclose( sphere.sag(x, y), R * (1 - np.sqrt(1.0 - (x * x + y * y) / R / R))) # Make sure non-unit stride arrays also work np.testing.assert_allclose( sphere.sag(x[::5, ::2], y[::5, ::2]), R * (1 - np.sqrt(1.0 - (x * x + y * y) / R / R))[::5, ::2]) do_pickle(sphere)
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) sphere = batoid.Sphere(R) x = rng.uniform(-0.3 * abs(R), 0.3 * abs(R), size=size) y = rng.uniform(-0.3 * abs(R), 0.3 * abs(R), size=size) z = np.full_like(x, -2 * abs(R)) 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(sphere, rv.copy()) rvr2 = sphere.reflect(rv.copy()) rays_allclose(rvr, rvr2) # print(f"{np.sum(rvr.failed)/len(rvr)*100:.2f}% failed") normal = sphere.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(): 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_ne(): objs = [ batoid.Quadric(10.0, 1.0), batoid.Quadric(11.0, 1.0), batoid.Quadric(10.0, 1.1), batoid.Sphere(10.0) ] all_obj_diff(objs)
def test_properties(): import random random.seed(5) for i in range(100): R = random.gauss(0.7, 0.8) sphere = batoid.Sphere(R) assert sphere.R == R do_pickle(sphere)
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(): sphere = batoid.Sphere(1.0) ray = batoid.Ray([0,0,-1], [0,0,-1]) ray = sphere.intersect(ray) assert ray.failed ray = batoid.Ray([0,0,-1], [0,0,-1]) sphere.intersect(ray) assert ray.failed
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 wavefront(optic, theta_x, theta_y, wavelength, nx=32, sphereRadius=None): """Compute wavefront. Parameters ---------- optic : batoid.Optic Optic for which to compute wavefront. theta_x, theta_y : float Field of incoming rays (gnomic projection) wavelength : float Wavelength of incoming rays nx : int, optional Size of ray grid to generate to compute wavefront. Default: 32 sphereRadius : float, optional Radius of reference sphere in meters. If None, then use optic.sphereRadius. Returns ------- wavefront : batoid.Lattice A batoid.Lattice object containing the wavefront values in waves and the primitive lattice vectors of the entrance pupil grid in meters. """ dirCos = gnomicToDirCos(theta_x, theta_y) rays = batoid.rayGrid( optic.dist, optic.pupilSize, dirCos[0], dirCos[1], -dirCos[2], nx, wavelength, 1.0, optic.inMedium ) if sphereRadius is None: sphereRadius = optic.sphereRadius outCoordSys = batoid.CoordSys() optic.traceInPlace(rays, outCoordSys=outCoordSys) w = np.where(1-rays.vignetted)[0] point = np.mean(rays.r[w], axis=0) # We want to place the vertex of the reference sphere one radius length away from the # intersection point. So transform our rays into that coordinate system. transform = batoid.CoordTransform( outCoordSys, batoid.CoordSys(point+np.array([0,0,sphereRadius]))) transform.applyForwardInPlace(rays) sphere = batoid.Sphere(-sphereRadius) sphere.intersectInPlace(rays) w = np.where(1-rays.vignetted)[0] # Should potentially try to make the reference time w.r.t. the chief ray instead of the mean # of the good (unvignetted) rays. t0 = np.mean(rays.t[w]) arr = np.ma.masked_array((t0-rays.t)/wavelength, mask=rays.vignetted).reshape(nx, nx) primitiveVectors = np.vstack([[optic.pupilSize/nx, 0], [0, optic.pupilSize/nx]]) return batoid.Lattice(arr, primitiveVectors)
def test_intersect(): import random random.seed(577) for i in range(100): R = random.gauss(10.0, 0.1) sphere = batoid.Sphere(R) 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 = sphere.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], sphere.sag(x, y), rtol=0, atol=1e-9) # Check normal for R=0 paraboloid (a plane) sphere = batoid.Sphere(0.0) np.testing.assert_array_equal(sphere.normal(0.1,0.1), [0,0,1])
def test_reflect(): rng = np.random.default_rng(577) for i in range(1000): R = rng.uniform(1.0, 2.0) sphere = batoid.Sphere(R) reflectivity = rng.uniform(0, 1) transmissivity = rng.uniform(0, 1) coating = batoid.SimpleCoating(reflectivity, transmissivity) rv = batoid.RayVector(0, 0, 10, 0, 0, -1, 0, 500e-9, 1.0) sphere.reflect(rv, coating=coating) assert (rv.flux[0] == reflectivity)
def wavefront(optic, wavelength, theta_x=0, theta_y=0, nx=32, rays=None, saveRays=False, sphereRadius=None): if rays is None: xcos = np.sin(theta_x) ycos = np.sin(theta_y) zcos = -np.sqrt(1.0 - xcos**2 - ycos**2) rays = batoid.rayGrid(optic.dist, optic.pupilSize, xcos, ycos, zcos, nx, wavelength, optic.inMedium) if saveRays: rays = batoid.RayVector(rays) if sphereRadius is None: sphereRadius = optic.sphereRadius outCoordSys = batoid.CoordSys() optic.traceInPlace(rays, outCoordSys=outCoordSys) goodRays = batoid._batoid.trimVignetted(rays) point = np.array( [np.mean(goodRays.x), np.mean(goodRays.y), np.mean(goodRays.z)]) # We want to place the vertex of the reference sphere one radius length away from the # intersection point. So transform our rays into that coordinate system. transform = batoid.CoordTransform( outCoordSys, batoid.CoordSys(point + np.array([0, 0, sphereRadius]))) transform.applyForwardInPlace(rays) sphere = batoid.Sphere(-sphereRadius) sphere.intersectInPlace(rays) goodRays = batoid._batoid.trimVignetted(rays) # Should potentially try to make the reference time w.r.t. the chief ray instead of the mean # of the good (unvignetted) rays. t0 = np.mean(goodRays.t0) ts = rays.t0[:] isV = rays.isVignetted[:] ts -= t0 ts /= wavelength wf = np.ma.masked_array(ts, mask=isV) return wf
def test_sphere(): rng = np.random.default_rng(5772156) size = 1000 for i in range(100): R = 1/rng.normal(0.0, 0.3) conic = 0.0 quad = batoid.Quadric(R, conic) sphere = batoid.Sphere(R) x = rng.uniform(-0.7*R, 0.7*R, size=size) y = rng.uniform(-0.7*R, 0.7*R, size=size) np.testing.assert_allclose( quad.sag(x,y), sphere.sag(x, y), rtol=0, atol=1e-11 ) np.testing.assert_allclose( quad.normal(x,y), sphere.normal(x, y), rtol=0, atol=1e-11 )
def test_refract(): rng = np.random.default_rng(5772) for i in range(1000): R = rng.uniform(1.0, 2.0) sphere = batoid.Sphere(R) reflectivity = rng.uniform(0, 1) transmissivity = rng.uniform(0, 1) coating = batoid.SimpleCoating(reflectivity, transmissivity) m1 = batoid.ConstMedium(rng.uniform(1.1, 1.2)) m2 = batoid.ConstMedium(rng.uniform(1.2, 1.3)) rv = batoid.RayVector(0, 0, 10, 0, 0, -1, 0, 500e-9, 1.0) sphere.refract(rv, m1, m2, coating=coating) assert (rv.flux[0] == transmissivity)
def test_properties(): rng = np.random.default_rng(5) for i in range(100): s1 = batoid.Sphere(rng.uniform(1, 3)) s2 = batoid.Paraboloid(rng.uniform(1, 3)) sum = batoid.Sum([s1, s2]) do_pickle(sum) assert s1 is sum.surfaces[0] assert s2 is sum.surfaces[1] s3 = batoid.Quadric(rng.uniform(3, 5), rng.uniform(-0.1, 0.1)) sum2 = batoid.Sum([s1, s2, s3]) do_pickle(sum2) assert s1 is sum2.surfaces[0] assert s2 is sum2.surfaces[1] assert s3 is sum2.surfaces[2]
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(0.05, 0.01) sphere = batoid.Sphere(R) r1s = sphere.intersect(r0s.copy()) r2s = batoid.RayVector([sphere.intersect(r0.copy()) for r0 in r0s]) assert r1s == r2s
def test_properties(): np.random.seed(5) for _ in range(100): s1 = batoid.Sphere(np.random.uniform(1, 3)) s2 = batoid.Paraboloid(np.random.uniform(1, 3)) sum = batoid.Sum([s1, s2]) do_pickle(sum) assert s1 is sum.surfaces[0] assert s2 is sum.surfaces[1] s3 = batoid.Quadric(np.random.uniform(3, 5), np.random.uniform(-0.1, 0.1)) sum2 = batoid.Sum([s1, s2, s3]) do_pickle(sum2) assert s1 is sum2.surfaces[0] assert s2 is sum2.surfaces[1] assert s3 is sum2.surfaces[2] do_pickle(sum)
def test_sag(): import random random.seed(57) for i in range(100): R = random.gauss(4.2, 0.3) sphere = batoid.Sphere(R) for j in range(10): x = random.uniform(-0.7*R, 0.7*R) y = random.uniform(-0.7*R, 0.7*R) result = sphere.sag(x, y) np.testing.assert_allclose(result, R*(1-math.sqrt(1.0-(x*x + y*y)/R/R))) # Check that it returned a scalar float and not an array assert isinstance(result, float) # Check vectorization x = np.random.uniform(-0.7*R, 0.7*R, size=(10, 10)) y = np.random.uniform(-0.7*R, 0.7*R, size=(10, 10)) np.testing.assert_allclose(sphere.sag(x, y), R*(1-np.sqrt(1.0-(x*x + y*y)/R/R))) # Make sure non-unit stride arrays also work np.testing.assert_allclose( sphere.sag(x[::5,::2], y[::5,::2]), (R*(1-np.sqrt(1.0-(x*x + y*y)/R/R)))[::5, ::2] )
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_intersect(): rng = np.random.default_rng(5772) 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]) sumCoordSys = batoid.CoordSys(origin=[0, 0, -1]) x = rng.uniform(-1, 1, size=size) y = rng.uniform(-1, 1, size=size) z = np.full_like(x, -10.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, -10.0) rv2 = batoid.intersect(sum, rv.copy(), sumCoordSys) assert rv2.coordSys == sumCoordSys 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, sum.sag(x, y) - 1, rtol=0, atol=1e-12) # Check default intersect coordTransform rv2 = rv.copy().toCoordSys(sumCoordSys) batoid.intersect(sum, rv2) assert rv2.coordSys == sumCoordSys 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, sum.sag(x, y) - 1, rtol=0, atol=1e-12)
def test_intersect(): rng = np.random.default_rng(5772) size = 10_000 for i in range(100): R = 1. / rng.normal(0.0, 0.3) sphereCoordSys = batoid.CoordSys(origin=[0, 0, -1]) sphere = batoid.Sphere(R) x = rng.uniform(-0.3 * abs(R), 0.3 * abs(R), size=size) y = rng.uniform(-0.3 * abs(R), 0.3 * abs(R), size=size) z = np.full_like(x, -2 * abs(R)) # 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, -2 * abs(R)) rv2 = batoid.intersect(sphere, rv.copy(), sphereCoordSys) assert rv2.coordSys == sphereCoordSys 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, sphere.sag(x, y) - 1, rtol=0, atol=1e-9) # Check default intersect coordTransform rv2 = rv.copy().toCoordSys(sphereCoordSys) batoid.intersect(sphere, rv2) assert rv2.coordSys == sphereCoordSys 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, sphere.sag(x, y) - 1, rtol=0, atol=1e-9)