Example #1
0
    def _map_filter_to_standard(self, filtername):
        """Maps a passed <filtername> to a standard Johnson/Cousins/Bessell
        filter profile. Returns a SpectralElement, defaulting to V"""

        band_mapping = {
            'U': 'johnson_u',
            'B': 'johnson_b',
            'V': 'johnson_v',
            'R': conf.bessell_R_file,
            'Rc': 'cousins_r',
            'I': conf.bessell_I_file,
            'Ic': 'cousins_i',
            'J': 'bessel_j',
            'H': 'bessel_h',
            'K': 'bessel_k',
            'G': conf.gaiadr2_g_file
        }
        band_name = band_mapping.get(self._convert_filtername(filtername),
                                     'johnson_v')
        if band_name.startswith('http://svo'):
            band = SpectralElement.from_file(band_name)
        else:
            band = SpectralElement.from_filter(band_name)

        return band
Example #2
0
def filter_vega_zp(filterfile, filterzero):
    '''
    ######################################################
    # Input                                              #
    # -------------------------------------------------- #
    # filterfile: file containing filter function        #
    # filterzero: flux corresponding to mag=0            #
    # -------------------------------------------------- #
    # Output                                             #
    # -------------------------------------------------- #
    # mag_0: magnitude of Vega in filter system.         #
    ######################################################
    '''
    from synphot import SourceSpectrum, SpectralElement, Observation
    
    #load Vega spectrum
    spec = SourceSpectrum.from_vega()
    #Load ascii filter function
    if filterfile.split('/')[-1][:6] == 'Bessel':
        filt = SpectralElement.from_file(filterfile, wave_unit='nm')
    else:
        filt = SpectralElement.from_file(filterfile, wave_unit='AA')
    wave = filt.waveset
    #Synthetic observation
    obs = Observation(spec, filt)
    flux = obs.effstim(flux_unit='jy', waverange=(wave[0],wave[-1]))
    #Calibrate to zero point (zp)
    mag_0 = -2.512*np.log10(flux/filterzero)
    return mag_0
Example #3
0
    def __init__(self):
        self.verbose = False
        self.telescope_area = 25.326 * 10000.  #JWST primary area in cm^2
        self.wave_bins = np.arange(0.5, 5, 0.1) * u.micron
        self.vega = SourceSpectrum.from_vega()
        self.g2v = STS.grid_to_spec('ck04models', 5860, 0., 4.40)
        self.kband = SpectralElement.from_filter('bessel_k')

        self.bpdir = './'
        self.bpfile = os.path.join(
            self.bpdir, 'F335M_nircam_plus_ote_throughput_moda_sorted.txt')
        self.bp = SpectralElement.from_file(self.bpfile, wave_unit=u.micron)
Example #4
0
def filter_mag(filterfile, filterzero, syn_spec, syn_err=None, wrange=None):
    '''
    ######################################################
    # Input                                              #
    # -------------------------------------------------- #
    # filterfile: file containing filter function        #
    # filterzero: flux [Jy] corresponding to mag=0       #
    #   syn_spec: synphot spectrum object of source      #
    #     wrange: (start,end) observed wavelength range  #
    # -------------------------------------------------- #
    # Output                                             #
    # -------------------------------------------------- #
    # mag: magnitude of source in filter system.         #
    ######################################################
    '''
    from synphot import SpectralElement, Observation, Empirical1D, SourceSpectrum
    import astropy.units as u
    
    #Load ascii filter function
    if filterfile.split('/')[-1][:6] == 'Bessel':
        filt = SpectralElement.from_file(filterfile, wave_unit='nm')
    else:
        filt = SpectralElement.from_file(filterfile, wave_unit='AA')
    if wrange is None:
        fwave = filt.waveset
        swave = syn_spec.waveset
        wrange = (max(fwave[0],swave[0]),min(fwave[-1],swave[-1]))
    waves = np.linspace(wrange[0], wrange[-1], 10000)
    #Synthetic observation
    obs = Observation(syn_spec, filt, force='extrap')
    flux = obs.effstim(flux_unit='jy', waverange=wrange).value
    #flux = obs.effstim(flux_unit='jy', wavelengths=waves).value
    #Calibrate magnitude with zero point
    mag = -2.512*np.log10(flux/filterzero)
    #Synthetic observation of error spectrum
    if syn_err is not None:
        #square filter and error spectrum
        filt2 = SpectralElement(Empirical1D, points=fwave,
                                lookup_table=np.square(filt(fwave)))
        pseudo_flux = np.square(syn_err(swave,flux_unit='jy')).value
        syn_err2 = SourceSpectrum(Empirical1D, points=swave,
                                  lookup_table=pseudo_flux)
        #sum errors in quadrature
        obs = Observation(syn_err2, filt2, force='extrap')
        #flux_err = np.sqrt(obs.effstim(waverange=wrange).value)
        flux_err = np.sqrt(obs.effstim(wavelengths=waves).value)
        #convert to magnitude error
        mag_err = (2.5/np.log(10))*(flux_err/flux)
        return mag, mag_err
    else:
        return mag
Example #5
0
def filter_flux(filterfile, syn_spec, syn_err=None, wrange=None):
    '''
    ######################################################
    # Input                                              #
    # -------------------------------------------------- #
    # filterfile: file containing filter function        #
    #   syn_spec: synphot spectrum object of source      #
    #     wrange: (start,end) observed wavelength range  #
    # -------------------------------------------------- #
    # Output                                             #
    # -------------------------------------------------- #
    # flux: flux of source in filter system.             #
    ######################################################
    '''
    from synphot import SpectralElement, Observation, Empirical1D, SourceSpectrum
    import astropy.units as u
    
    #Load ascii filter function
    if filterfile.split('/')[-1][:6] == 'Bessel':
        filt = SpectralElement.from_file(filterfile, wave_unit='nm')
    else:
        filt = SpectralElement.from_file(filterfile, wave_unit='AA')
    if wrange is None:
        fwave = filt.waveset
        swave = syn_spec.waveset
        wrange = (max(fwave[0],swave[0]),min(fwave[-1],swave[-1]))
        wlengths = fwave[np.logical_and(fwave>wrange[0], fwave<wrange[1])]
    else:
        fwave = filt.waveset
        wlengths = fwave[np.logical_and(fwave>wrange[0]*u.AA, fwave<wrange[1]*u.AA)]
    #Synthetic observation
    obs = Observation(syn_spec, filt, force='extrap')
    flux = obs.effstim(flux_unit='jy', waverange=wrange).value
    #flux = obs.effstim(flux_unit='jy', wavelengths=wlengths).value
    #Synthetic observation of error spectrum
    if syn_err is not None:
        #square filter and error spectrum
        filt2 = SpectralElement(Empirical1D, points=fwave,
                          lookup_table=np.square(filt(fwave)))
        pseudo_flux = np.square(syn_err(swave,flux_unit='jy')).value
        syn_err2 = SourceSpectrum(Empirical1D, points=swave,
                                   lookup_table=pseudo_flux)
        #sum errors in quadrature
        obs = Observation(syn_err2, filt2, force='extrap')
        flux_err = np.sqrt(obs.effstim(waverange=wrange).value)
        return flux, flux_err
    else:
        return flux
Example #6
0
    def rebin_spectra(self, new_waves):
        """
        Rebin a synphot spectra to a new wavelength grid conserving flux.
        Grid does not need to be linear and can be at higher or lower resolution

        Parameters
        ----------
        new_waves: an array of the output wavelenghts in Angstroms but other units can be
            specified

        Returns
        -------
        a new Spextrum instance

        """
        if isinstance(new_waves, u.Quantity):
            new_waves = new_waves.to(u.AA).value

        waves = self.waveset.value  # else assumed to be angstroms
        f = np.ones(len(waves))
        filt = SpectralElement(Empirical1D, points=waves, lookup_table=f)
        obs = Observation(self, filt, binset=new_waves, force='taper')
        newflux = obs.binflux
        rebin_spec = Empirical1D(points=new_waves,
                                 lookup_table=newflux,
                                 meta=self.meta)
        sp = Spextrum(modelclass=rebin_spec)
        sp = self._restore_attr(sp)
        sp.meta.update({"rebinned_spectra": True})

        return sp
Example #7
0
    def photons_in_range(self,
                         wmin=None,
                         wmax=None,
                         area=1 * u.cm**2,
                         filter_curve=None):
        """
        Return the number of photons between wave_min and wave_max or within
        a bandpass (filter)

        Parameters
        ----------
        wmin :
            [Angstrom]
        wmax :
            [Angstrom]
        area : u.Quantity
            [cm2]
        filter_curve : str
                Name of a filter from
                - a generic filter name (see ``DEFAULT_FILTERS``)
                - a spanish-vo filter service reference (e.g. ``"Paranal/HAWKI.Ks"``)
                - a filter in the spextra database
                - the path to the file containing the filter (see ``Passband``)
                - a ``Passband`` or ``synphot.SpectralElement`` object

        Returns
        -------
        counts : u.Quantity array

        """
        if isinstance(area, u.Quantity):
            area = area.to(u.cm**2).value  #
        if isinstance(wmin, u.Quantity):
            wmin = wmin.to(u.Angstrom).value
        if isinstance(wmax, u.Quantity):
            wmin = wmax.to(u.Angstrom).value

        if filter_curve is None:
            # this makes a bandpass out of wmin and wmax
            try:
                mid_point = 0.5 * (wmin + wmax)
                width = abs(wmax - wmin)
                filter_curve = SpectralElement(Box1D,
                                               amplitude=1,
                                               x_0=mid_point,
                                               width=width)
            except ValueError("Please specify wmin/wmax or a filter"):
                raise

        elif os.path.exists(filter_curve):
            filter_curve = Passband.from_file(filename=filter_curve)
        elif isinstance(filter_curve, (Passband, SpectralElement)):
            filter_curve = filter_curve
        else:
            filter_curve = Passband(filter_curve)

        obs = Observation(self, filter_curve)
        counts = obs.countrate(area=area * u.cm**2)

        return counts
Example #8
0
 def collapse(self, waveset):
     throughput = self.radiometry_table.throughput(waveset)
     self._throughput = SpectralElement(Empirical1D, points=waveset,
                                        lookup_table=throughput)
     emission = self.radiometry_table.emission(waveset)
     self._emission = SourceSpectrum(Empirical1D, points=waveset,
                                     lookup_table=emission)
Example #9
0
def get_phot(spec, bands):
    """
    Compute the fluxes in the requested bands.

    Parameters
    ----------
    spec : SpecData object
        the spectrum

    bands: list of strings
        bands requested

    Outputs
    -------
    band fluxes : numpy array
        calculated band fluxes
    """

    # create a SourceSpectrum object from the spectrum, excluding the bad regions
    spectrum = SourceSpectrum(
        Empirical1D,
        points=spec.waves.to(u.Angstrom)[spec.npts != 0],
        lookup_table=spec.fluxes[spec.npts != 0],
    )

    # path for band response curves
    band_path = pkg_resources.resource_filename("measure_extinction",
                                                "data/Band_RespCurves/")

    # dictionary linking the bands to their response curves
    bandnames = {
        "J": "2MASSJ",
        "H": "2MASSH",
        "K": "2MASSKs",
        "IRAC1": "IRAC1",
        "IRAC2": "IRAC2",
        "WISE1": "WISE1",
        "WISE2": "WISE2",
        "L": "AAOL",
        "M": "AAOM",
    }

    # define the units of the output fluxes
    funit = u.erg / (u.s * u.cm * u.cm * u.Angstrom)

    # compute the flux in each band
    fluxes = np.zeros(len(bands))
    for k, band in enumerate(bands):
        # create the bandpass (as a SpectralElement object)
        bp = SpectralElement.from_file(
            "%s%s.dat" %
            (band_path,
             bandnames[band]))  # assumes wavelengths are in Angstrom!!
        # integrate the spectrum over the bandpass, only if the bandpass fully overlaps with the spectrum (this actually excludes WISE2)
        if bp.check_overlap(spectrum) == "full":
            obs = Observation(spectrum, bp)
            fluxes[k] = obs.effstim(funit).value
        else:
            fluxes[k] = np.nan
    return fluxes
def get_phot(waves, exts, bands):
    """
    Compute the extinction in the requested bands

    Parameters
    ----------
    waves : numpy.ndarray
        The wavelengths

    exts : numpy.ndarray
        The extinction values at wavelengths "waves"

    bands: list of strings
        Bands requested

    Outputs
    -------
    band extinctions : numpy array
        Calculated band extinctions
    """
    # create a SourceSpectrum object from the extinction curve
    spectrum = SourceSpectrum(
        Empirical1D,
        points=waves * 1e4,
        lookup_table=exts,
    )

    # path for band response curves
    band_path = (
        "/Users/mdecleir/measure_extinction/measure_extinction/data/Band_RespCurves/"
    )

    # dictionary linking the bands to their response curves
    bandnames = {
        "J": "2MASSJ",
        "H": "2MASSH",
        "K": "2MASSKs",
        "IRAC1": "IRAC1",
        "IRAC2": "IRAC2",
        "WISE1": "WISE1",
        "WISE2": "WISE2",
        "L": "AAOL",
        "M": "AAOM",
    }

    # compute the extinction value in each band
    band_ext = np.zeros(len(bands))
    for k, band in enumerate(bands):
        # create the bandpass (as a SpectralElement object)
        bp = SpectralElement.from_file(
            "%s%s.dat" %
            (band_path,
             bandnames[band]))  # assumes wavelengths are in Angstrom!!
        # integrate the extinction curve over the bandpass, only if the bandpass fully overlaps with the extinction curve (this actually excludes WISE2)
        if bp.check_overlap(spectrum) == "full":
            obs = Observation(spectrum, bp)
            band_ext[k] = obs.effstim().value
        else:
            band_ext[k] = np.nan
    return band_ext
Example #11
0
    def _get_ter_property(self, ter_property):
        """
        Looks for arrays for transmission, emissivity, or reflection

        Parameters
        ----------
        ter_property : str
            ``transmission``, ``emissivity``, ``reflection``

        Returns
        -------
            response_curve : ``synphot.SpectralElement``

        """

        compliment_names = ["transmission", "emissivity", "reflection"]
        ii = np.where([ter_property == name
                       for name in compliment_names])[0][0]
        compliment_names.pop(ii)

        wave = self._get_array("wavelength")
        value_arr = self._get_array(ter_property)
        if value_arr is None:
            value_arr = self._compliment_array(*compliment_names)

        if value_arr is not None and wave is not None:
            response_curve = SpectralElement(Empirical1D,
                                             points=wave,
                                             lookup_table=value_arr)
        else:
            response_curve = None
            warnings.warn("Both wavelength and {} must be set"
                          "".format(ter_property))

        return response_curve
def get_phot(mwave, mflux, band_names, band_resp_filenames):
    """
    Compute the magnitudes in the requested bands.

    Parameters
    ----------
    mwave : vector
        wavelengths of model flux

    mflux : vector
        model fluxes

    band_names: list of strings
        names of bands requested

    band_resp_filename : list of strings
        filenames of band response functions for the requested bands

    Outputs
    -------
    bandinfo : BandData object
    """
    # get a BandData object
    bdata = BandData("BAND")

    # path for non-HST band response curves
    data_path = pkg_resources.resource_filename("measure_extinction",
                                                "data/Band_RespCurves/")

    # compute the fluxes in each band
    for k, cband in enumerate(band_names):
        if "HST" in cband:
            bp_info = cband.split("_")
            bp = STS.band("%s,%s,%s" % (bp_info[1], bp_info[2], bp_info[3]))
            ncband = "%s_%s" % (bp_info[1], bp_info[3])
        else:
            bp = SpectralElement.from_file("%s%s" %
                                           (data_path, band_resp_filenames[k]))
            ncband = cband

        # check if the wavelength units are in microns instead of Angstroms
        #   may not work
        if max(bp.waveset) < 500 * u.Angstrom:
            print("filter wavelengths not in angstroms")
            exit()
            # a.waveset *= 1e4
        iresp = bp(mwave)
        bflux = np.sum(iresp * mflux) / np.sum(iresp)
        bflux_unc = 0.0
        bdata.band_fluxes[ncband] = (bflux, bflux_unc)

    # calculate the band magnitudes from the fluxes
    bdata.get_band_mags_from_fluxes()

    # get the band fluxes from the magnitudes
    #   partially redundant, but populates variables useful later
    bdata.get_band_fluxes()

    return bdata
Example #13
0
def download_svo_filter(filter_name, return_style="synphot"):
    """
    Query the SVO service for the true transmittance for a given filter

    Copied 1 to 1 from tynt by Brett Morris

    Parameters
    ----------
    filt : str
        Name of the filter as available on the spanish VO filter service
        e.g: ``Paranal/HAWKI.Ks``

    return_style : str, optional
        Defines the format the data is returned
        - synphot: synphot.SpectralElement
        - table: astropy.table.Table
        - quantity: astropy.unit.Quantity [wave, trans]
        - array: np.ndarray [wave, trans], where wave is in Angstrom
        - vo_table : astropy.table.Table - original output from SVO service

    Returns
    -------
    filt_curve : See return_style
        Astronomical filter object.

    """
    path = download_file('http://svo2.cab.inta-csic.es/'
                         'theory/fps3/fps.php?ID={}'.format(filter_name),
                         cache=True)

    tbl = Table.read(path, format='votable')
    wave = u.Quantity(tbl['Wavelength'].data.data, u.Angstrom, copy=False)
    trans = tbl['Transmission'].data.data
    if return_style == "synphot":
        filt = SpectralElement(Empirical1D, points=wave, lookup_table=trans)
    elif return_style == "table":
        filt = Table(data=[wave, trans], names=["wavelength", "transmission"])
        filt.meta["wavelength_unit"] = "Angstrom"
    elif return_style == "quantity":
        filt = [wave, trans]
    elif return_style == "array":
        filt = [wave.value, trans]
    elif return_style == "vo_table":
        filt = tbl

    return filt
 def test_blackbody_maximum_agrees_with_wien(self, temp):
     '''Check the maximum of emission against Wien's law for photon rate'''
     emissivity = SpectralElement(Empirical1D, points=[1, 20],
                                  lookup_table=[1., 1.])
     flux = surf_utils.make_emission_from_emissivity(temp, emissivity)
     dlam = 0.1
     wave = np.arange(3, 20, dlam) * u.um
     wavemax = wave[np.argmax(flux(wave))]
     if isinstance(temp, u.Quantity):
         temp = temp.to(u.Kelvin, equivalencies=u.temperature()).value
     wienmax = 3669.7 * u.um / temp
     assert np.abs(wavemax - wienmax.to(u.um)) < dlam * u.um
Example #15
0
 def test_returns_correct_half_flux_with_bandpass(self):
     flux = np.ones(11) * u.Unit("ph s-1 m-2 um-1")
     wave = np.linspace(0.5, 2.5, 11) * u.um
     spec = SourceSpectrum(Empirical1D, points=wave, lookup_table=flux)
     bandpass = SpectralElement(Empirical1D,
                                points=np.linspace(1, 2, 13) * u.um,
                                lookup_table=0.5 * np.ones(13))
     counts = source_utils.photons_in_range([spec],
                                            1 * u.um,
                                            2 * u.um,
                                            bandpass=bandpass)
     assert counts.value == approx(0.5)
Example #16
0
    def __init__(self, filter_name=None, modelclass=None, **kwargs):

        if filter_name is not None:
            if filter_name in DEFAULT_FILTERS:
                filter_name = DEFAULT_FILTERS[filter_name]

            try:
                FilterContainer.__init__(self, filter_name)
                meta, wave, trans = self._loader()
                SpectralElement.__init__(self,
                                         Empirical1D,
                                         points=wave,
                                         lookup_table=trans,
                                         meta=meta)
            except ValueError as e1:
                try:  # try to download it from SVO
                    meta, wave, trans = self._from_svo(filter_name)
                    SpectralElement.__init__(self,
                                             Empirical1D,
                                             points=wave,
                                             lookup_table=trans,
                                             meta=meta)
                except ValueError as e2:
                    print("filter %s doesn't exist" % filter_name)

        elif modelclass is not None:
            SpectralElement.__init__(self, modelclass, **kwargs)
        else:
            raise ValueError("please define a filter")
Example #17
0
    def rebin_transmission(self, wavenew):
        """Rebins a potentially higher resolution atmospheric transmission
        spectrum to a new wavelength grid <wavenew>.
        Returns a new BaseUnitlessSpectrum constructed from the new passed
        wavelength grid and the resampled transmission"""

        wave = self.transmission.waveset
        specin = self.transmission(wave)
        # Have to pretend transmission is in a flux unit to make a SourceSpectrum
        # so drop here and put back at the end
        orig_unit = specin.unit
        spec = SourceSpectrum(Empirical1D,
                              points=wave,
                              lookup_table=specin.value)
        f = np.ones(len(wave))
        filt = SpectralElement(Empirical1D, points=wave, lookup_table=f)
        obs = Observation(spec, filt, binset=wavenew, force='taper')

        new_trans = SpectralElement(Empirical1D,
                                    points=wavenew,
                                    lookup_table=obs.binflux.value * orig_unit)

        return new_trans
Example #18
0
    def photons_from_source(self,
                            mag,
                            mag_filter,
                            filtername,
                            source_spec=None,
                            force_overlap='taper',
                            normalize=True):
        """Computes the photons coming from the source given by [source_spec] (Vega
        is assumed if not given) of the given <mag> in <mag_filter> (e.g. for "V=20",
        mag=20, mag_filter='V') when observed through the instrument's filter
        given by <filtername>
        """
        print("photons_from_source:", filtername)
        standard_filter = self._convert_filtername(filtername)
        band = self._map_filter_to_standard(mag_filter)
        print(band.meta.get('expr', 'Unknown'), type(source_spec))
        source_spec = source_spec or self._vega
        if type(source_spec) != SourceSpectrum:
            raise ETCError('Invalid sourcespec; must be a SourceSpectrum')

        # XXX Todo: allow support for AB mags here
        # test line below for comparison with SIGNAL. Difference in mag when
        # using proper normalization is ~mag-0.0155 (for B=22.7)
#        source_spec_norm = source_spec*10**(-0.4*mag)
        if normalize is True:
            source_spec_norm = source_spec.normalize(mag * units.VEGAMAG,
                                                     band,
                                                     vegaspec=self._vega)
        else:
            source_spec_norm = source_spec

        self._create_combined()
        filter_waves, filter_trans = self.instrument.filterset[
            filtername]._get_arrays(None)
        throughput = self._throughput_for_filter(filtername)
        waves, thru = throughput._get_arrays(filter_waves)
        spec_elements = SpectralElement(Empirical1D,
                                        points=filter_waves,
                                        lookup_table=filter_trans * thru)

        # get the synphot observation object
        synphot_obs = Observation(source_spec_norm,
                                  spec_elements,
                                  force=force_overlap)
        if self.obs is None:
            self.obs = synphot_obs
        countrate = synphot_obs.countrate(area=self.telescope.area)

        return countrate
Example #19
0
 def test_with_bandpass_and_area_returns_correct_value(
         self, flux, area, expected):
     flux = flux * u.Unit("ph s-1 m-2 um-1")
     spec = SourceSpectrum(Empirical1D,
                           points=np.linspace(0.5, 2.5, 11) * u.um,
                           lookup_table=flux)
     bandpass = SpectralElement(Empirical1D,
                                points=np.linspace(1, 2, 13) * u.um,
                                lookup_table=0.5 * np.ones(13))
     counts = source_utils.photons_in_range([spec],
                                            1 * u.um,
                                            2 * u.um,
                                            bandpass=bandpass,
                                            area=area)
     assert counts.value == approx(expected)
Example #20
0
    def system_transmission(self):

        wave_unit = u.Unit(rc.__currsys__["!SIM.spectral.wave_unit"])
        dwave = rc.__currsys__["!SIM.spectral.spectral_bin_width"]
        wave_min = rc.__currsys__["!SIM.spectral.wave_min"]
        wave_max = rc.__currsys__["!SIM.spectral.wave_max"]
        wave = np.arange(wave_min, wave_max, dwave)
        trans = np.ones_like(wave)
        sys_trans = SpectralElement(Empirical1D,
                                    points=wave * u.Unit(wave_unit),
                                    lookup_table=trans)

        for effect in self.get_z_order_effects(100):
            sys_trans *= effect.throughput

        return sys_trans
Example #21
0
    def gaussian(cls, center, fwhm, peak, **kwargs):
        """
        Creates a filter with a gaussian shape with given user parameters
        Returns
        -------

        """
        if isinstance(center, u.Quantity) is False:
            center = center*u.AA
        if isinstance(fwhm, u.Quantity) is False:
            fwhm = fwhm*u.AA

        sigma = fwhm / (2.0 * np.sqrt(2.0 * np.log(2.0)))

        modelclass = SpectralElement(Gaussian1D, amplitude=peak, mean=center, stddev=sigma, **kwargs)

        return cls(modelclass=modelclass)
Example #22
0
    def square(cls, wmin, wmax, transmission):
        """
        Creates a filter with a rectangular shape
        Returns
        -------

        """
        if isinstance(wmin, u.Quantity) is False:
            wmin = wmin*u.AA
        if isinstance(wmax, u.Quantity) is False:
            wmax = wmax*u.AA

        center = (wmax + wmin) * 0.5
        width = wmax - wmin
        modelclass = SpectralElement(Box1D, amplitude=transmission, x_0=center, width=width)

        return cls(modelclass=modelclass)
def plot_bandpasses(bands):
    # create the figure
    plt.figure(figsize=(15, 5))

    # plot all response curves
    for band in bands:
        bp = SpectralElement.from_file("%s%s.dat" % (band_path, band))
        if "MIPS" in band:
            wavelengths = bp.waveset * 10**4
        else:
            wavelengths = bp.waveset
        plt.plot(wavelengths, bp(bp.waveset), label=band)

    plt.xlabel(r"$\lambda$ [$\AA$]", size=15)
    plt.ylabel("transmission", size=15)
    plt.legend(ncol=2, loc=1)
    plt.show()
Example #24
0
    def set_bandpass_from_filter(self, filtername):
        """Loads the specified <filtername> from the transmission profile file
        which is mapped via the etc.config.Conf() items.
        Returns a SpectralElement instance for the filter profile
        """

        if len(filtername) == 2 and filtername[1] == 'p':
            filtername = filtername[0]

        filename = conf.mapping.get(filtername, None)
        if filename is None:
            raise ETCError('Filter name {0} is invalid.'.format(filtername))
        if 'LCO_' in filename().upper() and '.csv' in filename().lower():
            file_path = pkg_resources.files('etc.data').joinpath(
                os.path.expandvars(filename()))
            source = "LCO iLab format"
            header, wavelengths, throughput = self._read_lco_filter_csv(
                file_path)
        elif 'http://svo' in filename().lower():
            source = "SVO filter service"
            header, wavelengths, throughput = specio.read_remote_spec(
                filename(), wave_unit=u.AA, flux_unit=units.THROUGHPUT)
        else:
            source = "local file"
            file_path = pkg_resources.files('etc.data').joinpath(
                os.path.expandvars(filename()))
            warnings.simplefilter('ignore', category=AstropyUserWarning)
            header, wavelengths, throughput = specio.read_ascii_spec(
                file_path, wave_unit=u.nm, flux_unit=units.THROUGHPUT)
            if throughput.mean() > 1.0:
                throughput /= 100.0
                header['notes'] = 'Divided by 100.0 to convert from percentage'
        print("Reading from {} for {}".format(source, filtername))

        header['filename'] = filename
        header['descrip'] = filename.description
        meta = {'header': header, 'expr': filtername}

        return SpectralElement(Empirical1D,
                               points=wavelengths,
                               lookup_table=throughput,
                               meta=meta)
Example #25
0
    def from_vectors(cls, waves, trans, meta=None, wave_unit=u.AA):
        """
        Create a ``Passband`` directly from from vectos (lists, numpy.arrays, etc)
        Parameters
        ----------
        waves: list-like
        trans: list-like
        meta: dictionary containing the metadata
        wave_unit: u.Quantity, defaulted to angstroms

        Returns
        -------
        Passband
        """
        if isinstance(waves, u.Quantity) is False:
            waves = waves*wave_unit

        modelclass = SpectralElement(Empirical1D, points=waves, lookup_table=trans, meta=meta)

        return cls(modelclass=modelclass)
Example #26
0
def get_filter(filter_name):
    # first check locally
    # check generics
    # check spanish_vo
    path = find_file(filter_name, silent=True)

    if path is not None:
        tbl = ioascii.read(path)
        wave = quantity_from_table("wavelength", tbl, u.um).to(u.um)
        filt = SpectralElement(Empirical1D, points=wave,
                               lookup_table=tbl["transmission"])
    elif filter_name in FILTER_DEFAULTS:
        filt = download_svo_filter(FILTER_DEFAULTS[filter_name])
    else:
        try:
            filt = download_svo_filter(filter_name)
        except:
            filt = None

    return filt
Example #27
0
    def synphot_calcs(self, bpfile):
        """
        Calculate zeropoint for a given bandpass in several 
        photometric systems

        Arguments:
        ----------
        bpfile -- Text file containing the throuput table for 
                  a single bandpass

        Returns:
        --------
        Bandpass zeropoint value in Vega mags, AB mags, ST mags,
        photflam, photfnu, and megajansky. Also returns pivot wavelength
        """
        # Define wavelength list to use
        #wave_bins = np.arange(0.5, 5, 0.1) * u.micron

        # Use refactored synphot to calculate zeropoints
        orig_bp = SpectralElement.from_file(bpfile)

        # Now reduce the PCE curve by a factor equal to
        # the gain, in order to get the output to translate
        # from ADU/sec rather than e/sec
        bp = orig_bp / self.gain
        #bp.model.lookup_table = bp.model.lookup_table / self.gain

        photflam = bp.unit_response(self.telescope_area)
        photplam = bp.pivot()
        st_zpt = -2.5 * np.log10(photflam.value) - 21.1
        ab_zpt = (-2.5 * np.log10(photflam.value) - 21.1 -
                  5 * np.log10(photplam.value) + 18.6921)
        #mjy_zpt = units.convert_flux(photplam,photflam, 'MJy')
        mjy_zpt = photflam.to(u.MJy, u.spectral_density(photplam))
        obs = Observation(self.vega, bp, binset=wave_bins)
        vega_zpt = -obs.effstim(flux_unit='obmag', area=self.telescope_area)
        photfnu = units.convert_flux(photplam, photflam, units.FNU)
        return (vega_zpt.value, ab_zpt, st_zpt, photflam.value, photfnu.value,
                mjy_zpt.value, photplam.value)
Example #28
0
    def __init__(self, name=None, camera_type="CCD", **kwargs):
        _cam_types = [
            "CCD",
        ]
        self.camera_type = camera_type.upper() if camera_type.upper(
        ) in _cam_types else "CCD"
        self.name = name if name is not None else self.camera_type + ' camera'

        # Defaults assume no elements per channel on the assumption that it's
        # in the common optics (populated in Instrument())
        self.num_ar_coatings = kwargs.get('num_chan_ar_coatings', 0)
        self.num_lenses = kwargs.get('num_chan_lenses', 0)
        self.num_mirrors = kwargs.get('num_chan_mirrors', 0)

        # Fused silica (common lens material) and fused quartz (common for CCD windows)
        # turn out to have the same transmission...
        self.lens_trans = kwargs.get('chan_lens_trans', 0.93)

        self.mirror_refl = kwargs.get('chan_mirror_refl', 0.9925)
        # Transmission/Reflection values of optical elements coating
        self.ar_coating = kwargs.get('chan_ar_coating_refl', 0.99)

        # Read common components first
        trans_components = kwargs.get('trans_components', None)
        wavelengths = np.arange(300, 1501, 1) * u.nm
        if trans_components:
            print("Computing channel transmission from components")
            trans = len(wavelengths) * [
                1.0,
            ]
            for comp_name in trans_components.split(","):
                print(comp_name)
                element = read_element(comp_name)
                trans *= element(wavelengths)
        else:
            print("Computing channel transmission from elements")
            transmission = self._compute_transmission()
            trans = len(wavelengths) * [
                transmission,
            ]
        header = {}
        self.transmission = SpectralElement(Empirical1D,
                                            points=wavelengths,
                                            lookup_table=trans,
                                            keep_neg=True,
                                            meta={'header': header})

        ccd_qe = kwargs.get('ccd_qe', 0.9)
        if not isinstance(ccd_qe, (u.Quantity, numbers.Number)):
            file_path = os.path.expandvars(ccd_qe)
            if not os.path.exists(file_path):
                file_path = str(
                    pkg_resources.files('etc.data').joinpath(ccd_qe))
            header, wavelengths, throughput = specio.read_ascii_spec(
                file_path, wave_unit=u.nm, flux_unit=units.THROUGHPUT)
            if throughput.mean() > 1.0:
                throughput /= 100.0
                header['notes'] = 'Divided by 100.0 to convert from percentage'
            header['filename'] = ccd_qe
            self.ccd_qe = BaseUnitlessSpectrum(Empirical1D,
                                               points=wavelengths,
                                               lookup_table=throughput,
                                               keep_neg=False,
                                               meta={'header': header})
        else:
            self.ccd_qe = ccd_qe
        self.ccd_gain = kwargs.get('ccd_gain', 1) * (u.electron / u.adu)
        self.ccd_readnoise = kwargs.get('ccd_readnoise',
                                        0) * (u.electron / u.pix)
        ccd_pixsize = kwargs.get('ccd_pixsize', 0)
        try:
            ccd_pixsize_units = u.Unit(
                kwargs.get('ccd_pixsize_units', 'micron'))
        except ValueError:
            ccd_pixsize_units = u.micron
        self.ccd_pixsize = (ccd_pixsize * ccd_pixsize_units).to(u.micron)
        self.ccd_xpixels = kwargs.get('ccd_xpixels', 0)
        self.ccd_ypixels = kwargs.get('ccd_ypixels', 0)
        self.ccd_xbinning = kwargs.get('ccd_xbinning', 1)
        self.ccd_ybinning = kwargs.get('ccd_ybinning', 1)
Example #29
0
    def __init__(self, name=None, inst_type="IMAGER", **kwargs):

        _ins_types = ["IMAGER", "SPECTROGRAPH"]
        self.name = name if name is not None else "Undefined"
        self.inst_type = inst_type.upper() if inst_type.upper(
        ) in _ins_types else "IMAGER"
        self.fiber_diameter = kwargs.get('fiber_diameter', None)
        if self.fiber_diameter is not None:
            self.fiber_diameter = self.fiber_diameter * u.arcsec

        self.grating_linespermm = kwargs.get('grating_linespermm', None)
        if self.grating_linespermm:
            self.grating_spacing = 1.0 / (self.grating_linespermm * (1 / u.mm))
        self.grating_blaze = kwargs.get('grating_blaze', 0.0)
        self.grating_gamma = kwargs.get('grating_gamma', 0.0)
        self.cam_focallength = kwargs.get('cam_focallength', None)
        if self.cam_focallength:
            self.cam_focallength = self.cam_focallength * u.mm
        self.dispersion_along_x = kwargs.get('dispersion_along_x', True)
        self._echelle_constant = None

        # Defaults assume a "standard" imager with a single lens, 2 AR coatings
        # on the front and back surfaces and no mirrors
        self.num_ar_coatings = kwargs.get('num_ar_coatings', 2)
        self.num_lenses = kwargs.get('num_inst_lenses', 1)
        self.num_mirrors = kwargs.get('num_inst_mirrors', 0)

        # Fused silica (common lens material) and fused quartz (common for CCD windows)
        # turn out to have the same transmission...
        self.lens_trans = kwargs.get('inst_lens_trans', 0.93)

        self.mirror_refl = kwargs.get('inst_mirror_refl', 0.9925)
        # Transmission/Reflection values of optical elements coating
        self.ar_coating = kwargs.get('inst_ar_coating_refl', 0.99)

        # Read common components first
        trans_components = kwargs.get('trans_components', None)
        wavelengths = np.arange(300, 1501, 1) * u.nm
        if trans_components:
            trans = len(wavelengths) * [
                1.0,
            ]
            for comp_name in trans_components.split(","):
                print(comp_name)
                element = read_element(comp_name.strip())
                trans *= element(wavelengths)
        else:
            print("Computing transmission from elements")
            transmission = self._compute_transmission()
            trans = len(wavelengths) * [
                transmission,
            ]
        header = {}
        self.transmission = SpectralElement(Empirical1D, points=wavelengths, lookup_table=trans,\
            keep_neg=True, meta={'header': header})

        fwhm = kwargs.get('fwhm', 1)
        try:
            fwhm_units = u.Unit(kwargs.get('fwhm_units', 'arcsec'))
        except ValueError:
            fwhm_units = u.arcsec
        try:
            # Already a Quantity
            self.fwhm = fwhm.to(u.arcsec)
        except AttributeError:
            self.fwhm = fwhm * fwhm_units

        focal_scale = kwargs.get('focal_scale', 0)
        try:
            focal_scale_units = u.Unit(
                kwargs.get('focal_scale_units', 'arcsec/mm'))
        except ValueError:
            focal_scale_units = u.arcsec / u.mm
        self.focal_scale = focal_scale * focal_scale_units

        channels = kwargs.get('channels', {'default': {}})
        self._num_channels = max(len(channels), 1)
        self.channelset = OrderedDict()
        self.filter2channel_map = OrderedDict()

        self.filterlist = kwargs.get('filterlist', [])
        self.filterset = OrderedDict()
        for filtername in self.filterlist:
            if filtername not in self.filterset:
                self.filterset[filtername] = self.set_bandpass_from_filter(
                    filtername)
                self.filter2channel_map[filtername] = 'default'

        for channel in channels:
            # Make copy of common params defined at the Instrument level and then
            # overwrite with channel/camera-specific versions
            camera_kwargs = kwargs.copy()
            # Delete any trans_components
            if 'trans_components' in camera_kwargs:
                del (camera_kwargs['trans_components'])
            for k, v in channels[channel].items():
                camera_kwargs[k] = v
            self.channelset[channel] = Camera(**camera_kwargs)
            if self.channelset[
                    channel].ccd_pixsize != 0 and self.focal_scale != 0:
                self.channelset[channel].ccd_pixscale = self.focal_scale.to(
                    u.arcsec / u.mm) * self.channelset[channel].ccd_pixsize.to(
                        u.mm)
            cam_filterlist = camera_kwargs.get('filterlist', [])
            for filtername in cam_filterlist:
                if filtername not in self.filterset:
                    self.filterlist.append(filtername)
                    self.filterset[filtername] = self.set_bandpass_from_filter(
                        filtername)
                self.filter2channel_map[filtername] = channel
Example #30
0
    def grism_cal(self, throughput_files):
        """
        Calculate flux cal outputs for grism mode. Input files should 
        contain the total system throughput for the grism plus crossing 
        filter. The input file list should contain throughputs for all 
        orders of just a single grism+crossing filter.

        Arguments:
        ----------
        throughput_files -- list of text files containing filter throughputs

        Returns:
        --------
        Astropy table containing flux calibration information for grism mode
        """
        print('need to update this for synphot')

        allrows = []
        filters = np.array([])
        pupils = np.array([])
        orders = np.array([])
        fluxes = np.array([])
        uncs = np.array([])
        nelems = np.array([])
        waves = np.array([])
        resps = np.array([])
        #waves = np.expand_dims(waves,axis=0)
        #resps = np.expand_dims(resps,axis=0)

        for file in throughput_files:
            # Read in necessary file info
            with fits.open(file) as h:
                cname = h[0].header['COMPNAME']

            junk, filter, mod = cname.split('_')
            mod = mod[-1].upper()
            pupil = 'GRISMR'
            print('Eventually need to be smarter about pupil value!!!!!')

            print("opening {}".format(file))
            bp = SpectralElement.from_file(file)

            # Now reduce the PCE curve by a factor equal to
            # the gain, in order to get the output to translate
            # from ADU/sec rather than from e/sec
            bp.model.lookup_table = bp.model.lookup_table / self.gain

            # Pivot wavelength in microns
            pwave = bp.pivot()
            #pwave = pwave.to(u.micron)

            # Need to find the order here, so that the proper
            # dispersion value is used.
            ord = file.find('order')
            order = file[ord + 5]
            dispersion = self.disp[order]

            #find pivot? mean? center? effective? wavelength
            #denom = self.h * self.c / eff_lambda
            #countratedensity = self.telescope_area * tp['Throughput'] * vegaflux / denom

            #countratedensityflux,countratedensitywave,pwave = self.getcountsflux(bp)
            #totalrate = self.integrate(countratedensity)

            #Is the variable below used at all?
            #grism_total_rate += totalrate

            obs = self.getcountsflux(bp)

            # From observation, get the flux in counts
            countratedensityflux = obs(obs.binset,
                                       flux_unit='count',
                                       area=self.telescope_area)

            print('countratedensityflux', countratedensityflux)

            # Multiply by dispersion
            countratedensityflux *= dispersion

            # Countrate density at the pivot wavelength
            print('pivot', pwave.value)
            print('countratedensityflux*dispersion', countratedensityflux)
            print('obs.binset', obs.binset)
            #cnorm = np.interp(pwave.value,obs.binset,countratedensityflux)
            cnorm = obs(pwave, flux_unit='count',
                        area=self.telescope_area) * dispersion
            print('cnorm', cnorm)

            # Vega flux value at pivot wavelength, convert to Jansky
            vega_pivot = self.vega(pwave)
            j0 = units.convert_flux(pwave, vega_pivot, 'MJy')

            # Now we need to divide by the area of a pixel in
            # sterradians, so we can eventually get MJy/str per ADU/sec
            j0 /= self.pixel_area_sr

            #vega_pivotorig = np.interp(pwave.value,self.vega.waveset,self.vega(self.vega.waveset))
            #print("units of vega flux are: {}".format(self.vega(self.vega.waveset).unit))
            #j0 = self.toJansky(vega_pivot.value,pwave.value) / 1.e6

            # Ratio of Vega flux to the countrate density at pivot wavelength
            ratio = j0 / cnorm

            print('')
            print('PHOTMJSR', ratio)
            print(
                'NIRISS values are 0.01 to 2.0. I would expect ours to be similar!'
            )
            print('')

            # Define a set of wavelengths to evaluate relative fluxcal
            goodpts = bp(bp.waveset) > 0.0001
            minwave = np.min(bp.waveset[goodpts])
            maxwave = np.max(bp.waveset[goodpts])
            w = minwave.value
            allwaves = np.array([w])
            while (w < maxwave.value):
                delt = w / (np.absolute(np.int(order)) * self.resolving_power)
                allwaves = np.append(allwaves, w + delt)
                w += delt

            # Calculate Vega flux at each wavelength
            nelem = len(allwaves)
            allfluxes = self.vega(allwaves)
            alljansky = units.convert_flux(allwaves, allfluxes, 'MJy')
            allcounts = np.interp(allwaves, obs.binset, countratedensityflux)
            #allfluxes = np.interp(allwaves,self.vega.waveset,self.vega(self.vega.waveset))
            #alljansky = self.toJansky(allfluxes,allwaves) / 1.e6
            # Normalize the Vega counts at all wavelengths by the value at the pivot
            # wavelength
            allratio = alljansky.value / allcounts / ratio / self.pixel_area_sr

            #print(np.min(allwaves),np.max(allwaves),allwaves[0],allwaves[-1])
            #f,a = plt.subplots()
            #a.plot(allwaves,allratio,color='red')
            #a.set_xlabel('Wavelength ({})'.format(bp.waveset.unit))
            #a.set_ylabel('Normalized Ratio (MJy/str per count/sec)')
            #a.set_title("{}, Order {}".format(filter.upper(),order))
            #f.savefig(os.path.split(file)[-1]+'_allratios.pdf')

            #f,a = plt.subplots()
            #a.plot(allwaves,alljansky,color='red')
            #a.set_xlabel('Wavelength ({})'.format(bp.waveset.unit))
            #a.set_ylabel('Vega Flux (MJy)')
            #a.set_title("{}, Order {}".format(filter.upper(),order))
            #f.savefig(os.path.split(file)[-1]+'_alljansky.pdf')

            #f,a = plt.subplots()
            #a.plot(allwaves,allcounts,color='red')
            #a.set_xlabel('Wavelength ({})'.format(bp.waveset.unit))
            #a.set_ylabel('Counts (count/sec)')
            #a.set_title("{}, Order {}".format(filter.upper(),order))
            #f.savefig(os.path.split(file)[-1]+'_allcounts.pdf')

            if np.min(allcounts) < 0:
                print('WARNING: counts<0 for {},{}'.format(
                    filter.upper(), order))
                stop

            if '444' in filter:
                print("")
                print('444!!!!!!')
                print(allratio.value)
                print(len(allratio.value))
                print(type(allratio.value))
                #bad = allratio.value > 1e5
                bad = np.where(allratio.value > 1e5)
                print(len(bad[0]))
                print(type(bad[0]))
                print(alljansky.value[bad[0][0:10]])
                print(allcounts[bad[0][0:10]])
                print(ratio)
                print(self.str_per_detector)
                print(allratio.value[bad[0][0:10]])
                print(allwaves)
                stop

            # Pad allwaves and allratio to be the length needed by the
            # photom reference file. Also convert wavelengths to microns.
            allwaves = np.pad(allwaves / 1.e4, (0, self.maxlen - nelem),
                              'constant')
            allratio = np.pad(allratio, (0, self.maxlen - nelem), 'constant')

            print(allwaves[100:105])
            print(allcounts[100:105])
            print(alljansky[100:105])
            print(alljansky[100:105] / allcounts[100:105])
            print(ratio)
            print(allratio[100:105])

            print("******************")
            print("******************")
            print("******************")
            print("need real conversion factor and uncertainty!!!")
            conversion_factor = 1000.
            uncertainty = ratio * .1
            #row = [filter,pupil,np.int(order),ratio*conversion_factor,uncertainty,nelem,allwaves,allratio]

            # Populate lists that will be used to create the final table
            filters = np.append(filters, filter)
            pupils = np.append(pupils, pupil)
            orders = np.append(orders, np.int(order))
            fluxes = np.append(fluxes, ratio * conversion_factor)
            uncs = np.append(uncs, uncertainty)
            nelems = np.append(nelems, nelem)

            print(allwaves.shape)

            if len(waves) == 0:
                waves = allwaves
                waves = np.expand_dims(waves, axis=0)
                resps = allratio
                resps = np.expand_dims(resps, axis=0)
            else:
                waves = np.append(waves,
                                  np.expand_dims(allwaves, axis=0),
                                  axis=0)
                resps = np.append(resps,
                                  np.expand_dims(allratio, axis=0),
                                  axis=0)

        print('waves.shape', waves.shape)
        print('resps.shape', resps.shape)

        print(filters)
        print(pupils)
        print(orders)
        print(fluxes)
        print(uncs)
        print(nelems)
        print(waves[0, 40:45])
        print(resps[0, 40:45])

        # Zip all data together
        alldata = np.array(zip(np.array(filters), np.array(pupils),
                               np.array(fluxes), np.array(uncs),
                               np.array(orders), np.array(nelems),
                               np.array(waves), np.array(resps)),
                           dtype=self.model.phot_table.dtype)

        return alldata