Exemple #1
0
def test_changing_npix():
    '''
    Test that using different npix will result in same PSF
    '''
    # Create a NIRCam instance using the default npix=1024
    nircam_1024 = webbpsf.NIRCam()
    nircam_1024.pupilopd = None # Set to none so I don't have to worry about making new OPDs
    psf_1024 = nircam_1024.calc_psf(oversample=2, nlambda=1, add_distortion=False)

    # Create a NIRCam instance using npix=2048
    npix = 2048
    nircam_2048 = webbpsf.NIRCam()
    nircam_2048.pupil = os.path.join(webbpsf.utils.get_webbpsf_data_path(),
                                     f'jwst_pupil_RevW_npix{npix}.fits.gz')
    nircam_2048.pupilopd = None # Set to none so I don't have to worry about making new OPDs
    psf_2048 = nircam_2048.calc_psf(oversample=2, nlambda=1, add_distortion=False)

    # Let's check individual pixel values, at least where the PSF is not too dim.
    # Check all pixels which have > 1e-6 of the total flux (we can safely ignore pixels with very low intensity)
    mask = psf_1024[0].data>1e-6
    assert np.allclose(psf_1024[0].data[mask], psf_2048[0].data[mask], rtol=0.01), 'Pixel values differ by more than 1%'

    # Let's check that the total flux in the PSF does not change much.
    #  (A small amount is acceptable and not surprising, since higher resolution improves the fidelity at which
    #   we model light that is scattered by segment edges to very wide angles outside of the simulated PSF FOV)
    assert np.isclose(psf_1024[0].data.sum(), psf_2048[0].data.sum(), rtol=0.005), "PSF total flux should not change much"

    # Let's also check a derived property of the whole PSF: the FWHM.
    # The FWHM should be very close to identical for the two PSFs.
    assert np.isclose(webbpsf.measure_fwhm(psf_1024), webbpsf.measure_fwhm(psf_2048), rtol=0.0001), "PSF FWHM should not vary for different npix"
Exemple #2
0
def nircam_nocoro(filter, Aber_WSS):
    """
    Create PSF
    :param filter:
    :param Aber_WSS:
    :return:
    """
    # Create NIRCam object
    nc = webbpsf.NIRCam()
    # Set filter
    nc.filter = filter

    # Adjust OTE with aberrations
    nc, ote = webbpsf.enable_adjustable_ote(nc)
    nc.include_si_wfe = False  # set SI internal WFE to zero
    ote.reset()
    ote.zero()
    for i in range(nb_seg):
        seg = wss_segs[i].split('-')[0]
        ote._apply_hexikes_to_seg(seg, Aber_WSS[i, :])

    # Calculate PSF
    psf_nc = nc.calc_psf(oversample=1, fov_pixels=int(im_size_e2e), nlambda=1)
    psf_webbpsf = psf_nc[1].data

    return psf_webbpsf
def parse_aperture(aperture_name):
    '''
    Return [image mask, pupil mask, fov_pixels, trim_fov_pixels, pixelscale]
    '''
    
    aperture_keys = ['mask210r','mask335r','mask430r','masklwb','maskswb',
                     'fqpm1065','fqpm1140','fqpm1550','lyot2300']
    assert aperture_name in aperture_keys, \
        'Aperture {} not recognized! Must be one of {}'.format(aperture_name, aperture_keys)

    nc = webbpsf.NIRCam()
    miri = webbpsf.MIRI()

    aperture_dict = {
        'mask210r' : ['MASK210R','CIRCLYOT', 101, None, nc._pixelscale_short],
        'mask335r' : ['MASK335R','CIRCLYOT', 101, None, nc._pixelscale_long],
        'mask430r' : ['MASK430R','CIRCLYOT', 101, None, nc._pixelscale_long],
        'masklwb' : ['MASKLWB','WEDGELYOT', 351, 101, nc._pixelscale_long],
        'maskswb' : ['MASKSWB','WEDGELYOT', 351, 101, nc._pixelscale_short],
        'fqpm1065' : ['FQPM1065','MASKFQPM', 81, None, miri.pixelscale],
        'fqpm1140' : ['FQPM1140','MASKFQPM', 81, None, miri.pixelscale],
        'fqpm1550' : ['FQPM1550','MASKFQPM', 81, None, miri.pixelscale],
        'lyot2300' : ['LYOT2300','MASKLYOT', 81, None, miri.pixelscale]
        }
  
    return aperture_dict[aperture_name]
def psf_suite(nw=30,
              wmin=0.5,
              wmax=6.,
              instrument=wp.NIRCam(),
              outname='PSF',
              fov_arcsec=1.5,
              aperture='any'):

    nw = float(nw)
    waves = (np.arange(nw) / (nw - 1))**2. * (wmax - wmin) + wmin
    waves_m = waves * 1e-6

    for wave in waves_m:
        longname = outname + '_{0:.4f}'.format(wave * 1e6) + '.fits'
        longname = longname.lower()
        if os.path.isfile(longname):
            os.remove(longname)
        instrument.calcPSF(oversample=2,
                           fov_arcsec=fov_arcsec,
                           monochromatic=wave,
                           outfile=longname,
                           clobber='true')

        # Add mode keyword to header
        hdulist = pf.open(longname, mode='update')
        prihdr = hdulist[0].header
        prihdr['APERTURE'] = (
            aperture, 'The observing aperture within the instrument FOV')
        hdulist.flush()
        hdulist.close()
def nircam_coro(filter, fpm, ppm, Aber_WSS):
    """
    -- Deprecated function still used in analytical PASTIS and some notebooks. --

    Create NIRCam image with specified filter and coronagraph, and aberration input.
    :param filter: str, filter name
    :param fpm: focal plane mask
    :param ppm: pupil plane mask - Lyot stop
    :param Aber_WSS: list or array holding Zernike coefficients ordered in WSS convention and in METERS
    :return:
    """

    # Set up NIRCam and coronagraph
    nc = webbpsf.NIRCam()
    nc.filter = filter
    nc.image_mask = fpm
    nc.pupil_mask = ppm

    # Adjust OTE with aberrations
    nc, ote = webbpsf.enable_adjustable_ote(nc)
    nc.include_si_wfe = False  # set SI internal WFE to zero
    ote.reset()
    ote.zero()
    for i in range(NB_SEG):
        seg = WSS_SEGS[i].split('-')[0]
        ote._apply_hexikes_to_seg(seg, Aber_WSS[i,:])

    # Calculate PSF
    psf_nc = nc.calc_psf(oversample=1, fov_pixels=int(IM_SIZE_E2E), nlambda=1)
    psf_webbpsf = psf_nc[1].data

    return psf_webbpsf
Exemple #6
0
    def __init__(self, filter, width, resampling_factor=1):
        """
        :param f: tje filter of the form JWST.NIRCam.XXX or JWST.MIRI.XXX
        :param width: Width of the image along a single axis (this is approximate since images must be odd in dimension)
        :param resampling_factor: The integer amount of resampling done to increase resolution. (int)
        :param gaussFWHM: If a simple gaussian PSF is required the FWHM of that PSF in arcseconds. (float)
        """
        
        inst = filter.split('.')[1]
        f = filter.split('.')[-1]
        
        if filter in FLARE.filters.NIRCam_l: 
            self.resolution = 0.063
        elif filter in FLARE.filters.NIRCam_s: 
            self.resolution = 0.031
        else:
            print('filter not found')

        # Ndim must odd for convolution with the PSF
        ini_Ndim = int(width / self.resolution)
        if ini_Ndim % 2 != 0:
            self.Ndim = int(width / self.resolution)
        else:
            self.Ndim = int(width / self.resolution) + 1

        if inst == 'NIRCAM':  nc = webbpsf.NIRCam()

        nc.filter = f
        self.PSF = nc.calc_psf(oversample=resampling_factor, fov_pixels=self.Ndim)[0].data  # compute PSF
def nircam_nocoro(filter, Aber_WSS):
    """
    -- Deprecated function still used in analytical PASTIS and some notebooks. --
    :param filter:
    :param Aber_WSS:
    :return:
    """
    # Create NIRCam object
    nc = webbpsf.NIRCam()
    # Set filter
    nc.filter = filter

    # Adjust OTE with aberrations
    nc, ote = webbpsf.enable_adjustable_ote(nc)
    nc.include_si_wfe = False  # set SI internal WFE to zero
    ote.reset()
    ote.zero()
    for i in range(NB_SEG):
        seg = WSS_SEGS[i].split('-')[0]
        ote._apply_hexikes_to_seg(seg, Aber_WSS[i,:])

    # Calculate PSF
    psf_nc = nc.calc_psf(oversample=1, fov_pixels=int(IM_SIZE_E2E), nlambda=1)
    psf_webbpsf = psf_nc[1].data

    return psf_webbpsf
Exemple #8
0
def test_single_seg_psf(segmentid=1):
    """Test calculation of a single segment PSF, including options to remove piston/tip/tilt as used by MIRAGE

    """

    nrc = webbpsf.NIRCam()
    nrc.filter = 'F212N'
    nrc, ote = webbpsf.enable_adjustable_ote(nrc)
    ote.zero(zero_original=True)

    segname = webbpsf.constants.SEGNAMES_WSS_ORDER[segmentid-1][0:2]

    ote.move_seg_local(segname, xtilt=1, piston=-1)

    pupil = webbpsf.webbpsf_core.one_segment_pupil(segmentid)
    ote.amplitude = pupil[0].data


    psf = nrc.calc_psf(nlambda=1)

    ote.remove_piston = True
    ote.update_opd()
    psf_rm_piston = nrc.calc_psf(nlambda=1)
    assert np.allclose(psf[0].data, psf_rm_piston[0].data), "Piston removal should not affect the overall PSF"

    assert np.allclose( webbpsf.measure_centroid(psf), webbpsf.measure_centroid(psf_rm_piston)), "centroid should not shift"

    ote.remove_piston_tip_tilt = True
    ote.update_opd()
    psf_rm_ptt = nrc.calc_psf(nlambda=1)
    assert not np.allclose(psf[0].data, psf_rm_ptt[0].data), "Piston/Tip/Tip removal should shift the overall PSF"
    assert np.abs(webbpsf.measure_centroid(psf)[0] - webbpsf.measure_centroid(psf_rm_ptt)[0]) > 40, "centroid should shift susbtantially with/without tip/tilt removal"
Exemple #9
0
def test_move_sur(plot=False):
    """ Test we can move mirrors using Segment Update Requests
    """
    import webbpsf
    import os
    import glob
    surdir = os.path.join(webbpsf.__path__[0], 'tests', 'surs')
    surs = glob.glob(surdir+'/*sur.xml')

    nrc = webbpsf.NIRCam()
    nrc.filter='F212N'
    nrc, ote = webbpsf.enable_adjustable_ote(nrc)
    ote.zero(zero_original=True)

    for s in surs:
        print("Testing "+s)
        ote.reset()
        ote.move_sur(s)
        # the coarse phasing SUR is a no-op after 3 groups; all others have some effect
        if 'coarse_phasing' not in s:
            assert not np.allclose(ote.segment_state, 0), "Expected some segments to be moved"

        ote.move_sur(s, reverse=True)
        assert np.allclose(ote.segment_state, 0), "Reversing moves didn't bring us back to zero"

        
    # Test every DOF on A1-1 and SM and check the OTE state updated accordingly
    s = glob.glob(surdir+'/example_alldof_A1-SM_sur.xml')[0]
    print("Testing "+s)
    ote.reset()
    ote.move_sur(s)
    assert np.allclose(ote.segment_state[0],  [1, 2, 3, 4, 5, 6])
    assert np.allclose(ote.segment_state[-1], [1, 2, 3, 4, 5, 0])
    
    
    # Test moving one at a time. This test relies on specifics of what's in the image stacking SUR.
    s = glob.glob(surdir+'/example_image_stacking*sur.xml')[0]
    print("Testing moving one group at a time with "+s)
    ote.reset()
    sur = webbpsf.surs.SUR(s)

    ngroups = len(sur.groups)
    oldstate = ote.segment_state.copy()

    for igrp in range(1, ngroups+1):
        print("Group {} should move segment {}".format(igrp, 2*igrp+6))
        ote.move_sur(s, group=igrp)

        movedsegs = np.abs((ote.segment_state - oldstate).sum(axis=1))
        assert (movedsegs!=0).sum()==1, "Only expected one segment to move"
        whichmoved = np.argmax(movedsegs)+1
        print ("Moved segment", whichmoved)
        assert whichmoved == 2*igrp+6, "An unexpected segment moved"
        oldstate = ote.segment_state.copy()
        if plot:
            psf = nrc.calc_psf(fov_pixels=256, add_distortion=False)
            plt.figure()
            ote.display_opd(title="After Group {}".format(igrp))
            plt.figure()
            webbpsf.display_psf(psf, ext=1, title="After Group {}".format(igrp))
def get_psf( wave, instrument, aperture_name, source_offset=(0, 0), otf_options=None,
        full_aperture=None):
    #Make the instrument and determine the mode
    if instrument.upper() == 'NIRCAM':
        ins = webbpsf.NIRCam()
        
        # WebbPSF needs to know the filter to select the optimal 
        # offset for the bar masks. The filter is not passed into
        # get_psf but is stored in the full aperture name in self._psfs
        if aperture_name in ['masklwb', 'maskswb']:
            # Everything after the aperture name is the filter name.
            full_aperture = self._psfs[0]['aperture_name']
            fname = full_aperture[full_aperture.find(aperture_name) + len(aperture_name):]
            ins.filter = fname
        if wave > 2.5:
            # need to toggle to LW detector.
            ins.detector='A5'
            ins.pixelscale = ins._pixelscale_long
    elif instrument.upper() == 'MIRI':
        ins = webbpsf.MIRI()
    else:
        raise ValueError('Only NIRCam and MIRI are supported instruments!')
    image_mask, pupil_mask, fov_pixels, trim_fov_pixels, pix_scl = parse_aperture(aperture_name)
    ins.image_mask = image_mask
    ins.pupil_mask = pupil_mask

    # Apply any extra options if specified by the user:
    for key in options.on_the_fly_webbpsf_options:
        ins.options[key] = options.on_the_fly_webbpsf_options[key]

    if options.on_the_fly_webbpsf_opd is not None:
        ins.pupilopd = options.on_the_fly_webbpsf_opd

    #get offset
    ins.options['source_offset_r'] = source_offset[0]
    ins.options['source_offset_theta'] = source_offset[1]
    ins.options['output_mode'] = 'oversampled'
    ins.options['parity'] = 'odd'

    psf_result = calc_psf_and_center(ins, wave, source_offset[0], source_offset[1], 3,
                            pix_scl, fov_pixels, trim_fov_pixels=trim_fov_pixels)

    pix_scl = psf_result[0].header['PIXELSCL']
    upsamp = psf_result[0].header['OVERSAMP']
    diff_limit = psf_result[0].header['DIFFLMT']
    psf = psf_result[0].data

    psf = {
        'int': psf,
        'wave': wave,
        'pix_scl': pix_scl,
        'diff_limit': diff_limit,
        'upsamp': upsamp,
        'instrument': instrument,
        'aperture_name': aperture_name,
        'source_offset': source_offset
    }

    return psf
Exemple #11
0
def create_dummy_psf_grid():
    """Use webbpsf to create a griddedPSFModel object"""
    nrc = webbpsf.NIRCam()
    nrc.filter = 'F200W'
    nrc.detector = 'NRCB1'
    grid = nrc.psf_grid(num_psfs=1,
                        all_detectors=False,
                        oversample=1,
                        fov_pixels=301,
                        add_distortion=False,
                        save=False)
    return grid
Exemple #12
0
def setup_coro(filter, fpm, ppm):
    """
    Set up a NIRCam coronagraph object.
    :param filter: str, filter name
    :param fpm: focal plane mask
    :param ppm: pupil plane mask - Lyot stop
    :return:
    """
    nc = webbpsf.NIRCam()
    nc.filter = filter
    nc.image_mask = fpm
    nc.pupil_mask = ppm

    return nc
Exemple #13
0
def generate_random_ote_deployment(out_dir, reduction_factor=0.2, save=True):
    """Create a WebbPSF adjustable OTE object representing a perturbed OTE
    mirror state by randomly generating mirror deployment errors.

    Parameters
    ----------
    out_dir : str
        Path to directory in which to save OPD FITS files and the deployment
        error dictionary
    reduction_factor : optional
        Factor by which to reduce the input deployment errors. Default is 0.2.
    save : bool, optional
        Denotes whether to save out the OPD (with and without tip/tilt)
        as FITs files and the deployment error dictionary as a yaml

    Returns
    -------
    ote : webbpsf.opds.OTE_Linear_Model_WSS object
        WebbPSF OTE object describing perturbed OTE state with tip and tilt removed
    segment_tilts : numpy.ndarray
        List of X and Y tilts for each mirror segment, in microradians
    ote_opd_with_tilts : numpy.ndarray
        Array representing perturbed OTE OPD pupil before tip/tilt is removed
    """
    # Make an adjustable OTE object with WebbPSF
    nc = webbpsf.NIRCam()
    nc, ote = webbpsf.enable_adjustable_ote(nc)

    # Generate OPD and vector list with reduced deployment errors
    deployment_errors = generate_deployment_errors(out_dir=out_dir, save=save)
    deployment_errors = reduce_deployment_errors(
        deployment_errors,
        reduction_factor=reduction_factor,
        out_dir=out_dir,
        save=save)

    # Create an OTE object with those deployment errors
    ote, segment_tilts = apply_deployment_errors(ote,
                                                 deployment_errors,
                                                 out_dir=out_dir,
                                                 save=save)
    ote_opd_with_tilts = ote.opd  # Save array to plot below

    # Remove tip and tilt
    ote = remove_piston_tip_tilt(ote, out_dir=out_dir, save=save)

    return ote, segment_tilts, ote_opd_with_tilts
def set_up_nircam():
    """
    Return a configured instance of the NIRCam simulator on JWST.

    Sets up the Lyot stop and filter from the configfile, turns of science instrument (SI) internal WFE and zeros
    the OTE.
    :return: Tuple of NIRCam instance, and its OTE
    """

    nircam = webbpsf.NIRCam()
    nircam.include_si_wfe = False
    nircam.filter = CONFIG_PASTIS.get('JWST', 'filter_name')
    nircam.pupil_mask = CONFIG_PASTIS.get('JWST', 'pupil_plane_stop')

    nircam, ote = webbpsf.enable_adjustable_ote(nircam)
    ote.zero(zero_original=True)    # https://github.com/spacetelescope/webbpsf/blob/96537c459996f682ac6e9af808809ca13fb85e87/webbpsf/opds.py#L1125

    return nircam, ote
Exemple #15
0
def test_pupilopd_none():
    """Test that setting pupilopd=None does in fact result in no WFE
    In particular, this tests that setting opd to None also results in
    disabling the field-dependent component of the OTE linear model,
    as well as setting the global WFE component to zero.
    """

    nrc = webbpsf.NIRCam()
    nrc.pupilopd = None
    nrc.include_si_wfe = False

    ote_lom = nrc.get_optical_system().planes[0]
    assert ote_lom.rms()==0, "RMS WFE should be strictly 0"

    psf_small = nrc.calc_psf(fov_pixels=50, monochromatic=2e-6, add_distortion=False)
    centroid = webbpsf.measure_centroid(psf_small, relativeto='center')
    assert np.abs(centroid[0]) < 1e-5, "Centroid should be (0,0)"
    assert np.abs(centroid[1]) < 1e-5, "Centroid should be (0,0)"
Exemple #16
0
def load_ote_from_deployment_yaml(deployments_file, out_dir, save=True):
    """Create a WebbPSF adjustable OTE object representing a perturbed OTE
    mirror state using mirror deployments defined in a YAML file.

    Parameters
    ----------
    deployments_file : str
        Path to YAML file containing deployments data. Expects format as
        produced by ``mirage.psf.deployments.generate_deployment_errors``
    out_dir : str
        Path to directory in which to save OPD FITS files
    save : bool, optional
        Denotes whether to save out the OPD (with and without tip/tilt)
        as FITs files

    Returns
    -------
    ote : webbpsf.opds.OTE_Linear_Model_WSS object
        WebbPSF OTE object describing perturbed OTE state with tip and tilt removed
    segment_tilts : numpy.ndarray
        List of X and Y tilts for each mirror segment, in microradians
    ote_opd_with_tilts : numpy.ndarray
        Array representing perturbed OTE OPD pupil before tip/tilt is removed
    """
    # Make an adjustable OTE object with WebbPSF
    nc = webbpsf.NIRCam()
    nc, ote = webbpsf.enable_adjustable_ote(nc)

    # Open existing file with previous deployments
    with open(deployments_file) as f:
        deployment_errors = yaml.unsafe_load(f)

    # Create OTE object and list of segment tilts
    ote, segment_tilts = apply_deployment_errors(ote,
                                                 deployment_errors,
                                                 out_dir=out_dir,
                                                 save=save)
    ote_opd_with_tilts = ote.opd  # Save array to plot below

    # Remove tip and tilt
    ote = remove_piston_tip_tilt(ote, out_dir=out_dir, save=save)

    return ote, segment_tilts, ote_opd_with_tilts
Exemple #17
0
def test_coarse_jitter():
    """ Test the jitter options for Coarse Point mode
    Simple test to verify functionality, and sanity check that more
    blur means a less sharp PSF.
    """
    nrc = webbpsf.NIRCam()
    nrc.include_si_wfe = False
    nrc.options['jitter']='PCS=Coarse'
    psf_coarse = nrc.calc_psf(nlambda=1, add_distortion=False, fov_pixels=30)

    nrc.options['jitter']='PCS=Coarse_Like_ITM'
    psf_coarse_like_itm = nrc.calc_psf(nlambda=1, add_distortion=False, fov_pixels=30)

    # These test values are handwaved without any particular rigor
    assert psf_coarse[0].header['JITRSTRL'] < 0.5, "Coarse point blurs the image less than expected"

    assert psf_coarse_like_itm[0].header['JITRSTRL'] < 0.05, "Coarse point (Like ITM) blurs the image less than expected"

    assert psf_coarse[0].header['JITRTYPE'].startswith('PCS Coarse'), "Header keyword not as expected"
    assert 'JITRCPV2' in psf_coarse[0].header, "Header doesn't contain the expected coarse point offset V2 keyword"
    assert 'JITRCPV3' in psf_coarse[0].header, "Header doesn't contain the expected coarse point offset V3 keyword"
Exemple #18
0
def test_enable_adjustable_ote():
    """ Some basic tests of the OTE LOM"""
    nc = webbpsf.NIRCam()
    nc, ote = webbpsf.enable_adjustable_ote(nc)

    # did this produce an OTE object?
    assert isinstance(ote, webbpsf.opds.OTE_Linear_Model_WSS), "Didn't get an OTE object back"

    # can we compute the rms?
    rms = ote.rms()

    # and can we move a mirror?

    ote.move_seg_local('B1', piston=10, clocking=200)

    assert ote.segment_state[6, 2] == 10, "Couldn't piston"
    assert ote.segment_state[6, 3] == 200, "Couldn't clock"

    # did that misalignment make things much worse?

    assert ote.rms() > rms*10, "Huge piston offset didn't make the WFE much worse"
def num_matrix_jwst():
    """
    Generate a numerical PASTIS matrix for a JWST coronagraph.

    All inputs are read from the (local) configfile and saved to the specified output directory.
    """

    import webbpsf
    from e2e_simulators import webbpsf_imaging as webbim

    # Keep track of time
    start_time = time.time()  # runtime is currently around 21 minutes
    print('Building numerical matrix for JWST\n')

    # Parameters
    resDir = os.path.join(CONFIG_INI.get('local', 'local_data_path'), 'active',
                          'matrix_numerical')
    which_tel = CONFIG_INI.get('telescope', 'name')
    nb_seg = CONFIG_INI.getint(which_tel, 'nb_subapertures')
    im_size_e2e = CONFIG_INI.getint('numerical', 'im_size_px_webbpsf')
    inner_wa = CONFIG_INI.getint(which_tel, 'IWA')
    outer_wa = CONFIG_INI.getint(which_tel, 'OWA')
    sampling = CONFIG_INI.getfloat('numerical', 'sampling')
    fpm = CONFIG_INI.get(which_tel, 'focal_plane_mask')  # focal plane mask
    lyot_stop = CONFIG_INI.get(which_tel, 'pupil_plane_stop')  # Lyot stop
    filter = CONFIG_INI.get(which_tel, 'filter_name')
    nm_aber = CONFIG_INI.getfloat('calibration', 'single_aberration') * u.nm
    wss_segs = webbpsf.constants.SEGNAMES_WSS_ORDER
    zern_max = CONFIG_INI.getint('zernikes', 'max_zern')
    zern_number = CONFIG_INI.getint('calibration', 'zernike')
    zern_mode = util.ZernikeMode(
        zern_number)  # Create Zernike mode object for easier handling
    wss_zern_nb = util.noll_to_wss(
        zern_number)  # Convert from Noll to WSS framework

    # If subfolder "matrix_numerical" doesn't exist yet, create it.
    if not os.path.isdir(resDir):
        os.mkdir(resDir)

    # If subfolder "OTE_images" doesn't exist yet, create it.
    if not os.path.isdir(os.path.join(resDir, 'OTE_images')):
        os.mkdir(os.path.join(resDir, 'OTE_images'))

    # If subfolder "psfs" doesn't exist yet, create it.
    if not os.path.isdir(os.path.join(resDir, 'psfs')):
        os.mkdir(os.path.join(resDir, 'psfs'))

    # If subfolder "darkholes" doesn't exist yet, create it.
    if not os.path.isdir(os.path.join(resDir, 'darkholes')):
        os.mkdir(os.path.join(resDir, 'darkholes'))

    # Create the dark hole mask.
    pup_im = np.zeros([im_size_e2e, im_size_e2e
                       ])  # this is just used for DH mask generation
    dh_area = util.create_dark_hole(pup_im, inner_wa, outer_wa, sampling)

    # Create a direct WebbPSF image for normalization factor
    fake_aber = np.zeros([nb_seg, zern_max])
    psf_perfect = webbim.nircam_nocoro(filter, fake_aber)
    normp = np.max(psf_perfect)
    psf_perfect = psf_perfect / normp

    # Set up NIRCam coro object from WebbPSF
    nc_coro = webbpsf.NIRCam()
    nc_coro.filter = filter
    nc_coro.image_mask = fpm
    nc_coro.pupil_mask = lyot_stop

    # Null the OTE OPDs for the PSFs, maybe we will add internal WFE later.
    nc_coro, ote_coro = webbpsf.enable_adjustable_ote(
        nc_coro)  # create OTE for coronagraph
    nc_coro.include_si_wfe = False  # set SI internal WFE to zero

    #-# Generating the PASTIS matrix and a list for all contrasts
    matrix_direct = np.zeros([nb_seg, nb_seg])  # Generate empty matrix
    all_psfs = []
    all_dhs = []
    all_contrasts = []

    print('nm_aber: {}'.format(nm_aber))

    for i in range(nb_seg):
        for j in range(nb_seg):

            print('\nSTEP: {}-{} / {}-{}'.format(i + 1, j + 1, nb_seg, nb_seg))

            # Get names of segments, they're being addressed by their names in the ote functions.
            seg_i = wss_segs[i].split('-')[0]
            seg_j = wss_segs[j].split('-')[0]

            # Put the aberration on the correct segments
            Aber_WSS = np.zeros([
                nb_seg, zern_max
            ])  # The Zernikes here will be filled in the WSS order!!!
            # Because it goes into _apply_hexikes_to_seg().
            Aber_WSS[i, wss_zern_nb - 1] = nm_aber.to(
                u.m
            ).value  # Aberration on the segment we're currently working on;
            # convert to meters; -1 on the Zernike because Python starts
            # numbering at 0.
            Aber_WSS[j, wss_zern_nb - 1] = nm_aber.to(
                u.m).value  # same for other segment

            # Putting aberrations on segments i and j
            ote_coro.reset(
            )  # Making sure there are no previous movements on the segments.
            ote_coro.zero()  # set OTE for coronagraph to zero

            # Apply both aberrations to OTE. If i=j, apply only once!
            ote_coro._apply_hexikes_to_seg(seg_i, Aber_WSS[
                i, :])  # set segment i  (segment numbering starts at 1)
            if i != j:
                ote_coro._apply_hexikes_to_seg(seg_j,
                                               Aber_WSS[j, :])  # set segment j

            # If you want to display it:
            # ote_coro.display_opd()
            # plt.show()

            # Save OPD images for testing
            opd_name = 'opd_' + zern_mode.name + '_' + zern_mode.convention + str(
                zern_mode.index) + '_segs_' + str(i + 1) + '-' + str(j + 1)
            plt.clf()
            ote_coro.display_opd()
            plt.savefig(os.path.join(resDir, 'OTE_images', opd_name + '.pdf'))

            print('Calculating WebbPSF image')
            image = nc_coro.calc_psf(fov_pixels=int(im_size_e2e),
                                     oversample=1,
                                     nlambda=1)
            psf = image[0].data / normp

            # Save WebbPSF image to disk
            filename_psf = 'psf_' + zern_mode.name + '_' + zern_mode.convention + str(
                zern_mode.index) + '_segs_' + str(i + 1) + '-' + str(j + 1)
            util.write_fits(psf,
                            os.path.join(resDir, 'psfs',
                                         filename_psf + '.fits'),
                            header=None,
                            metadata=None)
            all_psfs.append(psf)

            print('Calculating mean contrast in dark hole')
            dh_intensity = psf * dh_area
            contrast = np.mean(dh_intensity[np.where(dh_intensity != 0)])
            print('contrast:', contrast)

            # Save DH image to disk and put current contrast in list
            filename_dh = 'dh_' + zern_mode.name + '_' + zern_mode.convention + str(
                zern_mode.index) + '_segs_' + str(i + 1) + '-' + str(j + 1)
            util.write_fits(dh_intensity,
                            os.path.join(resDir, 'darkholes',
                                         filename_dh + '.fits'),
                            header=None,
                            metadata=None)
            all_dhs.append(dh_intensity)
            all_contrasts.append(contrast)

            # Fill according entry in the matrix
            matrix_direct[i, j] = contrast

    # Transform saved lists to arrays
    all_psfs = np.array(all_psfs)
    all_dhs = np.array(all_dhs)
    all_contrasts = np.array(all_contrasts)

    # Filling the off-axis elements
    matrix_two_N = np.copy(
        matrix_direct
    )  # This is just an intermediary copy so that I don't mix things up.
    matrix_pastis = np.copy(
        matrix_direct)  # This will be the final PASTIS matrix.

    for i in range(nb_seg):
        for j in range(nb_seg):
            if i != j:
                matrix_off_val = (matrix_two_N[i, j] - matrix_two_N[i, i] -
                                  matrix_two_N[j, j]) / 2.
                matrix_pastis[i, j] = matrix_off_val
                print('Off-axis for i{}-j{}: {}'.format(
                    i + 1, j + 1, matrix_off_val))

    # Normalize matrix for the input aberration
    matrix_pastis /= np.square(nm_aber.value)

    # Save matrix to file
    filename_matrix = 'PASTISmatrix_num_' + zern_mode.name + '_' + zern_mode.convention + str(
        zern_mode.index)
    util.write_fits(matrix_pastis,
                    os.path.join(resDir, filename_matrix + '.fits'),
                    header=None,
                    metadata=None)
    print('Matrix saved to:', os.path.join(resDir, filename_matrix + '.fits'))

    # Save the PSF and DH image *cubes* as well (as opposed to each one individually)
    util.write_fits(all_psfs,
                    os.path.join(resDir, 'psfs', 'psf_cube' + '.fits'),
                    header=None,
                    metadata=None)
    util.write_fits(all_dhs,
                    os.path.join(resDir, 'darkholes', 'dh_cube' + '.fits'),
                    header=None,
                    metadata=None)
    np.savetxt(os.path.join(resDir, 'contrasts.txt'), all_contrasts, fmt='%e')

    # Tell us how long it took to finish.
    end_time = time.time()
    print('Runtime for matrix_building.py:', end_time - start_time, 'sec =',
          (end_time - start_time) / 60, 'min')
    print('Data saved to {}'.format(resDir))
# And now, let's make a plot of the original image.
plt.figure(figsize=(9, 9))
imshow_norm(data, interval=PercentileInterval(99.), stretch=SqrtStretch())
plt.colorbar()
plt.savefig(data_file + '.png', dpi=300)
plt.clf()

# And let's make a plot of the image showing the positions of the objects.
plt.figure(figsize=(9, 9))
imshow_norm(data, interval=PercentileInterval(99.), stretch=SqrtStretch())
plt.scatter(init_tbl['x_0'], init_tbl['y_0'], s=10, color='black')
plt.colorbar()
plt.savefig(data_file + '_with_daostarfinder_objects.png', dpi=300)

# Let's go and load up the psf grid, which will be 3x3 for now.
nrc = webbpsf.NIRCam()
nrc.filter = hdu[0].header['FILTER']  #"F150W"
if (hdu[0].header['DETECTOR'] == 'NRCALONG'):
    nrc.detector = 'NRCA5'
elif (hdu[0].header['DETECTOR'] == 'NRCBLONG'):
    nrc.detector = 'NRCB5'
else:
    nrc.detector = hdu[0].header['DETECTOR']  #'NRCA3'
#nrc_grid = nrc.psf_grid(num_psfs=9, all_detectors=False)
nrc_grid = nrc.psf_grid(num_psfs=25,
                        all_detectors=False,
                        fov_pixels=33,
                        oversample=2)

eval_xshape = int(np.ceil(nrc_grid.data.shape[2] / nrc_grid.oversampling))
eval_yshape = int(np.ceil(nrc_grid.data.shape[1] / nrc_grid.oversampling))
Exemple #21
0
# Note: meant to just copy/paste into ipython command line

# Import WebbPSF and setup multiprocessing
import webbpsf
webbpsf.webbpsf_core.poppy.conf.use_fftw = False
webbpsf.webbpsf_core.poppy.conf.use_multiprocessing = True
ncores = 8
webbpsf.webbpsf_core.poppy.conf.n_processes = ncores 

inst = webbpsf.NIRCam()
inst.filter = "F430M"
inst.detector_position = (1024,1024)

# Baseline test: No SI WFE, no distortion
inst.include_si_wfe = False
%timeit psf = inst.calc_psf(add_distortion=False, monochromatic=4.3e-6)                                                                                
# Result: 911 ms ± 5.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit psf = inst.calc_psf(add_distortion=False, nlambda=ncores)                                                                                
# Result: 5.62 s ± 177 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

# Turn on SI WFE at center of detector (will use interpolation)
inst.include_si_wfe = True
%timeit psf = inst.calc_psf(add_distortion=False, monochromatic=4.3e-6)                                                                                
# Result: 1.41 s ± 12.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit psf = inst.calc_psf(add_distortion=False, nlambda=ncores)                                                                                
# Result: 6.1 s ± 96.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

# Use pixel (0,0) to force extrapolation algorithm
inst.detector_position = (0,0)
%timeit psf = inst.calc_psf(add_distortion=False, monochromatic=4.3e-6)                                                                                
# Result: 1.8 s ± 12.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
def generate_wavefront_errors(nb_of_maps, errors, nb_zernikes, path):

    import poppy, webbpsf
    import random
    import matplotlib.pyplot as plt

    # intial wavefront map
    nc = webbpsf.NIRCam()
    nc, ote = webbpsf.enable_adjustable_ote(nc)
    osys = nc._get_aberrations()

    # perturbed wavefront map
    nc_perturb = webbpsf.NIRCam()
    nc_perturb, ote_perturb = webbpsf.enable_adjustable_ote(nc_perturb)
    osys_perturb = nc_perturb._get_aberrations()

    # final wavefront map
    nc_final = webbpsf.NIRCam()
    nc_final, ote_final = webbpsf.enable_adjustable_ote(nc_final)
    osys_final = nc_final._get_aberrations()

    tab_opd_final = []
    for n, error in zip(range(nb_of_maps), errors):
        print(n, error)

        # change aberrations in wavefront map: example with random zernikes
        # this map will be our perturbation map and we will add it to the initial map with a certain weight

        # creating the perturbation map
        #weight = 0.2
        weight = error / 100
        for i in range(nb_zernikes):
            #tmp = random.randint(-10,10)
            tmp = random.randint(-1, 1)
            osys_perturb.zernike_coeffs[
                i] = weight * tmp * osys.zernike_coeffs[i]
            osys_final.zernike_coeffs[i] = osys.zernike_coeffs[
                i] + weight * tmp * osys.zernike_coeffs[i]

        # implementing and displaying the wavefront maps
        #display_ote_and_psf(nc, ote, title="Initial OPD and PSF")

        ote_perturb.reset()
        ote_perturb.move_global_zernikes(osys_perturb.zernike_coeffs[0:10])
        #display_ote_and_psf(nc_perturb, ote_perturb, title="Perturbed OPD and PSF")

        ote_final.reset()
        ote_final.move_global_zernikes(osys_final.zernike_coeffs[0:10])
        #display_ote_and_psf(nc_final, ote_final, title="Final OPD and PSF")

        rms = ote.rms()
        rms_perturb = ote_perturb.rms()
        rms_final = ote_final.rms()
        print(rms, rms_perturb, rms_final)
        print('')

        #print(osys.zernike_coeffs)
        #print('')
        #print(osys_perturb.zernike_coeffs)
        #print('')
        #print(osys_final.zernike_coeffs)
        #print('')

        opd = poppy.zernike.opd_from_zernikes(
            osys.zernike_coeffs[0:10],
            npix=1024,
            basis=poppy.zernike.zernike_basis_faster)

        opd_perturb = poppy.zernike.opd_from_zernikes(
            osys_perturb.zernike_coeffs[0:10],
            npix=1024,
            basis=poppy.zernike.zernike_basis_faster)

        opd_final = poppy.zernike.opd_from_zernikes(
            osys_final.zernike_coeffs[0:10],
            npix=1024,
            basis=poppy.zernike.zernike_basis_faster)

        #tab_opd_final.append(opd_final)

        write_fits(path + '_opd' + str(n) + '.fits', opd)
        write_fits(path + '_opd_perturb' + str(n) + '.fits', opd_perturb)
        write_fits(path + '_opd_final' + str(n) + '.fits', opd_final)

        #plt.figure(figsize=(12,4))
        #ax1 = plt.subplot(131)
        #ax1.imshow(opd)
        #ax1.set_title('initial wavefront map')
        #ax2 = plt.subplot(132)
        #ax2.imshow(opd_perturb)
        #ax2.set_title('perturbed wavefront map')
        #ax3 = plt.subplot(133)
        #ax3.imshow(opd_final)
        #ax3.set_title('sum of maps')
        #plt.show()

        wavefront_error = mse(np.nan_to_num(opd), np.nan_to_num(opd_final))
        print('mse', error, wavefront_error)
        print("MSE: %.2f" % (wavefront_error * 100))

    return tab_opd_final
Exemple #23
0
 def resetPSF(self):
     import webbpsf
     if self.filter not in self.FILTERS:
         raise ValueError("Filter %s is not a valid %s filter" %
                          (self.filter, self.classname))
     have_psf = False
     if os.path.exists(os.path.join(self.out_path, "psf_cache")):
         if os.path.exists(
                 os.path.join(
                     self.out_path, "psf_cache",
                     "psf_{}_{}_{}.fits".format("NIRCam", self.filter,
                                                self.oversample))):
             with pyfits.open(
                     os.path.join(
                         self.out_path,
                         "psf_cache", "psf_{}_{}_{}.fits".format(
                             "NIRCam", self.filter,
                             self.oversample))) as psf:
                 if psf[0].header['VERSION'] >= webbpsf.__version__ and (
                         self.psf_commands is None
                         or self.psf_commands == ''):
                     self.psf = AstroImage(data=psf[0].data,
                                           detname="NIRCam {} PSF".format(
                                               self.filter),
                                           logger=self.logger)
                     have_psf = True
     if not have_psf:
         base_state = self.getState()
         self.updateState(
             base_state +
             "<br /><span class='indented'>Generating PSF</span>")
         ins = webbpsf.NIRCam()
         if self.psf_commands is not None and self.psf_commands != '':
             for attribute, value in self.psf_commands.iteritems():
                 setattr(ins, attribute, value)
         ins.filter = self.filter
         max_safe_size = int(
             np.floor(30. * self.PHOTPLAM[self.filter] /
                      (2. * self.SCALE[0])))
         max_ins_size = max(self.DETECTOR_SIZE) * self.oversample
         max_conv_size = int(np.floor(2048 / self.oversample))
         self._log(
             "info", "PSF choosing between {}, {}, and {}".format(
                 max_safe_size, max_ins_size, max_conv_size))
         psf = ins.calcPSF(oversample=self.oversample,
                           fov_pixels=min(max_safe_size, max_ins_size,
                                          max_conv_size))
         psf[0].header['VERSION'] = webbpsf.__version__
         if os.path.exists(os.path.join(self.out_path, "psf_cache")):
             dest = os.path.join(
                 self.out_path, "psf_cache",
                 "psf_{}_{}_{}.fits".format("NIRCam", self.filter,
                                            self.oversample))
             pyfits.writeto(dest,
                            psf[0].data,
                            header=psf[0].header,
                            overwrite=True)
         self.psf = AstroImage(data=psf[0].data,
                               detname="NIRCam %s PSF" % (self.filter),
                               logger=self.logger)
         self.updateState(base_state)
def generate_wavefront_errors_correction(nb_of_maps, errors, nb_zernikes):
    import poppy, webbpsf
    import matplotlib.pyplot as plt

    # intial wavefront map
    nc = webbpsf.NIRCam()
    nc, ote = webbpsf.enable_adjustable_ote(nc)
    osys = nc._get_aberrations()

    # final wavefront map
    nc_final = webbpsf.NIRCam()
    nc_final, ote_final = webbpsf.enable_adjustable_ote(nc_final)
    osys_final = nc_final._get_aberrations()

    tab_wavefront_error = np.zeros(nb_of_maps)
    tab_error = np.zeros(nb_of_maps)
    for n, error in zip(range(nb_of_maps), errors):
        print(n, error)
        #print(zip(range(nb_of_maps)))
        #print(errors)
        # change aberrations in wavefront map: example with random zernikes
        # this map will be our perturbation map and we will add it to the initial map with a certain weight

        # creating the perturbation map
        #weight = 0.2
        #weight = error/100
        osys_corrected = osys.zernike_coeffs.copy()

        for i in range(nb_zernikes):
            if i < error + 1:
                osys_corrected[i] = 0

        print(osys.zernike_coeffs)
        print(osys_corrected)

        opd = poppy.zernike.opd_from_zernikes(
            osys.zernike_coeffs,
            npix=1024,
            basis=poppy.zernike.zernike_basis_faster)

        opd_corrected = poppy.zernike.opd_from_zernikes(
            osys_corrected,
            npix=1024,
            basis=poppy.zernike.zernike_basis_faster)

        wavefront_error = mse(np.nan_to_num(opd), np.nan_to_num(opd_corrected))
        print('mse', error, wavefront_error)

        #tab_opd_final.append(opd_final)

        #write_fits('_opd'+str(n)+'.fits',opd)
        #write_fits('_opd_corrected'+str(n)+'.fits',opd_corrected)

        plt.figure(figsize=(12, 4))
        ax1 = plt.subplot(131)
        #ax1.imshow(opd,vmin=np.min(opd),vmax=np.max(opd))
        ax1.imshow(opd)
        ax1.set_title('initial wavefront map')
        ax2 = plt.subplot(132)
        #ax2.imshow(opd_corrected,vmin=np.min(opd),vmax=np.max(opd))
        ax2.imshow(opd_corrected)
        ax2.set_title('corrected wavefront map')
        ax3 = plt.subplot(133)
        ax3.imshow(opd - opd_corrected)
        ax3.set_title('sum of maps')
        plt.show()

        tab_wavefront_error[n] = wavefront_error
        tab_error[n] = error

    return tab_wavefront_error, tab_error
Exemple #25
0
def test_visualize_wfe_budget():
    """Basic test that the visual optical budget functionality at least runs without raising an error or exception
    No actual checking of the output plots is performed.
    """
    nrc = webbpsf.NIRCam()
    nrc.visualize_wfe_budget()
Exemple #26
0
def set_sim_params(args):
    #logging.basicConfig(level=logging.INFO, format='%(name)-12s: %(levelname)-8s %(message)s',)
    print "WebbPSF",pkg_resources.get_distribution("webbpsf").version," Poppy",pkg_resources.get_distribution("poppy").version
    #webbpsf.setup_logging(level='ERROR')

    nruns= args.nruns            #
    filt = args.f.upper()        #e.g. F430M
    mask_coron=args.mask.upper() #e.g. MASK430R
    pupil_stop=args.stop.upper() #e.g. CIRCLYOT
    instr=args.I.upper()

    if instr=='NIRCAM': lambda0=float(filt[1:4])/100
    elif instr=='MIRI': lambda0=float(filt[1:5])/100
    jitter= args.jitter 
    grid_step=args.gstep
    gridpoints=args.gnpts
    side=int(np.sqrt(gridpoints))
    max_step=np.floor(side/2)
    rms= args.rms 

    fovarcsec = args.fov #default 7.04 
    #fovpixels = 64

    sigmaTA = 4.7
    sigmaFSM= 2.0


    if instr=='NIRCAM': img=webbpsf.NIRCam()
    elif instr=='MIRI': img=webbpsf.MIRI()

    if filt not in img.filter_list:
        if instr == 'MIRI'  : print "\nFilter "+mask_coron+" not available. Select from: ",[f for f in img.filter_list if "C" in f[-1]],'\n'
        if instr == 'NIRCAM': print "\nFilter "+mask_coron+" not available. Select from: ",img.filter_list,'\n'
        sys.exit()

    if mask_coron not in img.image_mask_list:
        print "\nCoronagraphic mask "+mask_coron+" not available. Select from: ",img.image_mask_list,'\n'
        sys.exit()

    if pupil_stop not in img.pupil_mask_list[:2]:
        print "\nPupil mask "+pupil_stop+" not available. Select from: ",img.pupil_mask_list[:2],'\n' 
        sys.exit()

    opd_rms = [int(img.opd_list[i][-8:-5]) for i in xrange(len(img.opd_list))]
    if rms not in opd_rms:
        print "\nOPD rms ("+str(rms)+") not available. Select from: ",opd_rms,'\n'
        sys.exit()
    if instr=='NIRCAM' and args.noopd==False: opd='/Users/lajoie/WebbPSF/webbpsf-data/NIRCam/OPD/OPD_RevV_nircam_'+str(rms)+'.fits'
    if instr=='MIRI'   and args.noopd==False: opd='/Users/lajoie/WebbPSF/webbpsf-data/MIRI/OPD/OPD_RevV_miri_'+str(rms)+'.fits'

    img.filter=filt
    img._rotation=0.
    img.options["output_mode"]='detector sampled'
    if args.noopd==False: 
        img.pupilopd = (opd, 0) #select FITS extension for OPD
        print opd

    if instr=='NIRCAM': 
        outdir='./Results'+'/'+instr+'/'+filt+'_'+mask_coron+'/DitherStep'+str(int(grid_step))+'/Jitter'+str(int(jitter))+'/rms'+str(rms)+'nm/'
    else: outdir='./Results'+'/'+instr+'/'+filt+'/DitherStep'+str(int(grid_step))+'/Jitter'+str(int(jitter))+'/rms'+str(rms)+'nm/'

    if gridpoints != 9: 
        raw_input(" *** generating grids of %i points: ENTER to continue " %gridpoints)
        outdir = outdir[:-1]+"_Grid_%i_pts/" %gridpoints
        

    if not os.path.exists(outdir):
        os.makedirs(outdir)

    x0,y0 = get_source_offset(lambda0, filt, mask_coron)
    if np.abs(x0) > fovarcsec/2: 
        print "\n optimal bar occulter offset out of FOV. Please fix FOV in code (fovarcsec).\n   x_offset=%f Total fov=%f\n" %(x0,fovarcsec)
        sys.exit()

    generate_PSF(img, outdir, nruns, x0, y0, grid_step, side, max_step, sigmaTA, sigmaFSM, mask_coron, pupil_stop, lambda0, jitter, rms, fovarcsec)

    return
Exemple #27
0
def test_segment_tilt_signs(fov_pix=50, plot=False):
    """Test that segments move in the direction expected when tilted.

    The local coordinate systems are non-obvious, to say the least. This verifies
    sign conventions and coordinates are consistent in the linear optical model and
    optical propagation code.

    """

    if plot:
        fig, axs = plt.subplots(3, 5,
                                figsize=(14,
                                         9))  #, sharex = True, sharey = True)

    nrc = webbpsf.NIRCam()

    ote = webbpsf.opds.OTE_Linear_Model_WSS()
    nrc.include_si_wfe = False  # not relevant for this test

    tilt = 1.0

    # We im for relatively minimalist PSF calcs, to reduce test runtime
    psf_kwargs = {
        'monochromatic': 2e-6,
        'fov_pixels': fov_pix,
        'oversample': 1,
        'add_distortion': False
    }

    # Which way are things expected to move?
    #
    # A1:  +X rotation -> -Y pixels (DMS), +Y rotation -> -X pixels
    # B1: +X rotation -> +Y pixels, +Y rotation -> +X pixels
    # C1: +X rotation -> +X/+Y pixels, +Y rotation -> -Y/+X pixels
    # (for C1, A/B means A is the sqrt(3)/2 component, B is the 1/2 component)
    #
    # The above derived from Code V models by R. Telfer, subsequently cross checked by Perrin

    for i, iseg in enumerate(['A1', 'B1', 'C1']):
        ote.zero()

        pupil = webbpsf.webbpsf_core.one_segment_pupil(iseg)

        ote.amplitude = pupil[0].data
        nrc.pupil = ote

        # CENTERED PSF:
        psf = nrc.calc_psf(**psf_kwargs)
        cen_ref = webbpsf.measure_centroid(psf, boxsize=10, threshold=1)

        ote.move_seg_local(iseg, xtilt=tilt)
        # XTILT PSF:
        psfx = nrc.calc_psf(**psf_kwargs)
        cen_xtilt = webbpsf.measure_centroid(psfx, boxsize=10, threshold=1)

        if iseg.startswith("A"):
            assert cen_xtilt[0] < cen_ref[
                0], "Expected A1:  +X rotation -> -Y pixels (DMS coords)"
            assert np.isclose(
                cen_xtilt[1], cen_ref[1],
                atol=1), "Expected A1:  +X rotation -> no change in X"
        elif iseg.startswith("A"):
            assert cen_xtilt[0] > cen_ref[
                0], "Expected B1: +X rotation -> +Y pixels(DMS coords)"
            assert np.isclose(
                cen_xtilt[1], cen_ref[1],
                atol=1), "Expected B1:  +X rotation -> no change in Y"
        elif iseg.startswith("C"):
            assert cen_xtilt[0] > cen_ref[
                0], "Expected C1: +X rotation -> +X/+Y pixels"
            assert cen_xtilt[1] > cen_ref[
                1], "Expected C1: +X rotation -> +X/+Y pixels"

        if plot:
            axs[i, 0].imshow(psf[0].data,
                             norm=matplotlib.colors.LogNorm(vmax=1e-2,
                                                            vmin=1e-5),
                             origin="lower")
            axs[i, 0].set_title(iseg + ": centered")
            axs[i, 0].axhline(y=fov_pix / 2)
            axs[i, 0].axvline(x=fov_pix / 2)
            # PLOT RESULTING OPD:
            im = axs[i, 1].imshow(ote.opd,
                                  vmin=-4e-6,
                                  vmax=4e-6,
                                  origin="lower")
            axs[i, 1].set_title("OPD (yellow +)")
            axs[i, 2].imshow(psfx[0].data,
                             norm=matplotlib.colors.LogNorm(vmax=1e-2,
                                                            vmin=1e-5),
                             origin="lower")
            axs[i, 2].set_title(iseg + ": xtilt {} um".format(tilt))
            axs[i, 2].axhline(y=fov_pix / 2)
            axs[i, 2].axvline(x=fov_pix / 2)

        ote.zero()
        ote.move_seg_local(iseg, ytilt=tilt)
        # YTILT PSF:
        psfy = nrc.calc_psf(**psf_kwargs)
        cen_ytilt = webbpsf.measure_centroid(psfy, boxsize=10, threshold=1)

        if iseg.startswith("A"):
            assert cen_ytilt[1] < cen_ref[
                1], "Expected A1:  +Y rotation -> -X pixels (DMS coords)"
            assert np.isclose(
                cen_ytilt[0], cen_ref[0],
                atol=1), "Expected A1:  +Y rotation -> no change in Y"
        elif iseg.startswith("A"):
            assert cenyxtilt[0] > cen_ref[
                0], "Expected B1: +Y rotation -> +X pixels(DMS coords)"
            assert np.isclose(
                cen_xtilt[0], cen_ref[0],
                atol=1), "Expected B1:  +Y rotation -> no change in Y"
        elif iseg.startswith("C"):
            assert cen_ytilt[0] < cen_ref[
                0], "Expected C1: +Y rotation -> -Y/+X pixels"
            assert cen_ytilt[1] > cen_ref[
                1], "Expected C1: +Y rotation -> -Y/+X pixels"

        # PLOT RESULTING OPD:
        if plot:
            im = axs[i, 3].imshow(ote.opd,
                                  vmin=-4e-6,
                                  vmax=4e-6,
                                  origin="lower")
            axs[i, 3].set_title("OPD (yellow +)")
            axs[i, 4].imshow(psfy[0].data,
                             norm=matplotlib.colors.LogNorm(vmax=1e-2,
                                                            vmin=1e-5),
                             origin="lower")
            axs[i, 4].set_title(iseg + ": ytilt {} um".format(tilt))
            axs[i, 4].axhline(y=fov_pix / 2)
            axs[i, 4].axvline(x=fov_pix / 2)
Exemple #28
0
        zern_number)  # Convert from Noll to WSS framework

    # If subfolder "calibration" doesn't exist yet, create it.
    if not os.path.isdir(outDir):
        os.mkdir(outDir)

    # If subfolder "images" in "calibration" doesn't exist yet, create it.
    if not os.path.isdir(os.path.join(outDir, 'images')):
        os.mkdir(os.path.join(outDir, 'images'))

    # Create Zernike mode object for easier handling
    zern_mode = util.ZernikeMode(zern_number)

    # Create NIRCam objects, one for perfect PSF and one with coronagraph
    print('Setting up the E2E simulation.')
    nc = webbpsf.NIRCam()
    # Set filter
    nc.filter = filter

    # Same for coronagraphic case
    nc_coro = webbpsf.NIRCam()
    nc_coro.filter = filter

    # Add coronagraphic elements to nc_coro
    nc_coro.image_mask = fpm
    nc_coro.pupil_mask = lyot_stop

    # Null the OTE OPDs for the PSFs, maybe we will add internal WFE later.
    nc, ote = webbpsf.enable_adjustable_ote(nc)  # create OTE for default PSF
    nc_coro, ote_coro = webbpsf.enable_adjustable_ote(
        nc_coro)  # create OTE for coronagraph
def main():

    # Here's the path to the data
    data_path = '/data1/car_24_apt_01073/mirage/reduced/'

    nrc_sca = 'nrca1'

    data_files = [
        'jw01073001001_01101_00001_' + nrc_sca + '_cal',
        'jw01073001001_01101_00002_' + nrc_sca + '_cal',
        'jw01073001002_01101_00003_' + nrc_sca + '_cal',
        'jw01073001002_01101_00004_' + nrc_sca + '_cal',
        'jw01073001003_01101_00005_' + nrc_sca + '_cal',
        'jw01073001003_01101_00006_' + nrc_sca + '_cal',
        'jw01073001004_01101_00007_' + nrc_sca + '_cal',
        'jw01073001004_01101_00008_' + nrc_sca + '_cal'
    ]

    # Let's load in the first file
    hdu = fits.open(data_path + data_files[0] + '.fits')

    # Let's start by loading up the psf grid, which will be 3x3 for now.
    nrc = webbpsf.NIRCam()
    nrc.filter = hdu[0].header['FILTER']  #"F150W"
    if (hdu[0].header['DETECTOR'] == 'NRCALONG'):
        nrc.detector = 'NRCA5'
    elif (hdu[0].header['DETECTOR'] == 'NRCBLONG'):
        nrc.detector = 'NRCB5'
    else:
        nrc.detector = hdu[0].header['DETECTOR']  #'NRCA3'
    #nrc_grid = nrc.psf_grid(num_psfs=9, all_detectors=False)
    nrc_grid = nrc.psf_grid(num_psfs=25,
                            all_detectors=False,
                            fov_pixels=33,
                            oversample=2)

    # And let's close the first data file.
    hdu.close()

    # Now, let's spawn processes that will run on the various images.

    number_of_processes_at_the_same_time = 8

    process_list = []
    for i in range(number_of_processes_at_the_same_time):
        data_file_name = data_files[i]
        hdu = fits.open(data_path + data_files[i] + '.fits')
        image = ImageModel(data_path + data_files[i] + '.fits')
        data_array = hdu[1].data
        pheader = hdu[0].header
        fheader = hdu[1].header
        hdu.close()
        #print(data_file_name)
        p = multiprocessing.Process(target=psf_fit,
                                    args=(data_array, data_file_name, nrc_grid,
                                          fheader, image))
        p.start()
        process_list.append(p)

    for process in process_list:
        process.join()
Exemple #30
0
def main():
    # save the figures
    figdir = 'Figures'

    # just listing the wide filters
    nircam_bandpasses = 'F070W,F090W,F115W,F150W,F200W,F277W,F356W,F444W'
    miri_bandpasses = 'F560W,F770W,F1000W,F1130W,F1280W,F1500W,F1800W,F2100W,F2550W'
    nircam_bandpasses = nircam_bandpasses.split(',')
    miri_bandpasses = miri_bandpasses.split(',')

    # configure the instruments
    nircam = W.NIRCam()
    miri = W.MIRI()

    # load the bandpasses
    bandpasses = {}
    for bp in nircam_bandpasses:
        nircam.filter = bp
        bpmodel = nircam._getSynphotBandpass(nircam.filter)
        bandpasses[bp] = bpmodel
    for bp in miri_bandpasses:
        miri.filter = bp
        bpmodel = miri._getSynphotBandpass(miri.filter)
        bandpasses[bp] = bpmodel

    # we just need a couple of bandpasses for testing
    use_bandpasses = nircam_bandpasses + miri_bandpasses[0:3]

    # init the kilonova model and create some arrays to store output
    kn = Kilonova()

    # do this for a few distances
    for j, dmpc in enumerate(DISTANCE):
        time = []
        rfphase = []
        flux = {}

        if j == 0:
            fig = plt.figure(figsize=(8, 15))
            ax = fig.add_subplot(1, 1, 1)

        for i, phase in enumerate(kn._times):
            # get the kilonova model and spectrum for this phase and distance
            lam, flam = kn.get_model(phase)
            lamz, fnorm = kn.get_norm_model(phase, dmpc)
            name = 'kilonova_{:+.2f}'.format(phase)
            spec = S.ArraySpectrum(wave=lamz,
                                   flux=fnorm,
                                   waveunits='angstrom',
                                   fluxunits='flam',
                                   name=name)

            # get synthetic mags in each passband
            for bp in use_bandpasses:
                passband = bandpasses[bp]
                obs = S.Observation(spec, passband, force='taper')
                try:
                    mag = obs.effstim('abmag')
                except ValueError as e:
                    mag = np.nan
                thispb = flux.get(bp)
                if thispb is None:
                    thispb = [
                        mag,
                    ]
                else:
                    thispb.append(mag)
                flux[bp] = thispb

            # keep a track of rest-frame phase + observer frame days (should make much difference at these distances)
            dist = c.Distance(dmpc * u.megaparsec)
            z = dist.z
            rfphase.append(phase)
            time.append(phase * (1. + z))

            # write output photometry tables
            if i % 5 == 0:
                # convert flam -> fnu -> mJy (YUCK)
                lam_micron = lamz * ANGSTROM_TO_MICRON
                f_nu = (((lamz * ANGSTROM_TO_CM)**2.) /
                        SPEED_OF_LIGHT) * fnorm / ANGSTROM_TO_MICRON
                f_mjy = f_nu * FNU_TO_MJY
                table_name = 'Tables/kilonova_orig_{}Mpc_p{:+.2f}.txt'.format(
                    dmpc, phase)
                this_spec = at.Table([lam_micron, f_mjy],
                                     names=['wave_micron', 'flux_mjy'])
                this_spec.sort('wave_micron')
                this_spec.write(table_name,
                                format='ascii.fixed_width',
                                delimiter=' ',
                                overwrite='True')

                # plot output spectral sequence
                if j == 0:
                    fplot = flam / flam.mean()
                    ax.plot(lam * ANGSTROM_TO_MICRON, fplot + 180 - i, 'k-')

        # finalize spectral sequence plot
        if j == 0:
            ax.tick_params(axis='both', which='major', labelsize='large')
            ax.set_xlabel(r'Rest Wavelength ($\mu$m)', fontsize='xx-large')
            ax.set_ylabel(r'Relative F$_{\lambda}$ + constant',
                          fontsize='xx-large')
            ax.set_xlim(0.5, 9.5)
            fig.tight_layout(rect=[0, 0, 1, 0.96])
            plt.savefig('{}/kilonova_spec.pdf'.format(figdir))
            plt.close(fig)

        # dump output mag tables
        arrays = [
            rfphase,
            time,
        ] + [flux[bp] for bp in use_bandpasses]
        names = ['rfphase', 'ofphase'] + [bp for bp in use_bandpasses]
        out = at.Table(arrays, names=names)
        out.write('Tables/kilonova_phottable_{}Mpc.txt'.format(dmpc),
                  delimiter=' ',
                  format='ascii.fixed_width',
                  overwrite=True)

        # plot up the lightcurves
        npbs = len(use_bandpasses)
        color = iter(plt.cm.tab20(np.linspace(0, 1, npbs)))
        with PdfPages('{}/kilonova_phot_{}Mpc.pdf'.format(figdir,
                                                          dmpc)) as pdf:
            fig = plt.figure(figsize=(10, 10))
            for i, bp in enumerate(use_bandpasses):

                # save four passbands per page
                if i % 4 == 0 and i > 0:
                    fig.suptitle(
                        'Kilonova Synthetic Photometry {} Mpc'.format(dmpc),
                        fontsize='xx-large')
                    fig.tight_layout(rect=[0, 0, 1, 0.93])
                    pdf.savefig(fig)
                    plt.close(fig)
                    fig = plt.figure(figsize=(10, 10))

                # plot up a passband
                ax = fig.add_subplot(2, 2, i % 4 + 1)
                thiscol = next(color)
                ax.plot(out['ofphase'],
                        out[bp],
                        marker='o',
                        linestyle='-',
                        lw=0.5,
                        label=bp,
                        color=thiscol)
                ax.tick_params(axis='both', which='major', labelsize='large')
                ax.set_ylabel('{} (AB mag)'.format(bp), fontsize='xx-large')
                ax.set_xlabel('Observer-frame Phase (Days)',
                              fontsize='xx-large')
                ax.legend(loc='upper right', frameon=False)
                ymin, ymax = ax.get_ylim()
                ax.set_ylim((ymax, ymin))

                # finalize lightcurve plot
                if i == npbs - 1:
                    fig.suptitle(
                        'Kilonova Synthetic Photometry {} Mpc'.format(dmpc),
                        fontsize='xx-large')
                    fig.tight_layout(rect=[0, 0, 1, 0.93])
                    pdf.savefig(fig)
                    plt.close(fig)