def traceSplitReverse(self, r, inCoordSys=globalCoordSys, forwardCoordSys=None, reverseCoordSys=None, minFlux=1e-3, verbose=False): if verbose: strtemplate = "traceSplitReverse {:15s} flux = {:18.8f} nphot = {:10d}" print(strtemplate.format(self.name, np.sum(r.flux), len(r))) if self.skip: return r, None, inCoordSys, None if forwardCoordSys is None: forwardCoordSys = self.items[-1].coordSys if reverseCoordSys is None: reverseCoordSys = self.items[0].coordSys workQueue = [(r, inCoordSys, "reverse", len(self.items)-1)] outRForward = [] outRReverse = [] while workQueue: rays, inCoordSys, direction, itemIndex = workQueue.pop() item = self.items[itemIndex] if direction == "forward": rForward, rReverse, tmpForwardCoordSys, tmpReverseCoordSys = \ item.traceSplit(rays, inCoordSys, minFlux=minFlux, verbose=verbose) elif direction == "reverse": rForward, rReverse, tmpForwardCoordSys, tmpReverseCoordSys = \ item.traceSplitReverse(rays, inCoordSys, minFlux=minFlux, verbose=verbose) else: raise RuntimeError("Shouldn't get here!") rForward.trimVignettedInPlace(minFlux) rReverse.trimVignettedInPlace(minFlux) if itemIndex == 0: if len(rReverse) > 0: if tmpReverseCoordSys != reverseCoordSys: transform = batoid.CoordTransform(tmpReverseCoordSys, reverseCoordSys) transform.applyForwardInPlace(rReverse) outRReverse.append(rReverse) else: if len(rReverse) > 0: workQueue.append((rReverse, tmpReverseCoordSys, "reverse", itemIndex-1)) if itemIndex == len(self.items)-1: if len(rForward) > 0: if tmpForwardCoordSys != forwardCoordSys: transform = batoid.CoordTransform(tmpForwardCoordSys, forwardCoordSys) transform.applyForwardInPlace(rForward) outRForward.append(rForward) else: if len(rForward) > 1: workQueue.append((rForward, tmpForwardCoordSys, "forward", itemIndex+1)) rForward = batoid.concatenateRayVectors(outRForward) rReverse = batoid.concatenateRayVectors(outRReverse) return rForward, rReverse, forwardCoordSys, reverseCoordSys
def test_RayVector(): import random random.seed(5772) rayList = [] for i in range(1000): rayList.append( batoid.Ray( random.gauss(0.0, 1.0), # x random.gauss(0.0, 1.0), # y random.gauss(0.0, 1.0), # z random.gauss(0.0, 1.0), # vx random.gauss(0.0, 1.0), # vy random.gauss(0.0, 1.0), # vz random.gauss(0.0, 1.0), # t0 random.gauss(1000.0, 1.0), # wavelength random.gauss(100.0, 1.0), # flux random.choice([True, False]) # vignetted )) rayVector = batoid.RayVector(rayList) assert rayVector.monochromatic == False do_pickle(rayVector) np.testing.assert_equal(rayVector.x, np.array([ray.x for ray in rayVector])) np.testing.assert_equal(rayVector.y, np.array([ray.y for ray in rayVector])) np.testing.assert_equal(rayVector.z, np.array([ray.z for ray in rayVector])) np.testing.assert_equal(rayVector.vx, np.array([ray.vx for ray in rayVector])) np.testing.assert_equal(rayVector.vy, np.array([ray.vy for ray in rayVector])) np.testing.assert_equal(rayVector.vz, np.array([ray.vz for ray in rayVector])) np.testing.assert_equal(rayVector.t, np.array([ray.t for ray in rayVector])) np.testing.assert_equal(rayVector.wavelength, np.array([ray.wavelength for ray in rayVector])) np.testing.assert_equal(rayVector.flux, np.array([ray.flux for ray in rayVector])) np.testing.assert_equal(rayVector.vignetted, np.array([ray.vignetted for ray in rayVector])) np.testing.assert_equal(rayVector.failed, np.array([ray.failed for ray in rayVector])) np.testing.assert_equal( rayVector.phase([1, 2, 3], 4.0), np.array([ray.phase([1, 2, 3], 4.0) for ray in rayVector])) np.testing.assert_equal( rayVector.amplitude([1, 2, 3], 4.0), np.array([ray.amplitude([1, 2, 3], 4.0) for ray in rayVector])) np.testing.assert_equal( rayVector.v, np.array([[ray.vx, ray.vy, ray.vz] for ray in rayVector])) np.testing.assert_equal( rayVector.r, np.array([[ray.x, ray.y, ray.z] for ray in rayVector])) np.testing.assert_equal(rayVector.k, np.array([ray.k for ray in rayVector])) np.testing.assert_equal(rayVector.omega, np.array([ray.omega for ray in rayVector])) np.testing.assert_equal(rayVector.kx, np.array([ray.kx for ray in rayVector])) np.testing.assert_equal(rayVector.ky, np.array([ray.ky for ray in rayVector])) np.testing.assert_equal(rayVector.kz, np.array([ray.kz for ray in rayVector])) # Try the other ctor rayVector2 = batoid.RayVector( np.array([ray.x for ray in rayList]), np.array([ray.y for ray in rayList]), np.array([ray.z for ray in rayList]), np.array([ray.vx for ray in rayList]), np.array([ray.vy for ray in rayList]), np.array([ray.vz for ray in rayList]), np.array([ray.t for ray in rayList]), np.array([ray.wavelength for ray in rayList]), np.array([ray.flux for ray in rayList]), np.array([ray.vignetted for ray in rayList])) assert rayVector == rayVector2 assert rayVector2.monochromatic == False # See if we can make monochromatic True rayVector3 = batoid.RayVector(np.array([ray.x for ray in rayList]), np.array([ray.y for ray in rayList]), np.array([ray.z for ray in rayList]), np.array([ray.vx for ray in rayList]), np.array([ray.vy for ray in rayList]), np.array([ray.vz for ray in rayList]), np.array([ray.t for ray in rayList]), np.array([1.0 for ray in rayList]), np.array([ray.flux for ray in rayList]), np.array([ray.vignetted for ray in rayList])) assert rayVector3.monochromatic == True # Make sure we really got a view and not a copy x = rayVector.x x[0] += 1 assert np.all(x == rayVector.x) assert not rayVector.x.flags.owndata # What about lifetimes? What happens to x if rayVector disappears? x2 = np.copy(x) assert x is not x2 del rayVector assert np.all(x == x2) # it survives! # Test concatenateRayVectors rv1 = batoid.RayVector(rayList[0:5]) rv2 = batoid.RayVector(rayList[5:10]) rv3 = batoid.RayVector(rayList[10:12]) rv4 = batoid.RayVector(rayList[12:40]) rvA = batoid.concatenateRayVectors([rv1, rv2, rv3, rv4]) rvB = batoid.RayVector(rayList[0:40]) assert rvA == rvB
def drdth(optic, theta_x, theta_y, wavelength, nrad=6, naz=36, projection='postel'): """Calculate derivative of focal plane coord with respect to field angle. Parameters ---------- optic : batoid.Optic Optical system theta_x, theta_y : float Field angle in radians wavelength : float Wavelength in meters nrad : int, optional Number of ray radii to use. (see RayVector.asPolar()) naz : int, optional Approximate number of azimuthal angles in outermost ring. (see RayVector.asPolar()) projection : {'postel', 'zemax', 'gnomonic', 'stereographic', 'lambert', 'orthographic'} Projection used to convert field angle to direction cosines. Returns ------- drdth : (2, 2), ndarray Jacobian transformation matrix for converting between (theta_x, theta_y) and (x, y) on the focal plane. Notes ----- This is the Jacobian of pixels -> tangent plane, (and importantly, not pixels -> ra/dec). It should be *close* to the inverse plate scale though, especially near the center of the tangent plane projection. """ # We just use a finite difference approach here. dth = 1e-5 # Make direction cosine vectors nominalCos = fieldToDirCos(theta_x, theta_y, projection=projection) dthxCos = fieldToDirCos(theta_x + dth, theta_y, projection=projection) dthyCos = fieldToDirCos(theta_x, theta_y + dth, projection=projection) rays = batoid.RayVector.asPolar(optic=optic, wavelength=wavelength, dirCos=nominalCos, nrad=nrad, naz=naz) rays_x = batoid.RayVector.asPolar(optic=optic, wavelength=wavelength, dirCos=dthxCos, nrad=nrad, naz=naz) rays_y = batoid.RayVector.asPolar(optic=optic, wavelength=wavelength, dirCos=dthyCos, nrad=nrad, naz=naz) # Faster to concatenate and trace all at once rays_c = batoid.concatenateRayVectors([rays, rays_x, rays_y]) optic.trace(rays_c) n = len(rays) rays = rays_c[:n] rays_x = rays_c[n:2 * n] rays_y = rays_c[2 * n:3 * n] w = ~rays.vignetted mx = np.mean(rays.x[w]) my = np.mean(rays.y[w]) # meters / radian drx_dthx = (np.mean(rays_x.x[w]) - mx) / dth drx_dthy = (np.mean(rays_y.x[w]) - mx) / dth dry_dthx = (np.mean(rays_x.y[w]) - my) / dth dry_dthy = (np.mean(rays_y.y[w]) - my) / dth return np.array([[drx_dthx, drx_dthy], [dry_dthx, dry_dthy]])