Example #1
0
def test_SED_div():
    """Check that SEDs divide like I think they should...
    """
    a0 = galsim.SED(galsim.LookupTable([1,2,3,4,5], [1.1,2.2,3.3,4.4,5.5]),
                    wave_type='nm', flux_type='fphotons')
    for z in [0, 0.2, 0.4]:
        a = a0.atRedshift(z)

        # SED divided by function
        b = lambda w: w**2
        c = a/b
        x = 3.0
        np.testing.assert_almost_equal(c(x), a(x)/b(x), 10,
                                       err_msg="Found wrong value in SED.__div__")

        # SED divided by scalar
        d = a/4.2
        np.testing.assert_almost_equal(d(x), a(x)/4.2, 10,
                                       err_msg="Found wrong value in SED.__div__")
        do_pickle(d)

        # assignment division
        d /= 2
        np.testing.assert_almost_equal(d(x), a(x)/4.2/2, 10,
                                       err_msg="Found wrong value in SED.__div__")
        do_pickle(d)

        # SED divided by dimensionless SED
        e = galsim.SED('wave', 'nm', '1')
        d /= e
        np.testing.assert_almost_equal(d(x), a(x)/4.2/2/e(x), 10,
                                       err_msg="Found wrong value in SED.__div__")
Example #2
0
def test_SED_div():
    """Check that SEDs divide like I think they should...
    """
    a0 = galsim.SED(galsim.LookupTable([1,2,3,4,5], [1.1,2.2,3.3,4.4,5.5]),
                    wave_type='nm', flux_type='fphotons')
    for z in [0, 0.2, 0.4]:
        a = a0.atRedshift(z)

        # SED divided by function
        b = lambda w: w**2
        c = a/b
        x = 3.0
        np.testing.assert_almost_equal(c(x), a(x)/b(x), 10,
                                       err_msg="Found wrong value in SED.__div__")

        # SED divided by scalar
        d = a/4.2
        np.testing.assert_almost_equal(d(x), a(x)/4.2, 10,
                                       err_msg="Found wrong value in SED.__div__")
        do_pickle(d)

        # assignment division
        d /= 2
        np.testing.assert_almost_equal(d(x), a(x)/4.2/2, 10,
                                       err_msg="Found wrong value in SED.__div__")
        do_pickle(d)

        # SED divided by dimensionless SED
        e = galsim.SED('wave', 'nm', '1')
        d /= e
        np.testing.assert_almost_equal(d(x), a(x)/4.2/2/e(x), 10,
                                       err_msg="Found wrong value in SED.__div__")
Example #3
0
def test_stepk_maxk():
    """Test options to specify (or not) stepk and maxk.
    """
    aper = galsim.Aperture(diam=1.0)
    rng = galsim.BaseDeviate(123456)
    # Test frozen AtmosphericScreen first
    atm = galsim.Atmosphere(screen_size=30.0, altitude=10.0, speed=0.1, alpha=1.0, rng=rng)
    psf = galsim.PhaseScreenPSF(atm, 500.0, aper=aper, scale_unit=galsim.arcsec)
    stepk = psf.stepK()
    maxk = psf.maxK()

    psf2 = galsim.PhaseScreenPSF(atm, 500.0, aper=aper, scale_unit=galsim.arcsec,
                                 _force_stepk=stepk/1.5, _force_maxk=maxk*2.0)
    np.testing.assert_almost_equal(
            psf2.stepK(), stepk/1.5, decimal=7,
            err_msg="PhaseScreenPSF did not adopt forced value for stepK")
    np.testing.assert_almost_equal(
            psf2.maxK(), maxk*2.0, decimal=7,
            err_msg="PhaseScreenPSF did not adopt forced value for maxK")
    do_pickle(psf)
    do_pickle(psf2)

    # Try out non-geometric-shooting
    psf3 = atm.makePSF(lam=500.0, aper=aper, geometric_shooting=False)
    img = galsim.Image(32, 32, scale=0.2)
    do_shoot(psf3, img, "PhaseScreenPSF")
    # Also make sure a few other methods at least run
    psf3.centroid()
    psf3.maxSB()
Example #4
0
def test_aperture():
    """Test various ways to construct Apertures."""
    # Simple tests for constructing and pickling Apertures.
    aper1 = galsim.Aperture(diam=1.0)
    im = galsim.fits.read(os.path.join(imgdir, pp_file))
    aper2 = galsim.Aperture(diam=1.0, pupil_plane_im=im)
    do_pickle(aper1)
    do_pickle(aper2)
    # Automatically created Aperture should match one created via OpticalScreen
    aper1 = galsim.Aperture(diam=1.0)
    aper2 = galsim.Aperture(diam=1.0, lam=500, screen_list=[galsim.OpticalScreen(diam=1.0)])
    err_str = ("Aperture created implicitly using Airy does not match Aperture created using "
               "OpticalScreen.")
    assert aper1 == aper2, err_str
Example #5
0
def test_SED_basic():
    """Basic tests of SED functionality
    """
    c = constants.c.to('nm / s').value  # speed of light
    h = constants.h.to('erg s').value  # Planck's constant
    nm_w = np.arange(10, 1002, 10)
    A_w = np.arange(100, 10002, 100)

    try:
        # Eval-str must return a Real
        np.testing.assert_raises(ValueError,
                                 galsim.SED,
                                 spec="'eggs'",
                                 wave_type='A',
                                 flux_type='flambda')
    except ImportError:
        print('The assert_raises tests require nose')

    # All of these should be equivalent.  Flat spectrum with F_lambda = 200 erg/nm
    s_list = [
        galsim.SED(spec=lambda x: 200., flux_type='flambda', wave_type='nm'),
        galsim.SED(spec='200', flux_type='flambda', wave_type='nanometers'),
        galsim.SED('200', wave_type='nanometers', flux_type='flambda'),
        galsim.SED('200', 'nm', 'flambda'),
        galsim.SED('np.sqrt(4.e4)', 'nm', 'flambda'),
        galsim.SED('numpy.sqrt(4.e4)', 'nm', 'flambda'),
        galsim.SED('math.sqrt(4.e4)', 'nm', 'flambda'),
        # 200 erg/nm / 10 A/nm = 20 erg/A
        galsim.SED(spec='20', flux_type='flambda', wave_type='Angstroms'),
        # 200 erg/nm / (hc/w erg/photon) = 200 w/hc photons/nm
        galsim.SED(spec='200 * wave / %r' % (h * c),
                   wave_type='NANOmeters',
                   flux_type='fphotons'),
        # 200 erg/nm / (hc/w erg/photon) / 10 A/nm = 20 (w in A)/hc photons/A
        galsim.SED(spec='20 * (wave/10) / %r' % (h * c),
                   flux_type='fphotons',
                   wave_type='Ang'),
        # 200 erg/nm / (c/w^2 Hz/nm) = 200 w^2/c erg/Hz
        galsim.SED(spec='200 * wave**2 / %r' % c,
                   flux_type='fnu',
                   wave_type='nm'),
        galsim.SED(spec='200 * (wave/10)**2 / %r' % c,
                   flux_type='fnu',
                   wave_type='A'),
        galsim.SED(galsim.LookupTable([1, 1e3], [200, 200],
                                      interpolant='linear'),
                   wave_type='nanometers',
                   flux_type='flambda'),
        galsim.SED(galsim.LookupTable([1, 1e4], [20, 20],
                                      interpolant='linear'),
                   wave_type='ang',
                   flux_type='flambda'),
        galsim.SED(galsim.LookupTable([1, 1e3], [200 / (h * c), 2e5 / (h * c)],
                                      interpolant='linear'),
                   flux_type='fphotons',
                   wave_type='nm'),
        galsim.SED(galsim.LookupTable([1, 1e4], [2 / (h * c), 2e4 / (h * c)],
                                      interpolant='linear'),
                   flux_type='fphotons',
                   wave_type='A'),
        galsim.SED(galsim.LookupTable([1, 1e3], [200 / c, 2e8 / c],
                                      interpolant='linear',
                                      x_log=True,
                                      f_log=True),
                   flux_type='fnu',
                   wave_type='nanometers'),
        galsim.SED(galsim.LookupTable([1, 1e4], [2 / c, 2e8 / c],
                                      interpolant='linear',
                                      x_log=True,
                                      f_log=True),
                   flux_type='fnu',
                   wave_type='A'),
        galsim.SED(galsim.LookupTable(nm_w, 200. * np.ones(100)),
                   wave_type='nanometers',
                   flux_type='flambda'),
        galsim.SED(galsim.LookupTable(A_w, 20. * np.ones(100)),
                   flux_type='flambda',
                   wave_type='A'),
        galsim.SED(galsim.LookupTable(nm_w, 200. * nm_w / (h * c)),
                   flux_type='fphotons',
                   wave_type='nm'),
        galsim.SED(galsim.LookupTable(A_w, 2. * A_w / (h * c)),
                   flux_type='fphotons',
                   wave_type='A'),
        galsim.SED(galsim.LookupTable(nm_w, 200. * nm_w**2 / c),
                   flux_type='fnu',
                   wave_type='nanometers'),
        galsim.SED(galsim.LookupTable(A_w, 2. * A_w**2 / c),
                   flux_type='fnu',
                   wave_type='A'),
        galsim.SED(galsim.LookupTable(
            [1, 100 - 1.e-10, 100, 1000, 1000 + 1.e-10, 2000],
            [0., 0., 200., 200., 0., 0.],
            interpolant='linear'),
                   wave_type='nm',
                   flux_type='flambda')
    ]
    s_list += [
        s_list[9].thin(),
        s_list[10].thin(),
        s_list[11].thin(),
        s_list[12].thin(),
        s_list[13].thin(),
        s_list[14].thin(),
        s_list[15].thin(),
        s_list[15].thin(preserve_range=True),
        s_list[18].thin(),
        s_list[18].thin(preserve_range=True),
        s_list[21].thin(),
        s_list[21].thin(preserve_range=True),
        galsim.SED('1000', 'nm', 'flambda', redshift=4),
        galsim.SED('1000', 'nm', 'flambda').atRedshift(4.0),
    ]

    for k, s in enumerate(s_list):
        print(k, ' s = ', s)
        np.testing.assert_almost_equal(s(400) * h * c / 400, 200, decimal=10)
        np.testing.assert_almost_equal(s(900) * h * c / 900, 200, decimal=10)
        waves = np.arange(700, 800, 10)
        np.testing.assert_array_almost_equal(s(waves) * h * c / waves,
                                             200,
                                             decimal=10)

        if k < len(s_list) - 2:
            np.testing.assert_equal(s.redshift, 0.)
        else:
            np.testing.assert_almost_equal(s.redshift, 4.)

        # Only the first one is not picklable
        if k > 0:
            do_pickle(s, lambda x: (x(470), x(490), x(910)))
            do_pickle(s)
Example #6
0
def test_SED_mul():
    """Check that SEDs multiply like I think they should...
    """
    sed0 = galsim.SED(galsim.LookupTable([1, 2, 3, 4, 5],
                                         [1.1, 2.2, 3.3, 4.4, 5.5]),
                      wave_type='nm',
                      flux_type='fphotons')
    sed1 = galsim.SED(lambda nu: nu**2,
                      wave_type=units.Hz,
                      flux_type='fnu',
                      fast=False)
    sed2 = galsim.SED(17.0, wave_type='ang', flux_type='1')

    for sed, z in zip([sed0, sed1, sed2], [0, 0.2, 0.4]):
        a = sed.atRedshift(z)

        # SED multiplied by function
        b = lambda w: w**2
        c = a * b
        x = 3.0
        np.testing.assert_almost_equal(
            c(x), a(x) * b(x), 10, err_msg="Found wrong value in SED.__mul__")

        # function multiplied by SED
        c = b * a
        np.testing.assert_almost_equal(
            c(x), a(x) * b(x), 10, err_msg="Found wrong value in SED.__rmul__")

        # SED multiplied by scalar
        d = a * 4.2
        np.testing.assert_almost_equal(
            d(x), a(x) * 4.2, 10, err_msg="Found wrong value in SED.__mul__")
        if sed is sed0: do_pickle(d)

        # assignment multiplication
        d *= 2
        np.testing.assert_almost_equal(
            d(x),
            a(x) * 4.2 * 2,
            10,
            err_msg="Found wrong value in SED.__mul__")
        if sed is sed0: do_pickle(d)

        # SED multiplied by dimensionless, constant SED
        e = galsim.SED(2.0, 'nm', '1')
        f = a * e
        np.testing.assert_almost_equal(
            f(x), a(x) * e(x), 10, err_msg="Found wrong value in SED.__mul__")
        f = e * a
        np.testing.assert_almost_equal(
            f(x), e(x) * a(x), 10, err_msg="Found wrong value in SED.__mul__")

        # SED multiplied by dimensionless, non-constant SED
        g = galsim.SED('wave', 'nm', '1')
        h = a * g
        np.testing.assert_almost_equal(
            h(x), a(x) * g(x), 10, err_msg="Found wrong value in SED.__mul__")
        h2 = g * a
        np.testing.assert_almost_equal(
            h2(x), g(x) * a(x), 10, err_msg="Found wrong value in SED.__mul__")

    sed1 = galsim.SED('1', 'nm', 'fphotons', redshift=1)
    sed2 = galsim.SED('2', 'nm', 'fphotons', redshift=2)
    sed3 = galsim.SED('3', 'nm', '1')
    sed4 = galsim.SED('4', 'nm', '1')
    try:
        np.testing.assert_raises(TypeError, sed1.__mul__, sed2)
    except ImportError:
        print('The assert_raises tests require nose')
    np.testing.assert_almost_equal((sed1 * sed3)(100), 3.0, 10,
                                   "Found wrong value in SED.__mul__")
    np.testing.assert_almost_equal((sed2 * sed4)(10), 8.0, 10,
                                   "Found wrong value in SED.__mul__")
    np.testing.assert_almost_equal((sed3 * sed4)(30), 12.0, 10,
                                   "Found wrong value in SED.__mul__")
Example #7
0
def test_SED_basic():
    """Basic tests of SED functionality
    """
    c = 2.99792458e17  # speed of light in nm/s
    h = 6.62606957e-27 # Planck's constant in erg seconds
    nm_w = np.arange(10,1002,10)
    A_w = np.arange(100,10002,100)

    # All of these should be equivalent.  Flat spectrum with F_lambda = 200 erg/nm
    s_list = [
        galsim.SED(spec=lambda x: 200., flux_type='flambda', wave_type='nm'),
        galsim.SED(spec='200', flux_type='flambda', wave_type='nanometers'),
        galsim.SED('200', wave_type='nanometers', flux_type='flambda'),
        galsim.SED('200', 'nm', 'flambda'),
        galsim.SED('np.sqrt(4.e4)', 'nm', 'flambda'),
        galsim.SED('numpy.sqrt(4.e4)', 'nm', 'flambda'),
        galsim.SED('math.sqrt(4.e4)', 'nm', 'flambda'),
        # 200 erg/nm / 10 A/nm = 20 erg/A
        galsim.SED(spec='20', flux_type='flambda', wave_type='Angstroms'),
        # 200 erg/nm / (hc/w erg/photon) = 200 w/hc photons/nm
        galsim.SED(spec='200 * wave / %r'%(h*c), wave_type='NANOmeters', flux_type='fphotons'),
        # 200 erg/nm / (hc/w erg/photon) / 10 A/nm = 20 (w in A)/hc photons/A
        galsim.SED(spec='20 * (wave/10) / %r'%(h*c), flux_type='fphotons', wave_type='Ang'),
        # 200 erg/nm / (c/w^2 Hz/nm) = 200 w^2/c erg/Hz
        galsim.SED(spec='200 * wave**2 / %r'%c, flux_type='fnu', wave_type='nm'),
        galsim.SED(spec='200 * (wave/10)**2 / %r'%c, flux_type='fnu', wave_type='A'),
        galsim.SED(galsim.LookupTable([1,1e3],[200,200], interpolant='linear'),
                   wave_type='nanometers', flux_type='flambda'),
        galsim.SED(galsim.LookupTable([1,1e4],[20,20], interpolant='linear'),
                   wave_type='ang', flux_type='flambda'),
        galsim.SED(galsim.LookupTable([1,1e3],[200/(h*c),2e5/(h*c)], interpolant='linear'),
                   flux_type='fphotons', wave_type='nm'),
        galsim.SED(galsim.LookupTable([1,1e4],[2/(h*c),2e4/(h*c)], interpolant='linear'),
                   flux_type='fphotons', wave_type='A'),
        galsim.SED(galsim.LookupTable([1,1e3],[200/c,2e8/c], interpolant='linear',
                                      x_log=True, f_log=True),
                   flux_type='fnu', wave_type='nanometers'),
        galsim.SED(galsim.LookupTable([1,1e4],[2/c,2e8/c], interpolant='linear',
                                      x_log=True, f_log=True),
                   flux_type='fnu', wave_type='A'),
        galsim.SED(galsim.LookupTable(nm_w, 200.*np.ones(100)), wave_type='nanometers',
                   flux_type='flambda'),
        galsim.SED(galsim.LookupTable(A_w, 20.*np.ones(100)), flux_type='flambda', wave_type='A'),
        galsim.SED(galsim.LookupTable(nm_w, 200.*nm_w/(h*c)), flux_type='fphotons', wave_type='nm'),
        galsim.SED(galsim.LookupTable(A_w, 2.*A_w/(h*c)), flux_type='fphotons', wave_type='A'),
        galsim.SED(galsim.LookupTable(nm_w, 200.*nm_w**2/c), flux_type='fnu',
                   wave_type='nanometers'),
        galsim.SED(galsim.LookupTable(A_w, 2.*A_w**2/c), flux_type='fnu', wave_type='A'),
        galsim.SED(galsim.LookupTable([1, 100-1.e-10, 100, 1000, 1000+1.e-10, 2000],
                                      [0., 0., 200., 200., 0., 0.], interpolant='linear'),
                   wave_type='nm', flux_type='flambda')
    ]
    s_list += [
        s_list[9].thin(),
        s_list[10].thin(),
        s_list[11].thin(),
        s_list[12].thin(),
        s_list[13].thin(),
        s_list[14].thin(),
        s_list[15].thin(),
        s_list[15].thin(preserve_range=True),
        s_list[18].thin(),
        s_list[18].thin(preserve_range=True),
        s_list[21].thin(),
        s_list[21].thin(preserve_range=True),
        galsim.SED('1000', 'nm', 'flambda', redshift=4),
        galsim.SED('1000', 'nm', 'flambda').atRedshift(4.0),
    ]

    for k,s in enumerate(s_list):
        print(k,' s = ', s)
        np.testing.assert_almost_equal(s(400)*h*c/400, 200, decimal=10)
        np.testing.assert_almost_equal(s(900)*h*c/900, 200, decimal=10)
        waves = np.arange(700,800,10)
        np.testing.assert_array_almost_equal(s(waves) * h*c/waves, 200, decimal=10)

        if k < len(s_list)-2:
            np.testing.assert_equal(s.redshift, 0.)
        else:
            np.testing.assert_almost_equal(s.redshift, 4.)

        # Only the first one is not picklable
        if k > 0:
            do_pickle(s, lambda x: (x(470), x(490), x(910)) )
            do_pickle(s)
Example #8
0
def test_SED_mul():
    """Check that SEDs multiply like I think they should...
    """
    sed0 = galsim.SED(galsim.LookupTable([1,2,3,4,5], [1.1,2.2,3.3,4.4,5.5]),
                   wave_type='nm', flux_type='fphotons')
    sed1 = galsim.SED(lambda nu: nu**2, wave_type=units.Hz, flux_type='fnu', fast=False)
    sed2 = galsim.SED(17.0, wave_type='ang', flux_type='1')

    for sed, z in zip( [sed0, sed1, sed2], [0, 0.2, 0.4] ):
        a = sed.atRedshift(z)

        # SED multiplied by function
        b = lambda w: w**2
        c = a*b
        x = 3.0
        np.testing.assert_almost_equal(c(x), a(x) * b(x), 10,
                                       err_msg="Found wrong value in SED.__mul__")

        # function multiplied by SED
        c = b*a
        np.testing.assert_almost_equal(c(x), a(x) * b(x), 10,
                                       err_msg="Found wrong value in SED.__rmul__")

        # SED multiplied by scalar
        d = a*4.2
        np.testing.assert_almost_equal(d(x), a(x) * 4.2, 10,
                                       err_msg="Found wrong value in SED.__mul__")
        if sed is sed0: do_pickle(d)

        # assignment multiplication
        d *= 2
        np.testing.assert_almost_equal(d(x), a(x) * 4.2 * 2, 10,
                                       err_msg="Found wrong value in SED.__mul__")
        if sed is sed0: do_pickle(d)

        # SED multiplied by dimensionless, constant SED
        e = galsim.SED(2.0, 'nm', '1')
        f = a*e
        np.testing.assert_almost_equal(f(x), a(x) * e(x), 10,
                                       err_msg="Found wrong value in SED.__mul__")
        f = e*a
        np.testing.assert_almost_equal(f(x), e(x) * a(x), 10,
                                       err_msg="Found wrong value in SED.__mul__")

        # SED multiplied by dimensionless, non-constant SED
        g = galsim.SED('wave', 'nm', '1')
        h = a*g
        np.testing.assert_almost_equal(h(x), a(x) * g(x), 10,
                                       err_msg="Found wrong value in SED.__mul__")
        h2 = g*a
        np.testing.assert_almost_equal(h2(x), g(x) * a(x), 10,
                                       err_msg="Found wrong value in SED.__mul__")


    sed1 = galsim.SED('1', 'nm', 'fphotons', redshift=1)
    sed2 = galsim.SED('2', 'nm', 'fphotons', redshift=2)
    sed3 = galsim.SED('3', 'nm', '1')
    sed4 = galsim.SED('4', 'nm', '1')
    try:
        np.testing.assert_raises(TypeError, sed1.__mul__,  sed2)
    except ImportError:
        print('The assert_raises tests require nose')
    np.testing.assert_almost_equal((sed1*sed3)(100), 3.0, 10, "Found wrong value in SED.__mul__")
    np.testing.assert_almost_equal((sed2*sed4)(10), 8.0, 10, "Found wrong value in SED.__mul__")
    np.testing.assert_almost_equal((sed3*sed4)(30), 12.0, 10, "Found wrong value in SED.__mul__")
Example #9
0
def test_phase_screen_list():
    """Test list-like behaviors of PhaseScreenList."""
    rng = galsim.BaseDeviate(1234)
    rng2 = galsim.BaseDeviate(123)

    aper = galsim.Aperture(diam=1.0)

    ar1 = galsim.AtmosphericScreen(10,
                                   1,
                                   alpha=0.997,
                                   L0=None,
                                   time_step=0.01,
                                   rng=rng)
    assert ar1._time == 0.0, "AtmosphericScreen initialized with non-zero time."
    do_pickle(ar1)
    do_pickle(ar1, func=lambda x: x._tab2d(12.3, 45.6))
    do_pickle(ar1,
              func=lambda x: x._wavefront(aper.u, aper.v, None, theta0).sum())
    do_pickle(ar1, func=lambda x: x.wavefront(aper.u, aper.v, 0.0).sum())
    do_pickle(ar1,
              func=lambda x: np.sum(x.wavefront_gradient(aper.u, aper.v, 0.0)))
    t = np.empty_like(aper.u)
    ud = galsim.UniformDeviate(rng.duplicate())
    ud.generate(t.ravel())
    t *= 0.1  # Only do a few boiling steps
    do_pickle(ar1, func=lambda x: x.wavefront(aper.u, aper.v, t).sum())
    do_pickle(ar1,
              func=lambda x: np.sum(x.wavefront_gradient(aper.u, aper.v, t)))

    # Try seeking backwards
    assert ar1._time > 0.0
    ar1._seek(0.0)
    # But not before t=0.0
    try:
        np.testing.assert_raises(ValueError, ar1._seek, -1.0)
    except ImportError:
        pass

    # Check that L0=np.inf and L0=None yield the same thing here too.
    ar2 = galsim.AtmosphericScreen(10,
                                   1,
                                   alpha=0.997,
                                   L0=np.inf,
                                   time_step=0.01,
                                   rng=rng)
    assert ar1 == ar2
    # Create a couple new screens with different types/parameters
    ar2 = galsim.AtmosphericScreen(10,
                                   1,
                                   alpha=0.995,
                                   time_step=0.015,
                                   rng=rng2)
    assert ar1 != ar2
    ar3 = galsim.OpticalScreen(diam=1.0,
                               aberrations=[0, 0, 0, 0, 0, 0, 0, 0, 0.1])
    do_pickle(ar3)
    do_pickle(ar3,
              func=lambda x: x._wavefront(aper.u, aper.v, None, theta0).sum())
    do_pickle(ar3,
              func=lambda x: np.sum(
                  x._wavefront_gradient(aper.u, aper.v, None, theta0)))
    do_pickle(ar3, func=lambda x: x.wavefront(aper.u, aper.v).sum())
    do_pickle(ar3, func=lambda x: np.sum(x.wavefront_gradient(aper.u, aper.v)))
    atm = galsim.Atmosphere(
        screen_size=30.0,
        altitude=[0.0, 1.0],
        speed=[1.0, 2.0],
        direction=[0.0 * galsim.degrees, 120 * galsim.degrees],
        r0_500=0.15,
        rng=rng)
    atm.append(ar3)
    do_pickle(atm)
    do_pickle(atm,
              func=lambda x: x._wavefront(aper.u, aper.v, None, theta0).sum())
    do_pickle(atm,
              func=lambda x: x.wavefront(aper.u, aper.v, 0.0, theta0).sum())
    do_pickle(atm,
              func=lambda x: np.sum(x.wavefront_gradient(aper.u, aper.v, 0.0)))
    do_pickle(atm,
              func=lambda x: np.sum(
                  x._wavefront_gradient(aper.u, aper.v, 0.0, theta0)))

    # testing append, extend, __getitem__, __setitem__, __delitem__, __eq__, __ne__
    atm2 = galsim.PhaseScreenList(atm[:-1])  # Refers to first n-1 screens
    assert atm != atm2
    # Append a different screen to the end of atm2
    atm2.append(ar2)
    assert atm != atm2
    # Swap the last screen in atm2 for the one that should match atm.
    del atm2[-1]
    atm2.append(atm[-1])
    assert atm == atm2

    # Test building from empty PhaseScreenList
    atm3 = galsim.PhaseScreenList()
    atm3.extend(atm2)
    assert atm == atm3

    # Test constructing from existing PhaseScreenList
    atm4 = galsim.PhaseScreenList(atm3)
    del atm4[-1]
    assert atm != atm4
    atm4.append(atm[-1])
    assert atm == atm4

    # Test swap
    atm4[0], atm4[1] = atm4[1], atm4[0]
    assert atm != atm4
    atm4[0], atm4[1] = atm4[1], atm4[0]
    assert atm == atm4

    wf = atm._wavefront(aper.u, aper.v, None, theta0)
    wf2 = atm2._wavefront(aper.u, aper.v, None, theta0)
    wf3 = atm3._wavefront(aper.u, aper.v, None, theta0)
    wf4 = atm4._wavefront(aper.u, aper.v, None, theta0)

    np.testing.assert_array_equal(wf, wf2, "PhaseScreenLists are inconsistent")
    np.testing.assert_array_equal(wf, wf3, "PhaseScreenLists are inconsistent")
    np.testing.assert_array_equal(wf, wf4, "PhaseScreenLists are inconsistent")

    # Check copy
    import copy
    # Shallow copy copies by reference.
    atm5 = copy.copy(atm)
    assert atm[0] == atm5[0]
    assert atm[0] is atm5[0]
    atm._seek(1.0)
    assert atm[0]._time == 1.0, "Wrong time for AtmosphericScreen"
    assert atm[0] == atm5[0]
    assert atm[0] is atm5[0]
    # Deepcopy actually makes an indepedent object in memory.
    atm5 = copy.deepcopy(atm)
    assert atm[0] == atm5[0]
    assert atm[0] is not atm5[0]
    atm._seek(2.0)
    assert atm[0]._time == 2.0, "Wrong time for AtmosphericScreen"
    # But we still get equality, since this doesn't depend on mutable internal state:
    assert atm[0] == atm5[0]

    # Constructor should accept both list and indiv layers as arguments.
    atm6 = galsim.PhaseScreenList(atm[0])
    atm7 = galsim.PhaseScreenList([atm[0]])
    assert atm6 == atm7
    do_pickle(atm6,
              func=lambda x: x._wavefront(aper.u, aper.v, None, theta0).sum())
    do_pickle(atm6,
              func=lambda x: np.sum(x.wavefront_gradient(aper.u, aper.v, 0.0)))

    atm6 = galsim.PhaseScreenList(atm[0], atm[1])
    atm7 = galsim.PhaseScreenList([atm[0], atm[1]])
    atm8 = galsim.PhaseScreenList(
        atm[0:2])  # Slice returns PhaseScreenList, so this works too.
    assert atm6 == atm7
    assert atm6 == atm8

    # Check some actual derived PSFs too, not just phase screens.  Use a small pupil_plane_size and
    # relatively large pupil_plane_scale to speed up the unit test.
    atm._reset()
    assert atm[0]._time == 0.0, "Wrong time for AtmosphericScreen"
    kwargs = dict(exptime=0.05, time_step=0.01, diam=1.1, lam=1000.0)
    psf = atm.makePSF(**kwargs)
    do_pickle(psf)
    do_pickle(psf, func=lambda x: x.drawImage(nx=20, ny=20, scale=0.1))

    psf2 = atm2.makePSF(**kwargs)
    psf3 = atm3.makePSF(**kwargs)
    psf4 = atm4.makePSF(**kwargs)

    np.testing.assert_array_equal(psf, psf2,
                                  "PhaseScreenPSFs are inconsistent")
    np.testing.assert_array_equal(psf, psf3,
                                  "PhaseScreenPSFs are inconsistent")
    np.testing.assert_array_equal(psf, psf4,
                                  "PhaseScreenPSFs are inconsistent")