def test_lsst_y_focus(): # Check that applying reasonable focus depth (from O'Connor++06) indeed leads to smaller spot # size for LSST y-band. rng = galsim.BaseDeviate(9876543210) bandpass = galsim.Bandpass("LSST_y.dat", wave_type='nm') sed = galsim.SED("1", wave_type='nm', flux_type='flambda') obj = galsim.Gaussian(fwhm=1e-5) oversampling = 32 photon_ops0 = [ galsim.WavelengthSampler(sed, bandpass, rng=rng), galsim.FRatioAngles(1.234, 0.606, rng=rng), galsim.FocusDepth(0.0), galsim.Refraction(3.9) ] img0 = obj.drawImage( sensor=galsim.SiliconSensor(), method='phot', n_photons=100000, photon_ops=photon_ops0, scale=0.2/oversampling, nx=32*oversampling, ny=32*oversampling, rng=rng ) T0 = img0.calculateMomentRadius() T0 *= 10*oversampling/0.2 # arcsec => microns # O'Connor finds minimum spot size when the focus depth is ~ -12 microns. Our sensor isn't # necessarily the same as the one there though; our minimum seems to be around -6 microns. # That could be due to differences in the design of the sensor though. We just use -6 microns # here, which is still useful to test the sign of the `depth` parameter and the interaction of # the 4 different surface operators required to produce this effect, and is roughly consistent # with O'Connor. depth1 = -6. # microns, negative means surface is intrafocal depth1 /= 10 # microns => pixels photon_ops1 = [ galsim.WavelengthSampler(sed, bandpass, rng=rng), galsim.FRatioAngles(1.234, 0.606, rng=rng), galsim.FocusDepth(depth1), galsim.Refraction(3.9) ] img1 = obj.drawImage( sensor=galsim.SiliconSensor(), method='phot', n_photons=100000, photon_ops=photon_ops1, scale=0.2/oversampling, nx=32*oversampling, ny=32*oversampling, rng=rng ) T1 = img1.calculateMomentRadius() T1 *= 10*oversampling/0.2 # arcsec => microns np.testing.assert_array_less(T1, T0)
depths = np.linspace(-25, 25, 41) # microns obj = galsim.Gaussian(sigma=1e-4) sed = galsim.SED("1", wave_type='nm', flux_type='flambda') fig, ax = plt.subplots() oversampling = 16 for filter in ['g', 'z', 'y']: bandpass = galsim.Bandpass("LSST_{}.dat".format(filter), wave_type='nm') Ts = [] for depth in depths: depth_pix = depth / 10 surface_ops = [ galsim.WavelengthSampler(sed, bandpass, rng=bd), galsim.FRatioAngles(1.234, 0.606, rng=bd), galsim.FocusDepth(depth_pix), galsim.Refraction(3.9) # approx number for Silicon ] img = obj.drawImage( sensor=galsim.SiliconSensor(), method='phot', n_photons=1_000_000, surface_ops=surface_ops, scale=0.2 / oversampling, # oversample pixels to better resolve PSF size nx=32 * oversampling, # 6.4 arcsec stamp ny=32 * oversampling, ) Ts.append(img.calculateMomentRadius()) Ts = np.array(Ts) / 0.2 * 10 * oversampling # convert arcsec -> micron ax.scatter(depths, Ts, label=filter) ax.set_ylim(2, 7)
def test_refract(): ud = galsim.UniformDeviate(57721) for _ in range(1000): photon_array = galsim.PhotonArray(1000, flux=1) ud.generate(photon_array.dxdz) ud.generate(photon_array.dydz) photon_array.dxdz *= 1.2 # -0.6 to 0.6 photon_array.dydz *= 1.2 photon_array.dxdz -= 0.6 photon_array.dydz -= 0.6 # copy for testing later dxdz0 = np.array(photon_array.dxdz) dydz0 = np.array(photon_array.dydz) index_ratio = ud() * 4 + 0.25 # 0.25 to 4.25 refract = galsim.Refraction(index_ratio) refract.applyTo(photon_array) # Triangle is length 1 in the z direction and length sqrt(dxdz**2+dydz**2) # in the 'r' direction. rsqr0 = dxdz0**2 + dydz0**2 sintheta0 = np.sqrt(rsqr0) / np.sqrt(1 + rsqr0) # See if total internal reflection applies w = sintheta0 < index_ratio np.testing.assert_array_equal(photon_array.dxdz[~w], np.nan) np.testing.assert_array_equal(photon_array.dydz[~w], np.nan) np.testing.assert_array_equal(photon_array.flux, np.where(w, 1.0, 0.0)) sintheta0 = sintheta0[w] dxdz0 = dxdz0[w] dydz0 = dydz0[w] dxdz1 = photon_array.dxdz[w] dydz1 = photon_array.dydz[w] rsqr1 = dxdz1**2 + dydz1**2 sintheta1 = np.sqrt(rsqr1) / np.sqrt(1 + rsqr1) # Check Snell's law np.testing.assert_allclose(sintheta0, index_ratio * sintheta1) # Check azimuthal angle stays constant phi0 = np.arctan2(dydz0, dxdz0) phi1 = np.arctan2(dydz1, dxdz1) np.testing.assert_allclose(phi0, phi1) # Check plane of refraction is perpendicular to (0,0,1) np.testing.assert_allclose(np.dot( np.cross( np.stack([dxdz0, dydz0, -np.ones(len(dxdz0))], axis=1), np.stack([dxdz1, dydz1, -np.ones(len(dxdz1))], axis=1), ), [0, 0, 1]), 0.0, rtol=0, atol=1e-13) # Try a wavelength dependent index_ratio index_ratio = lambda w: np.where(w < 1, 1.1, 2.2) photon_array = galsim.PhotonArray(100) ud.generate(photon_array.wavelength) ud.generate(photon_array.dxdz) ud.generate(photon_array.dydz) photon_array.dxdz *= 1.2 # -0.6 to 0.6 photon_array.dydz *= 1.2 photon_array.dxdz -= 0.6 photon_array.dydz -= 0.6 photon_array.wavelength *= 2 # 0 to 2 dxdz0 = photon_array.dxdz.copy() dydz0 = photon_array.dydz.copy() refract_func = galsim.Refraction(index_ratio=index_ratio) refract_func.applyTo(photon_array) dxdz_func = photon_array.dxdz.copy() dydz_func = photon_array.dydz.copy() photon_array.dxdz = dxdz0.copy() photon_array.dydz = dydz0.copy() refract11 = galsim.Refraction(index_ratio=1.1) refract11.applyTo(photon_array) dxdz11 = photon_array.dxdz.copy() dydz11 = photon_array.dydz.copy() photon_array.dxdz = dxdz0.copy() photon_array.dydz = dydz0.copy() refract22 = galsim.Refraction(index_ratio=2.2) refract22.applyTo(photon_array) dxdz22 = photon_array.dxdz.copy() dydz22 = photon_array.dydz.copy() w = photon_array.wavelength < 1 np.testing.assert_allclose(dxdz_func, np.where(w, dxdz11, dxdz22)) np.testing.assert_allclose(dydz_func, np.where(w, dydz11, dydz22))