Ejemplo n.º 1
0
def test_sk_shoot():
    """Test SecondKick with photon shooting.  Particularly the flux of the final image.
    """
    rng = galsim.BaseDeviate(1234)
    obj = galsim.SecondKick(lam=500, r0=0.2, diam=4, flux=1.e4)
    im = galsim.Image(500, 500, scale=1)
    im.setCenter(0, 0)
    added_flux, photons = obj.drawPhot(im,
                                       poisson_flux=False,
                                       rng=rng.duplicate())
    print('obj.flux = ', obj.flux)
    print('added_flux = ', added_flux)
    print('photon fluxes = ', photons.flux.min(), '..', photons.flux.max())
    print('image flux = ', im.array.sum())
    assert np.isclose(added_flux, obj.flux)
    assert np.isclose(im.array.sum(), obj.flux)
    photons2 = obj.makePhot(poisson_flux=False, rng=rng.duplicate())
    assert photons2 == photons, "SecondKick makePhot not equivalent to drawPhot"

    # Can treat the profile as a convolution of a delta function and put it in a photon_ops list.
    delta = galsim.DeltaFunction(flux=1.e4)
    psf = galsim.SecondKick(lam=500, r0=0.2, diam=4)
    photons3 = delta.makePhot(poisson_flux=False,
                              rng=rng.duplicate(),
                              photon_ops=[psf])
    np.testing.assert_allclose(photons3.x, photons.x)
    np.testing.assert_allclose(photons3.y, photons.y)
    np.testing.assert_allclose(photons3.flux, photons.flux)
Ejemplo n.º 2
0
def test_sk_scale():
    """Test sk scale argument"""
    kwargs = {
        'lam': 500,
        'r0': 0.2,
        'diam': 4.0,
        'flux': 2.2,
        'obscuration': 0.3
    }
    sk_arcsec = galsim.SecondKick(scale_unit=galsim.arcsec, **kwargs)
    sk_arcmin = galsim.SecondKick(scale_unit='arcmin', **kwargs)
    do_pickle(sk_arcsec)
    do_pickle(sk_arcmin)

    np.testing.assert_almost_equal(sk_arcsec.flux, sk_arcmin.flux)
    np.testing.assert_almost_equal(sk_arcsec.kValue(0.0, 0.0),
                                   sk_arcmin.kValue(0.0, 0.0))
    np.testing.assert_almost_equal(sk_arcsec.kValue(0.0, 1.0),
                                   sk_arcmin.kValue(0.0, 60.0))
    np.testing.assert_almost_equal(sk_arcsec.kValue(0.0, 10.0),
                                   sk_arcmin.kValue(0.0, 600.0))
    np.testing.assert_almost_equal(
        sk_arcsec._sbs.xValue(galsim.PositionD(0.0, 6.0)._p),
        sk_arcmin._sbs.xValue(galsim.PositionD(0.0, 0.1)._p) / 60**2,
        decimal=5)
    np.testing.assert_almost_equal(
        sk_arcsec._sbs.xValue(galsim.PositionD(0.0, 0.6)._p),
        sk_arcmin._sbs.xValue(galsim.PositionD(0.0, 0.01)._p) / 60**2,
        decimal=5)
    np.testing.assert_almost_equal(
        sk_arcsec._sba.xValue(galsim.PositionD(0.0, 6.0)._p),
        sk_arcmin._sba.xValue(galsim.PositionD(0.0, 0.1)._p) / 60**2,
        decimal=5)
    np.testing.assert_almost_equal(
        sk_arcsec._sba.xValue(galsim.PositionD(0.0, 0.6)._p),
        sk_arcmin._sba.xValue(galsim.PositionD(0.0, 0.01)._p) / 60**2,
        decimal=5)

    if __name__ == '__main__':
        # These are a little slow, since the xValue is doing a real-space convolution integral.
        np.testing.assert_almost_equal(sk_arcsec.xValue(0.0, 6.0),
                                       sk_arcmin.xValue(0.0, 0.1) / 60**2,
                                       decimal=5)
        np.testing.assert_almost_equal(sk_arcsec.xValue(0.0, 1.2),
                                       sk_arcmin.xValue(0.0, 0.02) / 60**2,
                                       decimal=5)

    img1 = sk_arcsec.drawImage(nx=32, ny=32, scale=0.2)
    img2 = sk_arcmin.drawImage(nx=32, ny=32, scale=0.2 / 60.0)
    np.testing.assert_almost_equal(img1.array, img2.array)

    # Also check that default flux works
    del kwargs['flux']
    sk_arcsec = galsim.SecondKick(scale_unit=galsim.arcsec, **kwargs)
    sk_arcmin = galsim.SecondKick(scale_unit='arcmin', **kwargs)
    do_pickle(sk_arcsec)
    do_pickle(sk_arcmin)
    np.testing.assert_almost_equal(sk_arcmin.flux, 1.0)
    np.testing.assert_almost_equal(sk_arcsec.flux, 1.0)
Ejemplo n.º 3
0
def test_init():
    """Test generation of SecondKick profiles
    """
    obscuration = 0.5

    if __name__ == '__main__':
        lams = [300.0, 500.0, 1100.0]
        r0_500s = [0.1, 0.15, 0.3]
        kcrits = [0.1, 0.2, 0.4]
    else:
        lams = [500.0]
        r0_500s = [0.15]
        kcrits = [0.2]
    for lam in lams:
        for r0_500 in r0_500s:
            r0 = r0_500 * (lam / 500)**(6. / 5)
            for kcrit in kcrits:
                t0 = time.time()
                kwargs = {'lam': lam, 'r0': r0, 'kcrit': kcrit, 'diam': 4.0}
                print(kwargs)

                sk = galsim.SecondKick(flux=2.2, **kwargs)
                t1 = time.time()
                print('   stepk, maxk = ', sk.stepk, sk.maxk)
                np.testing.assert_almost_equal(sk.flux, 2.2)
                do_pickle(sk)
                t2 = time.time()

                gsp = galsim.GSParams(xvalue_accuracy=1.e-8,
                                      kvalue_accuracy=1.e-8)
                sk2 = galsim.SecondKick(flux=2.2, gsparams=gsp, **kwargs)
                assert sk2 != sk
                assert sk2 == sk.withGSParams(gsp)
                assert sk2 == sk.withGSParams(xvalue_accuracy=1.e-8,
                                              kvalue_accuracy=1.e-8)

                # Raw sk objects are hard to draw due to a large maxk/stepk ratio.
                # Decrease maxk by convolving in a smallish Gaussian.
                obj = galsim.Convolve(sk, galsim.Gaussian(fwhm=0.2))
                print('   obj stepk, maxk = ', obj.stepk, obj.maxk)
                check_basic(obj, "SecondKick")
                t3 = time.time()
                img = galsim.Image(16, 16, scale=0.2)
                do_shoot(obj, img, "SecondKick")
                t4 = time.time()
                do_kvalue(obj, img, "SecondKick")
                t5 = time.time()
                print(' times = ', t1 - t0, t2 - t1, t3 - t2, t4 - t3, t5 - t4)

                # Check negative flux
                sk2 = galsim.SecondKick(flux=-2.2, **kwargs)
                sk3 = sk.withFlux(-2.2)
                assert sk2 == sk3
                obj2 = galsim.Convolve(sk2, galsim.Gaussian(fwhm=0.2))
                check_basic(obj2, "SecondKick with negative flux")
Ejemplo n.º 4
0
def test_limiting_cases():
    """SecondKick has some two interesting limiting cases.
    A) When kcrit = 0, SecondKick = Convolve(Airy, VonKarman).
    B) When kcrit = inf, SecondKick = Airy
    Test these.
    """
    lam = 500.0
    r0 = 0.2
    diam = 8.36
    obscuration = 0.61

    # First kcrit=0
    sk = galsim.SecondKick(lam, r0, diam, obscuration, kcrit=0.0)
    limiting_case = galsim.Convolve(
        galsim.VonKarman(lam, r0, L0=1.e8),
        galsim.Airy(lam=lam, diam=diam, obscuration=obscuration)
    )
    print(sk.stepk, sk.maxk)
    print(limiting_case.stepk, limiting_case.maxk)

    for k in [0.0, 0.1, 0.3, 1.0, 3.0, 10.0, 20.0]:
        print(sk.kValue(0, k).real, limiting_case.kValue(0, k).real)
        np.testing.assert_allclose(
            sk.kValue(0, k).real,
            limiting_case.kValue(0, k).real,
            rtol=1e-3,
            atol=1e-4)

    # Normally, one wouldn't use SecondKick.xValue, since it does a real-space convolution,
    # so it's slow.  But we do allow it, so test it here.
    import time
    t0 = time.time()
    xv_2k = sk.xValue(0,0)
    print("xValue(0,0) = ",xv_2k)
    t1 = time.time()
    # The VonKarman * Airy xValue is much slower still, so don't do that.
    # Instead compare it to the 'sb' image.
    xv_image = limiting_case.drawImage(nx=1,ny=1,method='sb',scale=0.1)(1,1)
    print('from image ',xv_image)
    t2 = time.time()
    print('t = ',t1-t0, t2-t1)
    np.testing.assert_almost_equal(xv_2k, xv_image, decimal=3)

    # kcrit=inf
    sk = galsim.SecondKick(lam, r0, diam, obscuration, kcrit=np.inf)
    limiting_case = galsim.Airy(lam=lam, diam=diam, obscuration=obscuration)

    for k in [0.0, 0.1, 0.3, 1.0, 3.0, 10.0, 20.0]:
        print(sk.kValue(0, k).real, limiting_case.kValue(0, k).real)
        np.testing.assert_allclose(
            sk.kValue(0, k).real,
            limiting_case.kValue(0, k).real,
            rtol=1e-3,
            atol=1e-4)
Ejemplo n.º 5
0
def test_sk_ne():
    gsp = galsim.GSParams(maxk_threshold=1.1e-3, folding_threshold=5.1e-3)

    objs = [galsim.SecondKick(lam=500.0, r0=0.2, diam=4.0),
            galsim.SecondKick(lam=550.0, r0=0.2, diam=4.0),
            galsim.SecondKick(lam=500.0, r0=0.25, diam=4.0),
            galsim.SecondKick(lam=500.0, r0=0.25, diam=4.2),
            galsim.SecondKick(lam=500.0, r0=0.25, diam=4.2, obscuration=0.4),
            galsim.SecondKick(lam=500.0, r0=0.25, diam=4.2, kcrit=1.234),
            galsim.SecondKick(lam=500.0, r0=0.25, diam=4.2, flux=2.2),
            galsim.SecondKick(lam=500.0, r0=0.25, diam=4.2, scale_unit='arcmin'),
            galsim.SecondKick(lam=500.0, r0=0.2, diam=4.0, gsparams=gsp)]
    all_obj_diff(objs)
Ejemplo n.º 6
0
def test_sk_phase_psf():
    """Test that analytic second kick profile matches what can be obtained from PhaseScreenPSF with
    an appropriate truncated power spectrum.
    """
    # import matplotlib.pyplot as plt

    lam = 500.0
    r0 = 0.2
    diam = 4.0
    obscuration = 0.5

    rng = galsim.UniformDeviate(1234567890)
    weights = [0.652, 0.172, 0.055, 0.025, 0.074, 0.022]
    speed = [rng() * 20 for _ in range(6)]
    direction = [rng() * 360 * galsim.degrees for _ in range(6)]
    aper = galsim.Aperture(4.0, obscuration=obscuration, pad_factor=0.5)
    kcrits = [1, 3, 10] if __name__ == '__main__' else [1]
    for kcrit in kcrits:
        # Technically, we should probably use a smaller screen_scale here, but that runs really
        # slowly.  The below seems to work well enough for the tested kcrits.
        atm = galsim.Atmosphere(r0_500=r0,
                                r0_weights=weights,
                                rng=rng,
                                speed=speed,
                                direction=direction,
                                screen_size=102.4,
                                screen_scale=0.05,
                                suppress_warning=True)
        atm.instantiate(kmin=kcrit)
        print('instantiated atm')
        psf = galsim.PhaseScreenPSF(atm,
                                    lam=lam,
                                    exptime=10,
                                    aper=aper,
                                    time_step=0.1)
        print('made psf')
        phaseImg = psf.drawImage(nx=64, ny=64, scale=0.02)
        sk = galsim.SecondKick(lam=lam,
                               r0=r0,
                               diam=diam,
                               obscuration=obscuration,
                               kcrit=kcrit)
        print('made sk')
        skImg = sk.drawImage(nx=64, ny=64, scale=0.02)
        print('made skimg')
        phaseMom = galsim.hsm.FindAdaptiveMom(phaseImg)
        skMom = galsim.hsm.FindAdaptiveMom(skImg)

        print('moments: ', phaseMom.moments_sigma, skMom.moments_sigma)
        np.testing.assert_allclose(phaseMom.moments_sigma,
                                   skMom.moments_sigma,
                                   rtol=2e-2)
Ejemplo n.º 7
0
def test_sk_shoot():
    """Test SecondKick with photon shooting.  Particularly the flux of the final image.
    """
    rng = galsim.BaseDeviate(1234)
    obj = galsim.SecondKick(lam=500, r0=0.2, diam=4, flux=1.e4)
    im = galsim.Image(500, 500, scale=1)
    im.setCenter(0, 0)
    added_flux, photons = obj.drawPhot(im, poisson_flux=False, rng=rng)
    print('obj.flux = ', obj.flux)
    print('added_flux = ', added_flux)
    print('photon fluxes = ', photons.flux.min(), '..', photons.flux.max())
    print('image flux = ', im.array.sum())
    assert np.isclose(added_flux, obj.flux)
    assert np.isclose(im.array.sum(), obj.flux)
Ejemplo n.º 8
0
def test_structure_function():
    """Test that SecondKick structure function is equivalent to vonKarman structure function when
    kcrit=0.  This is nontrivial since the SecondKick structure function is numerically integrated,
    while the vK structure function is evaluated analytically.
    """
    lam = 500.0
    r0 = 0.2
    diam = 8.36
    obscuration = 0.61

    sk = galsim.SecondKick(lam, r0, diam, obscuration, kcrit=0.0)
    vk = galsim.VonKarman(lam, r0, L0=1.e10)

    for rho in [0.01, 0.03, 0.1, 0.3, 1.0, 3.0]:
        sksf = sk._structure_function(rho / r0)
        vksf = vk._structure_function(rho)
        print(sksf, vksf)
        np.testing.assert_allclose(sksf, vksf, rtol=2e-3, atol=1.e-3)
Ejemplo n.º 9
0
def make_plot(args):
    # Initiate some GalSim random number generators.
    rng = galsim.BaseDeviate(args.seed)
    u = galsim.UniformDeviate(rng)

    # The GalSim atmospheric simulation code describes turbulence in the 3D atmosphere as a series
    # of 2D turbulent screens.  The galsim.Atmosphere() helper function is useful for constructing
    # this screen list.

    # First, we estimate a weight for each screen, so that the turbulence is dominated by the lower
    # layers consistent with direct measurements.  The specific values we use are from SCIDAR
    # measurements on Cerro Pachon as part of the 1998 Gemini site selection process
    # (Ellerbroek 2002, JOSA Vol 19 No 9).

    Ellerbroek_alts = [0.0, 2.58, 5.16, 7.73, 12.89, 15.46]  # km
    Ellerbroek_weights = [0.652, 0.172, 0.055, 0.025, 0.074, 0.022]
    Ellerbroek_interp = galsim.LookupTable(Ellerbroek_alts, Ellerbroek_weights,
                                           interpolant='linear')

    # Use given number of uniformly spaced altitudes
    alts = np.max(Ellerbroek_alts)*np.arange(args.nlayers)/(args.nlayers-1)
    weights = Ellerbroek_interp(alts)  # interpolate the weights
    weights /= sum(weights)  # and renormalize

    # Each layer can have its own turbulence strength (roughly inversely proportional to the Fried
    # parameter r0), wind speed, wind direction, altitude, and even size and scale (though note that
    # the size of each screen is actually made infinite by "wrapping" the edges of the screen.)  The
    # galsim.Atmosphere helper function is useful for constructing this list, and requires lists of
    # parameters for the different layers.

    spd = []  # Wind speed in m/s
    dirn = [] # Wind direction in radians
    r0_500 = [] # Fried parameter in m at a wavelength of 500 nm.
    for i in range(args.nlayers):
        spd.append(u()*args.max_speed)  # Use a random speed between 0 and max_speed
        dirn.append(u()*360*galsim.degrees)  # And an isotropically distributed wind direction.
        # The turbulence strength of each layer is specified by through its Fried parameter r0_500,
        # which can be thought of as the diameter of a telescope for which atmospheric turbulence
        # and unaberrated diffraction contribute equally to image resolution (at a wavelength of
        # 500nm).  The weights above are for the refractive index structure function (similar to a
        # variance or covariance), however, so we need to use an appropriate scaling relation to
        # distribute the input "net" Fried parameter into a Fried parameter for each layer.  For
        # Kolmogorov turbulence, this is r0_500 ~ (structure function)**(-3/5):
        r0_500.append(args.r0_500*weights[i]**(-3./5))
        print("Adding layer at altitude {:5.2f} km with velocity ({:5.2f}, {:5.2f}) m/s, "
              "and r0_500 {:5.3f} m."
              .format(alts[i], spd[i]*dirn[i].cos(), spd[i]*dirn[i].sin(), r0_500[i]))

    # Apply fudge factor
    r0_500 = [r*args.turb_factor**(-3./5) for r in r0_500]

    # Make sure to use a consistent seed for the atmosphere when varying kcrit
    # Additionally, we set the screen size and scale.
    atmRng = galsim.BaseDeviate(args.seed+1)
    print("Inflating atmosphere")
    fftAtm = galsim.Atmosphere(r0_500=r0_500, L0=args.L0,
                               speed=spd, direction=dirn, altitude=alts, rng=atmRng,
                               screen_size=args.screen_size, screen_scale=args.screen_scale)
    with ProgressBar(args.nlayers) as bar:
        fftAtm.instantiate(_bar=bar)
    print(fftAtm[0].screen_scale, fftAtm[0].screen_size)
    print(fftAtm[0]._tab2d.f.shape)
    # `atm` is now an instance of a galsim.PhaseScreenList object.

    # Construct an Aperture object for computing the PSF.  The Aperture object describes the
    # illumination pattern of the telescope pupil, and chooses good sampling size and resolution
    # for representing this pattern as an array.
    aper = galsim.Aperture(diam=args.diam, lam=args.lam, obscuration=args.obscuration,
                           screen_list=fftAtm, pad_factor=args.pad_factor,
                           oversampling=args.oversampling)

    print("Drawing with Fourier optics")
    with ProgressBar(args.exptime/args.time_step) as bar:
        fftPSF = fftAtm.makePSF(lam=args.lam, aper=aper, exptime=args.exptime,
                                time_step=args.time_step, _bar=bar)
        fftImg = fftPSF.drawImage(nx=args.nx, ny=args.nx, scale=args.scale)

    fftMom = galsim.hsm.FindAdaptiveMom(fftImg)

    vk = galsim.Convolve(
        galsim.VonKarman(lam=args.lam, r0=args.r0_500*(args.lam/500.0)**(6./5), L0=args.L0),
        galsim.Airy(lam=args.lam, diam=args.diam, obscuration=args.obscuration)
    )
    vkImg = vk.drawImage(nx=args.nx, ny=args.nx, scale=args.scale)
    vkMom = galsim.hsm.FindAdaptiveMom(vkImg)

    # Start output at this point
    fig, axes = plt.subplots(nrows=5, ncols=4, figsize=(8, 8))
    FigureCanvasAgg(fig)
    for ax in axes.ravel():
        ax.set_xticks([])
        ax.set_yticks([])

    kcrits = np.logspace(np.log10(args.kmin), np.log10(args.kmax), 4)
    r0 = args.r0_500*(args.lam/500.0)**(6./5)
    for icol, kcrit in enumerate(kcrits):
        # reset atmRng
        atmRng = galsim.BaseDeviate(args.seed+1)
        print("Inflating atmosphere with kcrit={}".format(kcrit))
        atm = galsim.Atmosphere(r0_500=r0_500, L0=args.L0,
                                speed=spd, direction=dirn, altitude=alts, rng=atmRng,
                                screen_size=args.screen_size, screen_scale=args.screen_scale)
        with ProgressBar(args.nlayers) as bar:
            atm.instantiate(kmax=kcrit/r0, _bar=bar)
        kick1 = atm.makePSF(lam=args.lam, aper=aper, exptime=args.exptime,
                            time_step=args.time_step, second_kick=False)
        r0 = args.r0_500*(args.lam/500)**(6./5)
        kick2 = galsim.SecondKick(lam=args.lam, r0=r0, diam=args.diam, obscuration=args.obscuration,
                                  kcrit=kcrit)
        img1 = kick1.drawImage(nx=args.nx, ny=args.nx, scale=args.scale, method='phot',
                               n_photons=args.nphot)
        try:
            mom1 = galsim.hsm.FindAdaptiveMom(img1)
        except RuntimeError:
            mom1 = None
        img2 = kick2.drawImage(nx=args.nx, ny=args.nx, scale=args.scale, method='phot',
                               n_photons=args.nphot)
        try:
            mom2 = galsim.hsm.FindAdaptiveMom(img2)
        except RuntimeError:
            mom2 = None

        geom = galsim.Convolve(kick1, kick2)
        geomImg = geom.drawImage(nx=args.nx, ny=args.nx, scale=args.scale, method='phot',
                                 n_photons=args.nphot)
        try:
            geomMom = galsim.hsm.FindAdaptiveMom(geomImg)
        except RuntimeError:
            geomMom = None

        axes[0,icol].imshow(fftImg.array)
        axes[0,icol].text(0.5, 0.9, "{:6.3f}".format(fftMom.moments_sigma),
                          transform=axes[0,icol].transAxes, color='w')
        axes[1,icol].imshow(img1.array)
        if mom1:
            axes[1,icol].text(0.5, 0.9, "{:6.3f}".format(mom1.moments_sigma),
                              transform=axes[1,icol].transAxes, color='w')
        axes[2,icol].imshow(img2.array)
        if mom2:
            axes[2,icol].text(0.5, 0.9, "{:6.3f}".format(mom2.moments_sigma),
                              transform=axes[2,icol].transAxes, color='w')
        axes[3,icol].imshow(geomImg.array)
        if geomMom:
            axes[3,icol].text(0.5, 0.9, "{:6.3f}".format(geomMom.moments_sigma),
                              transform=axes[3,icol].transAxes, color='w')

        axes[4,icol].imshow(vkImg.array)
        axes[4,icol].text(0.5, 0.9, "{:6.3f}".format(vkMom.moments_sigma),
                          transform=axes[4,icol].transAxes, color='w')


        axes[0,icol].set_title("{:6.3f}".format(kcrit))


    axes[0, 0].set_ylabel("DFT")
    axes[1, 0].set_ylabel("1st kick")
    axes[2, 0].set_ylabel("2nd kick")
    axes[3, 0].set_ylabel("Geom")
    axes[4, 0].set_ylabel("Von Karman")

    fig.tight_layout()

    dirname, filename = os.path.split(args.outfile)
    if not os.path.exists(dirname):
        os.mkdir(dirname)
    fig.savefig(args.outfile)