Example #1
0
def niriss_background_scaling(param_dict, detector, module):
    """Determine the scaling factor needed to translate the pre-existing
    NIRISS WFSS background image to the requested signal level, which is
    one of "low", "medium", or "high", as with the ETC.

    Parameters
    ----------
    param_dict : dict
        Dictionary of observation information from an input yaml file

    detector : str
        Name of detector, e.g. "NRCA1"

    module : str
        Name of module, e.g. "A"

    Returns
    -------
    ratio : float
        Ratio of the background value at the pivot wavelength that
        corresponds to the requested level, ratioed to that for the
        "medium" level, which is what was used to create the NIRISS
        WFSS background images.
    """
    # Generate background spectra for all days
    background = jbt.background(param_dict['Telescope']['ra'],
                                param_dict['Telescope']['dec'], 4.)

    # Determine which optical element wheel contains the filter we want
    # to use for background estimation
    if param_dict['Readout']['pupil'][0].upper() == 'F':
        usefilt = 'pupil'
    else:
        usefilt = 'filter'

    # Get basic flux calibration information
    vegazp, photflam, photfnu, pivot_wavelength = fluxcal_info(
        param_dict, usefilt, detector, module)

    # Extract the spectrum value across all days at the pivot wavelength
    wave_diff = np.abs(background.bkg_data['wave_array'] - pivot_wavelength)
    bkgd_wave = np.where(wave_diff == np.min(wave_diff))[0][0]
    bkgd_at_pivot = background.bkg_data['total_bg'][:, bkgd_wave]

    # Now sort and determine the low/medium/high levels
    low, medium, high = find_low_med_high(bkgd_at_pivot)

    # Find the value based on the level in the yaml file
    background_level = param_dict['simSignals']['bkgdrate'].lower()
    if background_level == "low":
        level_value = low
    elif background_level == "medium":
        level_value = medium
    elif background_level == "high":
        level_value = high
    else:
        raise ValueError((
            "ERROR: Unrecognized background value: {}. Must be low, medium, or high"
            .format(params_dict['simSignals']['bkgdrate'])))
    return level_value
Example #2
0
def test_fluxcal_info():
    """Test that zeropoint information for the exposure is correctly retrieved
    """
    params = {
        'Inst': {
            "instrument": 'NIRCAM'
        },
        'Readout': {
            'filter': 'F200W',
            'pupil': 'CLEAR'
        },
        'Reffiles': {
            'flux_cal': os.path.join(CONFIG_DIR, 'NIRCam_zeropoints.list')
        }
    }

    detector = 'NRCA1'
    module = 'A'
    vegazp, photflam, photfnu, pivot = flux_cal.fluxcal_info(
        params['Reffiles']['flux_cal'], 'NIRCAM', params['Readout']['filter'],
        params['Readout']['pupil'], detector, module)

    assert vegazp == 25.3677
    assert photflam == 4.1652e-21
    assert photfnu == 5.4949e-31
    assert pivot == 1.9887
Example #3
0
def source_mags_to_ghost_mags(row,
                              flux_cal_file,
                              magnitude_system,
                              gap_summary_file,
                              log_skipped_filters=False):
    """Works only for NIRISS. Given a row from a source catalog, create a ghost source
    catalog containing the magnitudes of the ghost associated with the source in all
    filters contained in the original catalog.

    Parameters
    ----------
    row : astropy.table.Row
        Row containing a source from a source catalog

    flux_cal_file : str
        Name of file containing the flux calibration information
        for all filters

    magnitude_system : str
        Magnitude system of the magnitudes in the input catalog

    gap_summary_file : str
        Name of file that controls ghost locations relative to sources

    log_skipped_filters : bool
        If False, notices of skipped magnitude translations will not be logged.
        This is convenient for catalogs with lots of sources, because otherwise
        there can be many repeats of the message.

    Returns
    -------
    ghost_row : astropy.table.Table
        Single row table containing the ghost magnitudes in all filters

    skipped_non_niriss_cols : bool
        If True, non NIRISS magnitude columns were found and skipped

    """
    logger = logging.getLogger(
        'mirage.ghosts.niriss_ghosts.source_mags_to_ghost_mags')
    mag_cols = [key for key in row.colnames if 'magnitude' in key]
    ghost_row = Table()
    skipped_non_niriss_cols = False
    for mag_col in mag_cols:

        # Ghost magnitudes can currently be calcuated only for NIRISS
        if 'nircam' in mag_col.lower() or 'fgs' in mag_col.lower():
            skipped_non_niriss_cols = True
            continue

        col_parts = mag_col.split('_')
        if len(col_parts) == 3:
            filt = col_parts[1]
        else:
            raise ValueError(
                'Unsupported magnitude column name for ghost conversion: {}'.
                format(mag_col))

        wave = int(filt[1:4])
        if wave > 200:
            filter_value = filt
            pupil_value = 'CLEARP'
        else:
            filter_value = 'CLEAR'
            pupil_value = filt

        # Get basic flux calibration information for the filter
        vegazeropoint, photflam, photfnu, pivot = \
            fluxcal_info(flux_cal_file, 'niriss', filter_value, pupil_value, 'NIS', 'N')

        # Convert source magnitude to count rate
        mag = float(row[mag_col])
        countrate = magnitude_to_countrate('niriss',
                                           filt,
                                           magnitude_system,
                                           mag,
                                           photfnu=photfnu,
                                           photflam=photflam,
                                           vegamag_zeropoint=vegazeropoint)

        # Get the count rate associated with the ghost
        _, _, ghost_countrate = get_ghost(
            1024,
            1024,
            countrate,
            filter_value,
            pupil_value,
            gap_summary_file,
            log_skipped_filters=log_skipped_filters)

        # Convert count rate to magnitude
        ghost_mag = countrate_to_magnitude('niriss',
                                           filt,
                                           magnitude_system,
                                           ghost_countrate,
                                           photfnu=photfnu,
                                           photflam=photflam,
                                           vegamag_zeropoint=vegazeropoint)
        ghost_row[mag_col] = [ghost_mag]
    return ghost_row, skipped_non_niriss_cols
Example #4
0
    def crop_and_blot(self):
        """MAIN FUNCTION

        Extract an area of the input mosaic fits file that is slightly larger
        than the desired aperture, and use the JWST pipeline version of blot (called
        resample) to resample this to the correct pixel scale and introduce the
        proper distortion for the requested detector
        """
        # Initialize log
        self.logger = logging.getLogger('mirage.seed_image.fits_seed_image')
        self.logger.info('\n\nRunning fits_seed_image....\n')
        self.logger.info('using parameter file: {}\n'.format(self.paramfile))
        self.logger.info(
            'Original log file name: ./{}'.format(STANDARD_LOGFILE_NAME))

        self.detector_channel_value()
        self.distortion_file_value()
        module = self.module_value()
        self.siaf = pysiaf.Siaf(self.instrument)

        # Get information on the aperture
        self.aperture_info()
        xstart = self.subarr_bounds['xstart']
        ystart = self.subarr_bounds['ystart']
        xdim = self.subarr_bounds['xend'] - xstart + 1
        ydim = self.subarr_bounds['yend'] - ystart + 1

        # Flux calibration information
        self.flux_cal_file = self.expand_config_paths(self.flux_cal_file,
                                                      'flux_cal')
        vegazp, self.photflam, self.photfnu, self.pivot = fluxcal_info(
            self.flux_cal_file, self.instrument, self.filter, self.pupil,
            self.detector, module)

        # Get information on the instrument used to create the mosaic
        self.mosaic_metadata = tools.get_psf_metadata(self.mosaic_file)

        # Check that the FWHM of the mosaic PSF is smaller than the PSF
        # of the JWST PSF. If the opposite is true, we can't create a
        # matching kernel
        if self.mosaic_fwhm_units == 'arcsec':
            mosaic_fwhm_arcsec = copy.deepcopy(self.mosaic_fwhm)
            self.mosaic_fwhm /= self.mosaic_metadata['pix_scale1']

        # Identify the correct PSF for the JWST instrument/detector
        # Let's just read in the psf_wings file, which contains the PSF
        # at the center of the detector. It doesn't make much sense to
        # worry about spatially-dependent PSFs in this case
        self.jwst_psf = get_psf_wings(
            self.instrument, self.detector, self.filter, self.pupil,
            self.params['simSignals']['psfwfe'],
            self.params['simSignals']['psfwfegroup'],
            os.path.join(self.params['simSignals']['psfpath'], 'psf_wings'))
        self.outscale1, self.outscale2 = self.find_jwst_pixel_scale()

        # Find the FWHM of the JWST PSF
        j_yd, j_xd = self.jwst_psf.shape
        mid_y = j_yd // 2
        mid_x = j_xd // 2
        box = self.jwst_psf[mid_y - 10:mid_y + 11, mid_x - 10:mid_x + 11]
        jwst_x_fwhm, jwst_y_fwhm = tools.measure_fwhm(box)
        jwst_y_fwhm_arcsec = jwst_y_fwhm * self.outscale2
        self.logger.info('JWST FWHM in pix: {}'.format(jwst_y_fwhm))
        self.logger.info('JWST FWHM in arcsec: {}'.format(jwst_y_fwhm_arcsec))

        # If the FWHM of the mosaic image is larger than that of the JWST
        # PSF, then we cannot continue because we cannot create a matching
        # PSF kernel to use for convolution
        if mosaic_fwhm_arcsec > jwst_y_fwhm_arcsec:
            raise ValueError(
                ("ERROR: FWHM of the mosaic image is larger than that of "
                 "the JWST PSF. Unable to create a matching PSF kernel "
                 "using photutils. \nMosaic FWHM: {}\nJWST FWHM: {}".format(
                     self.mosaic_fwhm, jwst_y_fwhm_arcsec)))

        # The JWST PSF as read in is large (399 x 399 pixels). Crop to
        # something reasonable so that the convolution doesn't take
        # forever
        half_width = 50
        self.jwst_psf = self.jwst_psf[mid_y - half_width:mid_y + half_width +
                                      1, mid_x - half_width:mid_x +
                                      half_width + 1]

        # Collect the metadata relating to the mosaic and (optionally) PSF
        # and read in/create the PSF
        self.prepare_psf()

        # Crop
        self.logger.info("Cropping subarray from mosaic")
        pixel_scale = self.siaf[self.aperture].XSciScale
        crop = crop_mosaic.Extraction(
            mosaicfile=self.mosaic_file,
            data_extension_number=self.data_extension_number,
            wcs_extension_number=self.wcs_extension_number,
            center_ra=self.crop_center_ra,
            center_dec=self.crop_center_dec,
            dimensions=(xdim, ydim),
            outfile=self.cropped_file,
            jwst_pixel_scale=pixel_scale)
        crop.extract()

        # Convolve the cropped image with the appropriate PSF
        self.logger.info("Convolving with PSF kernel")
        crop.cropped = self.psf_convolution(crop.cropped)

        # Blot
        self.logger.info("Resampling subarray onto JWST pixel grid")
        blot = blot_image.Blot(instrument=self.instrument,
                               aperture=self.aperture,
                               ra=[self.blot_center_ra],
                               dec=[self.blot_center_dec],
                               pav3=[self.blot_pav3],
                               blotfile=crop.cropped,
                               distortion_file=self.distortion_file)
        blot.blot()

        # Blot the PSF associated with the mosaic
        #blot_psf = blot_image.Blot(instrument=self.instrument, aperture=self.aperture,
        #                           ra=[self.blot_center_ra], dec=[self.blot_center_dec],
        #                           pav3=[self.blot_pav3], blotfile=mosaic_psf_model,
        #                           distortion_file=self.distortion_file)
        #blot_psf.blot()

        for dm in blot.blotted_datamodels:
            # Create segmentation map
            # (used only when dispsersing image for WFSS)
            self.seed_image = dm.data
            self.seed_segmap = self.make_segmap(dm)

            # Save
            if self.blotted_file is not None:
                self.blotted_file = os.path.join(self.outdir,
                                                 self.blotted_file)
            self.seed_file, self.seedinfo = save(
                dm.data,
                self.paramfile,
                self.params,
                self.photflam,
                self.photfnu,
                self.pivot,
                self.framesize,
                self.nominal_dims,
                self.coords,
                self.grism_direct_factor,
                segmentation_map=self.seed_segmap,
                filename=self.blotted_file,
                base_unit='e-')
            self.logger.info('Blotted image saved to: {}'.format(
                self.seed_file))

        self.logger.info('\nfits_seed_image complete')
        logging_functions.move_logfile_to_standard_location(
            self.paramfile,
            STANDARD_LOGFILE_NAME,
            yaml_outdir=self.params['Output']['directory'],
            log_type='fits_seed')
Example #5
0
def low_med_high_background_spectrum(param_dict, detector, module):
    """Call jwst_backgrounds in order to produce an estimate of background
    versus wavelength for a particular pointing that corresponds to one of
    "low", "medium", or "high", as with the ETC.

    Parameters
    ----------
    param_dict : dict
        Dictionary of observation information from an input yaml file

    detector : str
        Name of detector, e.g. "NRCA1"

    module : str
        Name of module, e.g. "A"

    Returns
    -------
    background_waves : numpy.ndarray
        1D array with the wavelength values in microns associated with
        ``background_signals``

    background_spec : numpy.ndarray
        1D array containing background values in MJy/str

    """
    # Generate background spectra for all days
    background = jbt.background(param_dict['Telescope']['ra'],
                                param_dict['Telescope']['dec'], 4.)

    # Get basic flux calibration information
    vegazp, photflam, photfnu, pivot_wavelength = fluxcal_info(
        param_dict['Reffiles']['flux_cal'], param_dict['Inst']['instrument'],
        param_dict['Readout']['filter'], param_dict['Readout']['pupil'],
        detector, module)

    # Extract the spectrum value across all days at the pivot wavelength
    wave_diff = np.abs(background.bkg_data['wave_array'] - pivot_wavelength)
    bkgd_wave = np.where(wave_diff == np.min(wave_diff))[0][0]
    bkgd_at_pivot = background.bkg_data['total_bg'][:, bkgd_wave]

    # Now sort and determine the low/medium/high levels
    low, medium, high = find_low_med_high(bkgd_at_pivot)

    # Find the value based on the level in the yaml file
    background_level = param_dict['simSignals']['bkgdrate'].lower()
    if background_level == "low":
        level_value = low
    elif background_level == "medium":
        level_value = medium
    elif background_level == "high":
        level_value = high
    else:
        raise ValueError((
            "ERROR: Unrecognized background value: {}. Must be low, mediumn, or high"
            .format(param_dict['simSignals']['bkgdrate'])))

    # Find the day with the background at the pivot wavelength that
    # is closest to the value associated with the requested level
    diff = np.abs(bkgd_at_pivot - level_value)
    mindiff = np.where(diff == np.min(diff))[0][0]
    background_spec = background.bkg_data['total_bg'][mindiff, :]

    return background.bkg_data['wave_array'], background_spec