Beispiel #1
0
def test_FITSOpticalElement(tmpdir):
    circ_fits = poppy.CircularAperture().to_fits(grid_size=3, npix=10)
    fn = str(tmpdir / "circle.fits")
    circ_fits.writeto(fn, overwrite=True)

    # Test passing aperture via file on disk
    foe = poppy.FITSOpticalElement(transmission=fn)
    assert foe.amplitude_file == fn
    assert np.allclose(foe.amplitude, circ_fits[0].data)

    # Test passing OPD via FITS object, along with unit conversion
    circ_fits[0].header['BUNIT'] = 'micron'  # need unit for OPD
    foe = poppy.FITSOpticalElement(opd=circ_fits)
    assert foe.opd_file == 'supplied as fits.HDUList object'
    assert np.allclose(foe.opd, circ_fits[0].data * 1e-6)

    # make a cube
    rect_mask = poppy.RectangleAperture().sample(grid_size=3, npix=10)
    circ_mask = circ_fits[0].data
    circ_fits[0].data = np.stack([circ_mask, rect_mask])
    circ_fits[0].header['BUNIT'] = 'nm'  # need unit for OPD
    fn2 = str(tmpdir / "cube.fits")
    circ_fits.writeto(fn2, overwrite=True)

    # Test passing OPD as cube, with slice default, units of nanometers
    foe = poppy.FITSOpticalElement(opd=fn2)
    assert foe.opd_file == fn2
    assert foe.opd_slice == 0
    assert np.allclose(foe.opd, circ_mask * 1e-9)

    # Same cube but now we ask for the next slice
    foe = poppy.FITSOpticalElement(opd=(fn2, 1))
    assert foe.opd_file == fn2
    assert foe.opd_slice == 1
    assert np.allclose(foe.opd, rect_mask * 1e-9)
def WFIRSTSPC(npix=256, ratio=0.25):
    Tel_fname = os.path.join(os.environ['WEBBPSF_PATH'],
                             "AFTA_CGI_C5_Pupil_onax_256px_flip.fits")
    SP_fname = os.path.join(os.environ['WEBBPSF_PATH'],
                            "CGI/optics/CHARSPC_SP_256pix.fits.gz")
    FPM_fname = os.path.join(
        os.environ['WEBBPSF_PATH'],
        "CGI/optics/CHARSPC_FPM_25WA90_2x65deg_-_FP1res4_evensamp_D072_F770.fits.gz"
    )
    LS_fname = os.path.join(os.environ['WEBBPSF_PATH'],
                            "CGI/optics/SPC_LS_30D88_256pix.fits.gz")

    D_prim = 2.37 * u.m
    D_relay = 20 * u.mm
    fr_pri = 7.8
    fl_pri = D_prim * fr_pri
    fl_m2 = fl_pri * D_relay / D_prim
    fr_m3 = 20.
    fl_m3 = fr_m3 * D_relay

    oversamp = 4
    wfirst_optsys = poppy.FresnelOpticalSystem(pupil_diameter=D_prim,
                                               beam_ratio=ratio,
                                               npix=npix)

    telap = poppy.FITSOpticalElement(transmission=Tel_fname)
    SP = poppy.FITSOpticalElement(transmission=SP_fname)

    #default FPM pixelscale is in arcsecs
    FPM = poppy.FITSOpticalElement(
        transmission=FPM_fname,
        planetype=poppy.poppy_core.PlaneType.intermediate,
        pixelscale=0.005)
    SP.pixelscale = 0.5 * u.cm / SP.shape[0] / u.pix
    FPM.pixelscale = 0.5 * u.cm / SP.shape[0] / u.pix
    m1 = poppy.QuadraticLens(fl_pri, name='Primary')
    m2 = poppy.QuadraticLens(fl_m2, name='M2')
    m3 = poppy.QuadraticLens(fl_m3, name='M3')
    m4 = poppy.QuadraticLens(fl_m3, name='M4')
    m5 = poppy.QuadraticLens(fl_m3, name='M5')
    m6 = poppy.QuadraticLens(fl_m3, name='M6')

    wfirst_optsys.add_optic(telap)
    wfirst_optsys.add_optic(m1)
    wfirst_optsys.add_optic(m2, distance=fl_pri + fl_m2)
    wfirst_optsys.add_optic(m3, distance=fl_m2 + fl_m3)
    wfirst_optsys.add_optic(m4, distance=2 * fl_m3)
    wfirst_optsys.add_optic(SP, distance=fl_m3)
    wfirst_optsys.add_optic(m5, distance=fl_m3)
    wfirst_optsys.add_optic(FPM, distance=fl_m3)
    wfirst_optsys.add_optic(m5, distance=2 * fl_m3)

    wfirst_optsys.add_optic(poppy.ScalarTransmission(
        planetype=poppy.poppy_core.PlaneType.intermediate,
        name='focus',
    ),
                            distance=fl_m3 + 0.39999923 * u.m)
    return wfirst_optsys
Beispiel #3
0
def surfFITS(file_loc, optic_type, opdunit, name):
    optic_fits = fits.open(file_loc)
    optic_fits[0].data = np.float_(
        optic_fits[0].data)  # typecasting for POPPY workaround
    if optic_type == 'opd':
        optic_surf = poppy.FITSOpticalElement(name=name,
                                              opd=optic_fits,
                                              opdunits=opdunit)
    else:
        optic_surf = poppy.FITSOpticalElement(name=name,
                                              transmission=optic_fits)
    return optic_surf
Beispiel #4
0
def test_shift_rotation_consistency(npix=30, grid_size = 1.5, angle=35, display=False):
    """Test shifting & rotation together for FITS and Analytic optics
    Do we get consistent behavior from each? Are the signs and
    order of operations consistent?

    Tests the fix for issue #275.
    """
    import poppy
    if npix < 30:
        raise ValueError("Need npix>=30 for enough resolution for this test")

    # Create rectangle, rotated
    rect = poppy.RectangleAperture(shift_x=0.25, rotation=angle)
    rect_samp = rect.sample(grid_size=grid_size, npix=npix)

    # Create rectangle, turn into FITS, then rotate
    rect_fits = poppy.RectangleAperture().to_fits(grid_size=grid_size, npix=npix)
    rect2 = poppy.FITSOpticalElement(transmission=rect_fits, shift_x=0.25, rotation=angle)

    # Compare that they are consistent enough, meaning
    # no more than 1% pixel difference. That tolerance allows for the
    # imprecision of rotating low-res binary masks.
    diff = np.round(rect2.amplitude)-rect_samp
    assert np.abs(diff).sum() <= 0.01*rect_samp.sum(), "Shift and rotations differ unexpectedly"

    if display:
        plt.figure()
        plt.subplot(131)
        plt.imshow(rect_samp)
        plt.subplot(132)
        plt.imshow(np.round(rect2.amplitude))
        plt.subplot(133)
        plt.imshow(diff)
Beispiel #5
0
def test_OPD_in_waves_for_FITSOpticalElement():
    pupil_radius = 1 * u.m
    pupil = poppy.CircularAperture(radius=pupil_radius)
    reference_wavelength = 1 * u.um
    npix = 16
    single_wave_1um_lens = poppy.ThinLens(
        name='Defocus',
        nwaves=1,
        reference_wavelength=reference_wavelength,
        radius=pupil_radius)
    osys = poppy.OpticalSystem(oversample=1, npix=npix)
    osys.add_pupil(pupil)
    osys.add_pupil(single_wave_1um_lens)
    osys.add_detector(0.01 * u.arcsec / u.pixel, fov_pixels=3)
    # We have applied 1 wave of defocus at 1 um, so verify the center
    # has lower flux than at 2 um (it should be the 'hole' of the donut)
    psf_1um = osys.calc_psf(reference_wavelength)
    center_pixel_value = psf_1um[0].data[1, 1]
    psf_2um = osys.calc_psf(2 * reference_wavelength)
    assert psf_2um[0].data[1, 1] > psf_1um[0].data[1, 1]
    # Now, use the single_wave_1um_lens optic to make a
    # wavelength-independent 1 wave defocus
    lens_as_fits = single_wave_1um_lens.to_fits(what='opd', npix=3 * npix // 2)
    lens_as_fits[0].header['BUNIT'] = 'radian'
    lens_as_fits[0].data *= 2 * np.pi / reference_wavelength.to(u.m).value
    thin_lens_wl_indep = poppy.FITSOpticalElement(opd=lens_as_fits,
                                                  opdunits='radian')
    # We expect identical peak flux for all wavelengths, so check at 0.5x and 2x
    for prefactor in (0.5, 1.0, 2.0):
        osys = poppy.OpticalSystem(oversample=1, npix=npix)
        osys.add_pupil(pupil)
        osys.add_pupil(thin_lens_wl_indep)
        osys.add_detector(prefactor * 0.01 * u.arcsec / u.pixel, fov_pixels=3)
        psf = osys.calc_psf(wavelength=prefactor * u.um)
        assert np.isclose(center_pixel_value, psf[0].data[1, 1])
Beispiel #6
0
def test_shifting_optics( npix=30,  grid_size = 3, display=False):
    """Test shifting (translation) of Analytic and FITS Optical elements.
    Does shifting work as expected? Is it consistent between the two classes?

    Tests the fix for #247
    """
    import poppy
    pixsize =grid_size/npix
    shift_size = np.round(0.2/pixsize)*pixsize  # by construction, an integer # of pixels

    # Create a reference array
    circ = poppy.CircularAperture()
    circ_samp = circ.sample(npix=npix, grid_size=grid_size)

    # Create a shifted version, and make sure it's different
    circ_shift = poppy.CircularAperture( shift_x=shift_size)
    circ_shift_samp = circ_shift.sample(npix=npix, grid_size=grid_size)

    if display:
        plt.imshow(circ_samp-circ_shift_samp)
    assert np.allclose(circ_samp, circ_shift_samp) is False, "Shift didn't change array"

    # Make a FITS element.
    circ_fits = circ.to_fits(npix=npix, grid_size=grid_size)

    # Show we can shift that and get the same result as shifting the analytic element
    fits_shifted = poppy.FITSOpticalElement(transmission=circ_fits, shift_x=shift_size)
    np.testing.assert_allclose(fits_shifted.amplitude, circ_shift_samp, atol=1e-9,
                                       err_msg="Shifting Analytic and FITS versions are not consistent (v1, via shift_x)")

    # FITSOpticalElement also lets you specify shifts via fraction of the array. Let's
    # show that is  consistent.  This is older syntax that is discouraged, and may be
    # deprecated and removed eventually. But while available it should be correct.
    array_frac = shift_size/grid_size
    fits_shifted_v2 = poppy.FITSOpticalElement(transmission=circ_fits, shift=(array_frac, 0))
    np.testing.assert_allclose(fits_shifted.amplitude, fits_shifted_v2.amplitude, atol=1e-9,
                                       err_msg="Shifting FITS via shift/shift_x are not consistent")
    np.testing.assert_allclose(fits_shifted.amplitude, circ_shift_samp, atol=1e-9,
                                       err_msg="Shifting Analytic and FITS versions are not consistent (v2, via shift)")


    # Check in a 1D cut that the amount of shift is as expected -
    # this is implicitly also checked above via the match of Analytic and FITS
    # which use totally different methods to perform the shift.
    shift_in_pixels = int(shift_size/pixsize)
    assert np.allclose(np.roll(circ_samp[npix//2], shift_in_pixels),
                               circ_shift_samp[npix//2])
Beispiel #7
0
def test_fits_rot90_vs_ndimagerotate_consistency(plot=False):
    """Test that rotating a FITS HDUList via either of the two
    methods yields consistent results. This compares an exact
    90 degree rotation and an interpolating not-quite-90-deg rotation.
    Both methods should rotate counterclockwise and consistently.
    """
    letterf_hdu = poppy.optics.LetterFAperture().to_fits(npix=128)
    opt1 = poppy.FITSOpticalElement(transmission=letterf_hdu, rotation=90)
    opt2 = poppy.FITSOpticalElement(transmission=letterf_hdu,
                                    rotation=89.99999)
    assert np.allclose(opt1.amplitude, opt2.amplitude, atol=1e-5)

    if plot:
        fig, axes = plt.subplots(figsize=(10, 5), ncols=2)
        axes[0].imshow(opt1.amplitude)
        axes[0].set_title("Rot90")
        axes[1].imshow(opt2.amplitude)
        axes[1].set_title("ndimage rotate(89.9999)")
Beispiel #8
0
def test_analytic_vs_FITS_rotation_consistency(plot=False):
    """Test that rotating an AnalyticOpticalElement vs
    rotating a discretized version as a FITSOpticalElement
    are consistent in rotation direction (counterclockwise)
    and amount"""
    opt1 = poppy.optics.LetterFAperture(rotation=90)

    letterf_hdu = poppy.optics.LetterFAperture().to_fits(npix=128)
    opt2 = poppy.FITSOpticalElement(transmission=letterf_hdu, rotation=90)

    if plot:
        opt1.display()
        plt.figure()
        opt2.display()

    array1 = opt1.sample(npix=128)
    array2 = opt2.amplitude
    assert np.allclose(array1, array2)
Beispiel #9
0
    def create_image(self, coefficient_set_init=None, input_noise=None):
        # This is the function that creates the image (will probably call the config file) from some zernike coefficients
        # Copy paste the code that creates the image here

        import poppy
        from astropy.io import fits

        pupil_diameter = 6.559  # (in meter) As used in WebbPSF
        pupil_radius = pupil_diameter / 2

        osys = poppy.OpticalSystem()

        transmission = '/Users/mygouf/Python/webbpsf/webbpsf-data4/jwst_pupil_RevW_npix1024.fits.gz'
        #opd = '/Users/mygouf/Python/webbpsf/webbpsf-data/NIRCam/OPD/OPD_RevV_nircam_115.fits'
        opd = '/Users/mygouf/Python/webbpsf/webbpsf-data4/NIRCam/OPD/OPD_RevW_ote_for_NIRCam_requirements.fits.gz'
        hdul = fits.open(opd)
        hdul2 = fits.open(transmission)

        # Create wavefront map
        #print(coefficient_set_init)
        zernike_coefficients = np.append(0, coefficient_set_init)
        #zernike_coefficients *= 1e6 # conversion from meters to microns
        #wavefront_map = poppy.ZernikeWFE(radius=pupil_radius,
        #                                     coefficients=zernike_coefficients,
        #                                     aperture_stop=False)
        #print(zernike_coefficients)
        wavefront_map = poppy.zernike.opd_from_zernikes(
            zernike_coefficients,
            npix=1024,
            basis=poppy.zernike.zernike_basis_faster)
        wavefront_map = np.nan_to_num(wavefront_map) * hdul2[0].data

        fits.writeto('wavefront_map.fits',
                     wavefront_map,
                     hdul[0].header,
                     overwrite=True)
        #opd = wavefront_map
        opd = 'wavefront_map.fits'

        #myoptic = poppy.FITSOpticalElement(transmission='transfile.fits', opd='opdfile.fits', pupilscale="PIXELSCL")
        #opd = '/Users/mygouf/Python/webbpsf/webbpsf-data4/NIRCam/OPD/OPD_RevW_ote_for_NIRCam_requirements.fits.gz'

        jwst_opd = poppy.FITSOpticalElement(transmission=transmission, opd=opd)
        #jwst_opd = poppy.FITSOpticalElement(transmission=transmission)

        osys.add_pupil(jwst_opd)  # JWST pupil
        osys.add_detector(
            pixelscale=0.063, fov_arcsec=self.fov_arcsec,
            oversample=4)  # image plane coordinates in arcseconds

        psf = osys.calc_psf(4.44e-6)  # wavelength in microns
        psf_poppy = np.array(psf[0].data)
        poppy.display_psf(psf, title='JWST NIRCam test')

        #psf1 = osys.calc_psf(4.44e-6)                            # wavelength in microns
        #psf2 = osys.calc_psf(2.50e-6)                            # wavelength in microns
        #psf_poppy = psf1[0].data/2 + psf2[0].data/2

        psf_poppy = psf_poppy * 1e7 / np.max(psf_poppy)
        # Adding photon noise
        #image = np.random.normal(loc=psf_poppy, scale=np.sqrt(psf_poppy))
        if np.all(input_noise) == None:
            #noise =  np.random.normal(loc=psf_poppy, scale=np.sqrt(psf_poppy>1000))
            #noise =  self.rs.normal(loc=psf_poppy, scale=np.sqrt(psf_poppy>np.max(psf_poppy)/1000))
            noise = np.random.normal(
                loc=psf_poppy,
                scale=np.sqrt(psf_poppy > np.max(psf_poppy) / 1000))
            #noise = rs.poisson(psf_poppy)
            #print('Estimated Noise', np.mean(noise))
        else:
            noise = input_noise
            #print('Input Noise', np.mean(noise))

        image = psf_poppy + noise

        dict_ = {
            'image': image,
            'noise': noise,
            'wavefront_map': wavefront_map
        }
        #print(np.mean(image),np.mean(noise))

        return dict_
Beispiel #10
0
    def create_image_from_opd_file(self, opd=None, input_noise=None):
        # This is the function that creates the image (will probably call the config file) from some zernike coefficients
        # Copy paste the code that creates the image here

        import poppy
        from astropy.io import fits

        pupil_diameter = 6.559  # (in meter) As used in WebbPSF
        pupil_radius = pupil_diameter / 2

        osys = poppy.OpticalSystem()

        transmission = '/Users/mygouf/Python/webbpsf/webbpsf-data4/jwst_pupil_RevW_npix1024.fits.gz'
        #opd = '/Users/mygouf/Python/webbpsf/webbpsf-data/NIRCam/OPD/OPD_RevV_nircam_115.fits'
        #opd = '/Users/mygouf/Python/webbpsf/webbpsf-data4/NIRCam/OPD/OPD_RevW_ote_for_NIRCam_requirements.fits.gz'
        hdul = fits.open(opd)
        hdul2 = fits.open(transmission)

        wavefront_map = hdul[0].data

        jwst_opd = poppy.FITSOpticalElement(transmission=transmission, opd=opd)
        #jwst_opd = poppy.FITSOpticalElement(transmission=transmission)

        osys.add_pupil(jwst_opd)  # JWST pupil
        osys.add_detector(
            pixelscale=0.063, fov_arcsec=self.fov_arcsec,
            oversample=4)  # image plane coordinates in arcseconds

        psf = osys.calc_psf(4.44e-6)  # wavelength in microns
        psf_poppy = np.array(psf[0].data)
        poppy.display_psf(psf, title='JWST NIRCam test')

        #psf1 = osys.calc_psf(4.44e-6)                            # wavelength in microns
        #psf2 = osys.calc_psf(2.50e-6)                            # wavelength in microns
        #psf_poppy = psf1[0].data/2 + psf2[0].data/2

        psf_poppy = psf_poppy * 1e7 / np.max(psf_poppy)
        # Adding photon noise
        #image = np.random.normal(loc=psf_poppy, scale=np.sqrt(psf_poppy))
        if np.all(input_noise) == None:
            #noise =  np.random.normal(loc=psf_poppy, scale=np.sqrt(psf_poppy>1000))
            #noise =  self.rs.normal(loc=psf_poppy, scale=np.sqrt(psf_poppy>np.max(psf_poppy)/1000))
            noise = np.random.normal(
                loc=psf_poppy,
                scale=np.sqrt(psf_poppy > np.max(psf_poppy) / 1000))
            #noise = rs.poisson(psf_poppy)
            #print('Estimated Noise', np.mean(noise))
        else:
            noise = input_noise
            #print('Input Noise', np.mean(noise))

        image = psf_poppy + noise

        dict_ = {
            'image': image,
            'noise': noise,
            'wavefront_map': wavefront_map
        }
        #print(np.mean(image),np.mean(noise))

        return dict_