Exemplo n.º 1
0
def make_all_spectra(catalog_files,
                     input_spectra=None,
                     input_spectra_file=None,
                     extrapolate_SED=True,
                     output_filename=None,
                     normalizing_mag_column=None,
                     module=None,
                     detector=None,
                     ghost_catalog_files=[]):
    """Overall wrapper function

    Parameters
    ----------
    catalog_files : str or list
        Name(s) of Mirage-formatted source catalog file(s) (ascii)

    input_spectra : dict
        Dictionary containing spectra for some/all targets. Dictionary
        keys are the object indexes (such as from the index column in
        the catalog_file. Entries must be e.g.
        d[1] = {"wavelengths": <wavelength_list>,
                "fluxes": <List of spectral flux densities>}
        Wavelengths and fluxes can be lists, or lists with astropy units
        attached. If no units are supplied, Mirage assumes wavelengths
        in microns and flux densities in Flambda units.

    input_spectra_file : str
        Name of an hdf5 file containing spectra for some/all targets

    extrapolate_SED : bool
        If True and an input SED does not cover the entire wavelength range
        of the grism, linear interpolation is used to extend the SED

    output_filename : str
        Name of the output HDF5 file, which will contain SEDs for all
        targets.

    normalizing_mag_column : str
        If some/all of the input spectra (from input_spectra or
        input_spectra_file) are to be renormalized, they will
        be scaled based on the magnitude values given in this specified
        column from the input catalog_file.

    module : str
        Name of module (e.g. 'A'). Only used when ``normalizing_mag_column``
        is a NIRCam filter

    detector : str
        Name of detector (e.g. 'GUIDER1'). Only used when ``normalizing_mag_column``
        is for FGS

    ghost_catalog_files : list
        Source catalogs containing optical ghosts, based on the astrophysical
        sources within ``catalog_files``

    Returns
    -------
    output_filename : str
        Name of the saved HDF5 file containing all object spectra.
    """
    logger = logging.getLogger(
        'mirage.catalogs.spectra_from_catalog.make_all_spectra')

    # Create the output filename if needed
    if output_filename is None:
        output_filename = create_output_sed_filename(catalog_files[0],
                                                     input_spectra_file)

    # If normalizing_mag_column is not None, get instrument info in order
    # to find the correct gain value
    if normalizing_mag_column is not None:
        instrument = normalizing_mag_column.split('_')[0].lower()
        filter_name = 'none'
        pupil_name = 'none'
        if instrument == 'niriss':
            filter_name = normalizing_mag_column.split('_')[1]
            gain = MEAN_GAIN_VALUES['niriss']

        elif instrument == 'nircam':
            filter_name, pupil_name = mag_col_name_to_filter_pupil(
                normalizing_mag_column)
            filter_val = int(filter_name[1:4])
            if filter_val > 230.:
                channel = 'lw'
            else:
                channel = 'sw'
            selector = '{}{}'.format(channel, module.lower())
            gain = MEAN_GAIN_VALUES['nircam'][selector]

        elif instrument == 'fgs':
            gain = MEAN_GAIN_VALUES['fgs'][detector.lower()]

    # Dictionary to contain all input spectra
    all_input_spectra = {}

    # Read in input spectra from file, add to all_input_spectra dictionary
    if input_spectra_file is not None:
        spectra_from_file = hdf5_catalog.open(input_spectra_file)
        all_input_spectra = {**all_input_spectra, **spectra_from_file}

    # If both an input spectra file and dictionary are given, combine into
    # a single dictionary. Running in this order means that dictionary
    # inputs override inputs from a file.
    if input_spectra is not None:
        all_input_spectra = {**all_input_spectra, **input_spectra}

    # If a single input source catalog is provided, make it a list, in order
    # to be consistent with the case of multiple input catalogs
    if isinstance(catalog_files, str):
        catalog_files = [catalog_files]

    # Loop over input catalogs, which may be of different types
    # (e.g. point source, galaxy, etc)
    for catalog_file in catalog_files:
        # Read in input catalog
        ascii_catalog, mag_sys = read_catalog(catalog_file)

        # Create catalog output name if none is given
        cat_dir, cat_file = os.path.split(catalog_file)
        index = cat_file.rindex('.')
        suffix = cat_file[index:]
        outbase = cat_file[0:index] + '_with_flambda' + suffix
        flambda_output_catalog = os.path.join(cat_dir, outbase)

        catalog, filter_info = add_flam_columns(ascii_catalog, mag_sys)
        catalog.write(flambda_output_catalog, format='ascii', overwrite=True)
        logger.info(
            'Catalog updated with f_lambda columns, saved to: {}'.format(
                flambda_output_catalog))

        # Renormalize
        if len(all_input_spectra) > 0 and normalizing_mag_column is not None:
            rescaling_magnitudes = ascii_catalog['index',
                                                 normalizing_mag_column]

            # Note that nircam_module is ignored for non-NIRCam requests.
            # fgs_detector is ignored for non-FGS requests
            filter_thru_file = get_filter_throughput_file(
                instrument=instrument,
                filter_name=filter_name,
                pupil_name=pupil_name,
                nircam_module=module,
                fgs_detector=detector)
            all_input_spectra = rescale_normalized_spectra(
                all_input_spectra, rescaling_magnitudes, mag_sys,
                filter_thru_file, gain)

        # For sources in catalog_file but not in all_input_spectra, use the
        # magnitudes in the catalog_file, interpolate/extrapolate as necessary
        # and create continuum spectra
        indexes_to_create = []
        for i, line in enumerate(ascii_catalog):
            if line['index'] not in all_input_spectra.keys():
                indexes_to_create.append(i)

        if len(indexes_to_create) > 0:
            continuum = create_spectra(ascii_catalog[indexes_to_create],
                                       filter_info,
                                       extrapolate_SED=extrapolate_SED)
            all_input_spectra = {**all_input_spectra, **continuum}

    # Add entries for any ghost sources by copying the spectra for the
    # sources that caused the ghosts. This needs to be done after we have
    # spectra for all real sources, so we need a separate, second loop over
    # the catalogs here.
    for catalog_file in ghost_catalog_files:
        ascii_catalog, mag_sys = read_catalog(catalog_file)

        if 'corresponding_source_index_for_ghost' in ascii_catalog.colnames:
            print('Within the catalog')
            for source in ascii_catalog:
                if source['corresponding_source_index_for_ghost'] != 0:
                    spec = all_input_spectra[
                        source['corresponding_source_index_for_ghost']]
                    if source['index'] in all_input_spectra.keys():
                        raise ValueError((
                            "Attempting to add spectrum for ghost source {} to SED file, "
                            "but there is already a spectrum for that index present."
                            .format(source['index'])))
                    print('Found a ghost. Adding source.')
                    all_input_spectra[source[index]] = spec
                    logger.info((
                        "Adding spectrum for ghost source {} to SED file. Copy spectrum from source {}. "
                        .format(
                            source[index],
                            source['corresponding_source_index_for_ghost'])))

    # For convenience, reorder the sources by index number
    spectra = OrderedDict({})
    for item in sorted(all_input_spectra.items()):
        spectra[item[0]] = item[1]

    # Save the source spectra in an hdf5 file
    hdf5_catalog.save(spectra,
                      output_filename,
                      wavelength_unit='micron',
                      flux_unit='flam')
    logger.info('Spectra catalog file saved to {}'.format(output_filename))
    return output_filename
Exemplo n.º 2
0
def get_filter_info(column_names, magsys):
    """Given a list of catalog columns names (e.g. 'nircam_f480m_magnitude')
    get the corresponding PHOTFLAM, PHOTFNU, filter zeropoint and pivot
    wavelength values.

    Parameters
    ----------
    column_names : list
        List of Mirage catalog column names

    magsys : str
        Magnitude system (e.g. 'abmag')

    Returns
    -------
    info : dict
        Dictionary of values
        (e.g. info['nircam_f480m_magnitude'] = (<photflam>, <photfnu>,
        <zeropoint>, <pivot>))
    """
    # Get the correct zeropoint file name
    instrument = column_names[0].split('_')[0].lower()
    zp_file = ZEROPOINT_FILES[instrument]

    # Read in the file
    zp_table = ascii.read(zp_file)

    magsys = magsys.upper()
    info = {}

    if instrument in ['nircam', 'niriss']:
        for entry in column_names:
            filter_name, pupil_name = mag_col_name_to_filter_pupil(entry)

            # NIRCam zeropoint files have entries for WLP8 but not WLM8 since
            # they are identical
            if pupil_name.upper() == 'WLM8':
                pupil_name = 'WLP8'

            if instrument == 'nircam':
                match = ((zp_table['Filter'] == filter_name.upper()) &
                         (zp_table['Pupil'] == pupil_name.upper()) &
                         (zp_table['Module'] == 'B'))
            elif instrument == 'niriss':
                match = zp_table['Filter'] == filter_name.upper()

            if not np.any(match):
                raise ValueError("ERROR: no filter matching {} in {}.".format(
                    filter_name, zp_file))

            zp = zp_table[magsys].data[match][0]
            photflam = zp_table['PHOTFLAM'].data[match][0] * FLAMBDA_CGS_UNITS
            photfnu = zp_table['PHOTFNU'].data[match][0] * FNU_CGS_UNITS
            pivot = zp_table['Pivot_wave'].data[match][0] * u.micron
            info[entry] = (photflam, photfnu, zp, pivot)

    # For FGS, just use the values for GUIDER1 detector.
    elif instrument == 'fgs':
        line = zp_table[0]
        zp = line[magsys]
        photflam = line['PHOTFLAM'] * FLAMBDA_CGS_UNITS
        photfnu = line['PHOTFNU'] * FNU_CGS_UNITS
        pivot = line['Pivot_wave'] * u.micron
        info[column_names[0]] = (photflam, photfnu, zp, pivot)

    return info