Exemple #1
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
Exemple #2
0
def calculate_values(detector, filt, mjd, aper):
    # parameters can be removed from obsmode as needed
    obsmode = 'wfc3,{},{},mjd#{},aper#{}'.format(detector, filt, mjd, aper)
    bp = stsyn.band(obsmode)

    # STMag
    photflam = bp.unit_response(stsyn.conf.area)  # inverse sensitivity in flam
    stmag = -21.1 - 2.5 * log10(photflam.value)

    # Pivot Wavelength and bandwidth
    photplam = bp.pivot()  # pivot wavelength in angstroms
    bandwidth = bp.photbw()  # bandwidth in angstroms

    # ABMag
    abmag = stmag - 5 * log10(photplam.value) + 18.6921

    # Vegamag
    #for some reason stsyn.Vega doesn't load so we redefine it
    stsyn.Vega = SourceSpectrum.from_vega()
    obs = Observation(
        stsyn.Vega, bp, binset=bp.binset
    )  # synthetic observation of vega in bandpass using vega spectrum
    vegamag = -obs.effstim(flux_unit='obmag', area=stsyn.conf.area)

    return obsmode, photplam.value, bandwidth.value, photflam.value, stmag, abmag, vegamag.value
Exemple #3
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
Exemple #4
0
 def _observe_through_filter(bp, B, unit):
     if not synphot:
         raise AstropyWarning('synphot is required for bandpass filtering.')
     obs = Observation(B, bp)
     wave = obs.effective_wavelength()
     fluxd = obs.effstim(unit)
     return wave, fluxd
Exemple #5
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
Exemple #6
0
def calcMagFromSpec(bp, specPath, redden=None):
    sp = SourceSpectrum.from_file(specPath)
    if redden:
        sp = sp * redden
    obs = Observation(sp, bp)
    counts = obs.countrate(1.0)
    return -2.5 * np.log10(counts.value)
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
Exemple #8
0
    def kvega_to_f335abmag(self, kmag):
        '''Convert K band vegamag to F335M ABmag'''

        kmag = kmag * units.VEGAMAG
        new_g2v = self.g2v.normalize(kmag,
                                     band=self.kband,
                                     area=self.telescope_area,
                                     vegaspec=self.vega)
        obs = Observation(new_g2v, self.bp, binset=self.wave_bins)
        abmag = obs.effstim('ABmag', area=self.telescope_area)
        return abmag
Exemple #9
0
    def setup_class(self):
        """Subclass needs to define ``obsmode`` and ``spectrum``
        class variables for this to work.

        """
        if not HAS_PYSYNPHOT:
            raise ImportError(
                'ASTROLIB PYSYNPHOT must be installed to run these tests')

        # Make sure both software use the same graph and component tables.

        conf.graphtable = self.tables['graphtable']
        conf.comptable = self.tables['comptable']
        conf.thermtable = self.tables['thermtable']

        S.setref(graphtable=self.tables['graphtable'],
                 comptable=self.tables['comptable'],
                 thermtable=self.tables['thermtable'])

        # Construct spectra for both software.

        self.sp = parse_spec(self.spectrum)
        self.bp = band(self.obsmode)

        # Astropy version has no prior knowledge of instrument-specific
        # binset, so it has to be set explicitly.
        if hasattr(self.bp, 'binset'):
            self.obs = Observation(self.sp,
                                   self.bp,
                                   force=self.force,
                                   binset=self.bp.binset)
        else:
            self.obs = Observation(self.sp, self.bp, force=self.force)

        # Astropy version does not assume a default waveset
        # (you either have it or you don't). If there is no
        # waveset, no point comparing obs waveset against ASTROLIB.
        if self.sp.waveset is None or self.bp.waveset is None:
            self._has_obswave = False
        else:
            self._has_obswave = True

        self.spref = old_parse_spec(self.spectrum)
        self.bpref = S.ObsBandpass(self.obsmode)
        self.obsref = S.Observation(self.spref, self.bpref, force=self.force)

        # Ensure we are comparing in the same units
        self.bpref.convert(self.bp._internal_wave_unit.name)
        self.spref.convert(self.sp._internal_wave_unit.name)
        self.spref.convert(self.sp._internal_flux_unit.name)
        self.obsref.convert(self.obs._internal_wave_unit.name)
        self.obsref.convert(self.obs._internal_flux_unit.name)
Exemple #10
0
    def f335abmag_to_kvega(self, abmag):
        '''Convert F335M ABmag to K band vegamag'''

        abmag = abmag * u.ABmag
        new_g2v = self.g2v.normalize(abmag,
                                     band=self.bp,
                                     area=self.telescope_area,
                                     vegaspec=self.vega)
        obs = Observation(new_g2v, self.kband, binset=self.wave_bins)
        kmag = obs.effstim('Vegamag',
                           area=self.telescope_area,
                           vegaspec=self.vega)
        return kmag
    def setup_class(self):
        """Subclass needs to define ``obsmode`` and ``spectrum``
        class variables for this to work.

        """
        if not HAS_PYSYNPHOT:
            raise ImportError(
                'ASTROLIB PYSYNPHOT must be installed to run these tests')

        # Make sure both software use the same graph and component tables.

        conf.graphtable = self.tables['graphtable']
        conf.comptable = self.tables['comptable']
        conf.thermtable = self.tables['thermtable']

        S.setref(graphtable=self.tables['graphtable'],
                 comptable=self.tables['comptable'],
                 thermtable=self.tables['thermtable'])

        # Construct spectra for both software.

        self.sp = parse_spec(self.spectrum)
        self.bp = band(self.obsmode)

        # Astropy version has no prior knowledge of instrument-specific
        # binset, so it has to be set explicitly.
        if hasattr(self.bp, 'binset'):
            self.obs = Observation(self.sp, self.bp, force=self.force,
                                   binset=self.bp.binset)
        else:
            self.obs = Observation(self.sp, self.bp, force=self.force)

        # Astropy version does not assume a default waveset
        # (you either have it or you don't). If there is no
        # waveset, no point comparing obs waveset against ASTROLIB.
        if self.sp.waveset is None or self.bp.waveset is None:
            self._has_obswave = False
        else:
            self._has_obswave = True

        self.spref = old_parse_spec(self.spectrum)
        self.bpref = S.ObsBandpass(self.obsmode)
        self.obsref = S.Observation(self.spref, self.bpref, force=self.force)

        # Ensure we are comparing in the same units
        self.bpref.convert(self.bp._internal_wave_unit.name)
        self.spref.convert(self.sp._internal_wave_unit.name)
        self.spref.convert(self.sp._internal_flux_unit.name)
        self.obsref.convert(self.obs._internal_wave_unit.name)
        self.obsref.convert(self.obs._internal_flux_unit.name)
Exemple #12
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
Exemple #13
0
    def get_magnitude(self, filter_curve=None, system_name="AB"):
        """
            Obtain the magnitude in filter for a user specified photometric system

            Parameters
            ----------
            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
            system_name: str
                The photometric system Vega, AB or ST


            Returns
            -------
            spectrum : a Spextrum
                Input spectrum scaled to the given amplitude in the given filter
        """
        if 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)

        if system_name.lower() in ["vega"]:
            unit = u.mag
        elif system_name.lower() in ["st", "hst"]:
            unit = u.STmag
        else:
            unit = u.ABmag

        ref_spec = self.flat_spectrum(amplitude=0 * unit)
        ref_flux = Observation(ref_spec,
                               filter_curve).effstim(flux_unit=units.PHOTLAM)
        real_flux = Observation(self,
                                filter_curve).effstim(flux_unit=units.PHOTLAM)

        mag = -2.5 * np.log10(real_flux.value / ref_flux.value)

        return mag * unit
def zero_mag_flux(filter_name, photometric_system, return_filter=False):
    """
    Returns the zero magnitude photon flux for a filter

    Acceptable filter names are those given in
    ``scopesim.effects.ter_curves_utils.FILTER_DEFAULTS`` or a string with an
    appropriate name for a filter in the Spanish-VO filter-service. Such strings
    must use the naming convention: observatory/instrument.filter. E.g:
    ``paranal/HAWKI.Ks``, or ``Gemini/GMOS-N.CaT``.

    Parameters
    ----------
    filter_name : str
        Name of the filter - see above

    photometric_system : str
        ["vega", "AB", "ST"] Name of the photometric system

    return_filter : bool, optional
        If True, also returns the filter curve object

    Returns
    -------
    flux : float
        [PHOTLAM]
    filt : ``synphot.SpectralElement``
        If ``return_filter`` is True

    """

    filt = get_filter(filter_name)
    spec = get_zero_mag_spectrum(photometric_system)

    obs = Observation(spec, filt)
    flux = obs.effstim(flux_unit=PHOTLAM)

    if return_filter:
        return flux, filt
    else:
        return flux
Exemple #15
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)
Exemple #16
0
    def get_flux(self,
                 wmin=None,
                 wmax=None,
                 filter_curve=None,
                 flux_unit=units.FLAM):
        """
        Return the flux within a passband

        Parameters
        ----------
        wmin : float, u.Quantity
           minimum wavelength
        wmax : float, u.Quantity
          maximum wavelength
        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
        flux_unit: synphot.units, u.Quantity

        Returns
        -------

        """

        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:
                filter_curve = Passband.square(wmin=wmin, wmax=wmax)

            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)

        flux = Observation(self, filter_curve).effstim(flux_unit=flux_unit)

        return flux
Exemple #17
0
    def getcountsflux(self, bandpass):
        """
        Calculate the total flux through a bandpass

        Arguments:
        ----------
        bandpass -- synphot SpectralElement object that contains the
                    bandpass to use

        Returns:
        A synphot Observation object that combines the bandpass
        with a Vega spectrum
        """
        obs = Observation(self.vega, bandpass)
        return obs
Exemple #18
0
def get_limmag(source_spectrum, *, snr, exptime, coord, time, night,
               bandpass=None):
    """Get the limiting magnitude for a given SNR.

    Parameters
    ----------
    source_spectrum : synphot.SourceSpectrum
        The spectrum of the source.
    snr : float
        The desired SNR.
    exptime : astropy.units.Quantity
        The exposure time
    coord : astropy.coordinates.SkyCoord
        The coordinates of the source, for calculating zodiacal light
    time : astropy.time.Time
        The time of the observation, for calculating zodiacal light
    night : bool
        Whether the observation occurs on the day or night side of the Earth,
        for estimating airglow
    bandpass : synphot.SpecralElement
        Bandpass. Default: Dorado current baseline estimate.

    Returns
    -------
    astropy.units.Quantity
        The AB magnitude of the source
    """
    if bandpass is None:
        bandpass = bandpasses.NUV_D

    mag0 = Observation(source_spectrum, bandpass).effstim(
        u.ABmag, area=constants.AREA)

    result = _amp_for_signal_to_noise_oir_ccd(
        snr,
        exptime,
        constants.APERTURE_CORRECTION * _get_count_rate(
            source_spectrum, bandpass),
        _get_background_count_rate(bandpass, coord, time, night),
        constants.DARK_NOISE,
        constants.READ_NOISE,
        constants.NPIX
    ).to_value(u.dimensionless_unscaled)

    return -2.5 * np.log10(result) * u.mag + mag0
Exemple #19
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
Exemple #20
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
def flux2ABmag(wave, flux, band, mag, plot=False):

    # "band" should be either "sdss_g" or "V" for the moment

    # The input "wave" should be in nm

    # Define bandpass:
    if band == "sdss_g":
        bp = SpectralElement.from_file('../utility/photometry/g.dat')
        magvega = -0.08
    elif band == "V":
        bp = SpectralElement.from_filter('johnson_v')
        magvega = 0.03

    # SDSS g-band magnitude of Vega
    #gmagvega=-0.08

    # Read the spectrum of Vega
    sp_vega = SourceSpectrum.from_vega()
    wv_vega = sp_vega.waveset
    fl_vega = sp_vega(wv_vega, flux_unit=units.FLAM)

    ## Convolve with the bandpass
    obs_vega = Observation(sp_vega, bp)

    ## Integrated flux
    fluxtot_vega = obs_vega.integrate()

    # Read the synthetic spectrum
    sp = SourceSpectrum(Empirical1D, points = wave * 10., \
                        lookup_table=flux*units.FLAM)
    wv = sp.waveset
    fl = sp(wv, flux_unit=units.FLAM)

    ## Convolve with the bandpass
    obs = Observation(sp, bp, force='extrap')

    ## Integrated g-band flux
    fluxtot = obs.integrate()

    # Scaling factor to make the flux compatible with the desired magnitude
    dm = mag - magvega
    const = fluxtot_vega * 10**(-0.4 * dm) / fluxtot

    # Scale the original flux by const
    fl_scale = const * fl

    # Convert to ABmag
    fl_scale_mag = units.convert_flux(wv, fl_scale, out_flux_unit='abmag')

    sp_scaled_mag = SourceSpectrum(Empirical1D,
                                   points=wv,
                                   lookup_table=fl_scale_mag)

    # Plot
    if plot == True:
        fig, ax = plt.subplots(2, 1, sharex=True)
        ax[0].plot(wave * 10., flux, linestyle="-", marker="")
        ax[1].plot(wv[1:-1],
                   sp_scaled_mag(wv, flux_unit=u.ABmag)[1:-1],
                   linestyle="-",
                   marker="")
        ax[1].set_xlabel("Angstrom")
        ax[0].set_ylabel("$F_{\lambda}$")
        ax[1].set_ylabel("ABmag")
        ax[1].set_ylim(mag + 3., mag - 2.0)
        #plt.show()

    return (wv[1:-1] / 10., sp_scaled_mag(wv, flux_unit=u.ABmag)[1:-1])
class CommCase(object):
    """Base class for commissioning tests."""
    obsmode = None  # Observation mode string
    spectrum = None  # SYNPHOT-like string to construct spectrum
    force = None

    # Default tables are the latest available as of 2016-07-25.
    tables = {
        'graphtable': os.path.join('mtab$OLD_FILES', '07r1502mm_tmg.fits'),
        'comptable': os.path.join('mtab$OLD_FILES', '07r1502nm_tmc.fits'),
        'thermtable': 'mtab$tae17277m_tmt.fits'}

    def setup_class(self):
        """Subclass needs to define ``obsmode`` and ``spectrum``
        class variables for this to work.

        """
        if not HAS_PYSYNPHOT:
            raise ImportError(
                'ASTROLIB PYSYNPHOT must be installed to run these tests')

        # Make sure both software use the same graph and component tables.

        conf.graphtable = self.tables['graphtable']
        conf.comptable = self.tables['comptable']
        conf.thermtable = self.tables['thermtable']

        S.setref(graphtable=self.tables['graphtable'],
                 comptable=self.tables['comptable'],
                 thermtable=self.tables['thermtable'])

        # Construct spectra for both software.

        self.sp = parse_spec(self.spectrum)
        self.bp = band(self.obsmode)

        # Astropy version has no prior knowledge of instrument-specific
        # binset, so it has to be set explicitly.
        if hasattr(self.bp, 'binset'):
            self.obs = Observation(self.sp, self.bp, force=self.force,
                                   binset=self.bp.binset)
        else:
            self.obs = Observation(self.sp, self.bp, force=self.force)

        # Astropy version does not assume a default waveset
        # (you either have it or you don't). If there is no
        # waveset, no point comparing obs waveset against ASTROLIB.
        if self.sp.waveset is None or self.bp.waveset is None:
            self._has_obswave = False
        else:
            self._has_obswave = True

        self.spref = old_parse_spec(self.spectrum)
        self.bpref = S.ObsBandpass(self.obsmode)
        self.obsref = S.Observation(self.spref, self.bpref, force=self.force)

        # Ensure we are comparing in the same units
        self.bpref.convert(self.bp._internal_wave_unit.name)
        self.spref.convert(self.sp._internal_wave_unit.name)
        self.spref.convert(self.sp._internal_flux_unit.name)
        self.obsref.convert(self.obs._internal_wave_unit.name)
        self.obsref.convert(self.obs._internal_flux_unit.name)

    @staticmethod
    def _get_new_wave(sp):
        """Astropy version does not assume a default waveset
        (you either have it or you don't). This is a convenience
        method to duck-type ASTROLIB waveset behavior.
        """
        wave = sp.waveset
        if wave is None:
            wave = conf.waveset_array
        else:
            wave = wave.value
        return wave

    def _assert_allclose(self, actual, desired, rtol=1e-07,
                         atol=sys.float_info.min):
        """``assert_allclose`` only report percentage but we
        also want to know some extra info conveniently."""
        if isinstance(actual, Iterable):
            ntot = len(actual)
        else:
            ntot = 1

        n = np.count_nonzero(
            abs(actual - desired) > atol + rtol * abs(desired))
        msg = 'obsmode: {0}\nspectrum: {1}\n(mismatch {2}/{3})'.format(
            self.obsmode, self.spectrum, n, ntot)
        assert_allclose(actual, desired, rtol=rtol, atol=atol, err_msg=msg)

    # TODO: Confirm whether non-default atol is acceptable.
    #       Have to use this value to avoid AssertionError for very
    #       small non-zero flux values like 1.8e-26 to 2e-311.
    def _compare_nonzero(self, new, old, thresh=0.01, atol=1e-29):
        """Compare normally when results from both are non-zero."""
        i = (new != 0) & (old != 0)

        # Make sure non-zero atol is not too high, otherwise just let it fail.
        if atol > (thresh * min(new.max(), old.max())):
            atol = sys.float_info.min

        self._assert_allclose(new[i], old[i], rtol=thresh, atol=atol)

    def _compare_zero(self, new, old, thresh=0.01):
        """Special handling for comparison when one of the results
        is zero. This is because ``rtol`` will not work."""
        i = ((new == 0) | (old == 0)) & (new != old)
        try:
            self._assert_allclose(new[i], old[i], rtol=thresh)
        except AssertionError as e:
            pytest.xfail(str(e))  # TODO: Will revisit later

    def test_band_wave(self, thresh=0.01):
        """Test bandpass waveset."""
        wave = self._get_new_wave(self.bp)
        self._assert_allclose(wave, self.bpref.wave, rtol=thresh)

    def test_spec_wave(self, thresh=0.01):
        """Test source spectrum waveset."""
        wave = self._get_new_wave(self.sp)

        # TODO: Failure due to different wavesets for blackbody; Ignore?
        try:
            self._assert_allclose(wave, self.spref.wave, rtol=thresh)
        except (AssertionError, ValueError):
            self._has_obswave = False  # Skip obs waveset tests
            if 'bb(' in self.spectrum:
                pytest.xfail('Blackbody waveset implementations are different')
            elif 'unit(' in self.spectrum:
                pytest.xfail('Flat does not use default waveset anymore')
            else:
                raise

    def test_obs_wave(self, thresh=0.01):
        """Test observation waveset."""
        if not self._has_obswave:  # Nothing to test
            return

        # Native
        wave = self.obs.waveset.value

        # TODO: Failure due to different wavesets for blackbody; Ignore?
        try:
            self._assert_allclose(wave, self.obsref.wave, rtol=thresh)
        except (AssertionError, ValueError):
            if 'bb(' in self.spectrum:
                pytest.xfail('Blackbody waveset implementations are different')
            elif 'unit(' in self.spectrum:
                self._has_obswave = False  # Skip binned flux test
                pytest.xfail('Flat does not use default waveset anymore')
            else:
                raise

        # Binned
        binset = self.obs.binset.value
        self._assert_allclose(binset, self.obsref.binwave, rtol=thresh)

    @pytest.mark.parametrize('thrutype', ['zero', 'nonzero'])
    def test_band_thru(self, thrutype, thresh=0.01):
        """Test bandpass throughput, which is always between 0 and 1."""
        wave = self.bpref.wave
        thru = self.bp(wave).value

        if thrutype == 'zero':
            self._compare_zero(thru, self.bpref.throughput, thresh=thresh)
        else:  # nonzero
            self._compare_nonzero(thru, self.bpref.throughput, thresh=thresh)

    @pytest.mark.parametrize('fluxtype', ['zero', 'nonzero'])
    def test_spec_flux(self, fluxtype, thresh=0.01):
        """Test flux for source spectrum in PHOTLAM."""
        wave = self.spref.wave
        flux = self.sp(wave).value

        if fluxtype == 'zero':
            self._compare_zero(flux, self.spref.flux, thresh=thresh)
        else:  # nonzero
            self._compare_nonzero(flux, self.spref.flux, thresh=thresh)

    @pytest.mark.parametrize('fluxtype', ['zero', 'nonzero'])
    def test_obs_flux(self, fluxtype, thresh=0.01):
        """Test flux for observation in PHOTLAM."""
        wave = self.obsref.wave
        flux = self.obs(wave).value

        # Native
        if fluxtype == 'zero':
            self._compare_zero(flux, self.obsref.flux, thresh=thresh)
        else:  # nonzero
            self._compare_nonzero(flux, self.obsref.flux, thresh=thresh)

        if not self._has_obswave:  # Do not compare binned flux
            return

        # Binned (cannot be resampled)
        binflux = self.obs.binflux.value
        if fluxtype == 'zero':
            self._compare_zero(binflux, self.obsref.binflux, thresh=thresh)
        else:  # nonzero
            try:
                self._compare_nonzero(binflux, self.obsref.binflux,
                                      thresh=thresh)
            except AssertionError as e:
                if 'unit(' in self.spectrum:
                    pytest.xfail('Flat does not use default waveset anymore:\n'
                                 '{0}'.format(str(e)))
                else:
                    raise

    def test_countrate(self, thresh=0.01):
        """Test observation countrate calculations."""
        ans = self.obsref.countrate()

        # Astropy version does not assume a default area.
        val = self.obs.countrate(conf.area).value

        self._assert_allclose(val, ans, rtol=thresh)

    def test_efflam(self, thresh=0.01):
        """Test observation effective wavelength."""
        ans = self.obsref.efflam()
        val = self.obs.effective_wavelength().value
        self._assert_allclose(val, ans, rtol=thresh)

    def teardown_class(self):
        """Reset config for both software."""
        for cfgname in self.tables:
            conf.reset(cfgname)

        S.setref()
Exemple #23
0
class CommCase(object):
    """Base class for commissioning tests."""
    obsmode = None  # Observation mode string
    spectrum = None  # SYNPHOT-like string to construct spectrum
    force = None

    # Default tables are the latest available as of 2016-07-25.
    tables = {
        'graphtable': os.path.join('mtab$OLD_FILES', '07r1502mm_tmg.fits'),
        'comptable': os.path.join('mtab$OLD_FILES', '07r1502nm_tmc.fits'),
        'thermtable': 'mtab$tae17277m_tmt.fits'
    }

    def setup_class(self):
        """Subclass needs to define ``obsmode`` and ``spectrum``
        class variables for this to work.

        """
        if not HAS_PYSYNPHOT:
            raise ImportError(
                'ASTROLIB PYSYNPHOT must be installed to run these tests')

        # Make sure both software use the same graph and component tables.

        conf.graphtable = self.tables['graphtable']
        conf.comptable = self.tables['comptable']
        conf.thermtable = self.tables['thermtable']

        S.setref(graphtable=self.tables['graphtable'],
                 comptable=self.tables['comptable'],
                 thermtable=self.tables['thermtable'])

        # Construct spectra for both software.

        self.sp = parse_spec(self.spectrum)
        self.bp = band(self.obsmode)

        # Astropy version has no prior knowledge of instrument-specific
        # binset, so it has to be set explicitly.
        if hasattr(self.bp, 'binset'):
            self.obs = Observation(self.sp,
                                   self.bp,
                                   force=self.force,
                                   binset=self.bp.binset)
        else:
            self.obs = Observation(self.sp, self.bp, force=self.force)

        # Astropy version does not assume a default waveset
        # (you either have it or you don't). If there is no
        # waveset, no point comparing obs waveset against ASTROLIB.
        if self.sp.waveset is None or self.bp.waveset is None:
            self._has_obswave = False
        else:
            self._has_obswave = True

        self.spref = old_parse_spec(self.spectrum)
        self.bpref = S.ObsBandpass(self.obsmode)
        self.obsref = S.Observation(self.spref, self.bpref, force=self.force)

        # Ensure we are comparing in the same units
        self.bpref.convert(self.bp._internal_wave_unit.name)
        self.spref.convert(self.sp._internal_wave_unit.name)
        self.spref.convert(self.sp._internal_flux_unit.name)
        self.obsref.convert(self.obs._internal_wave_unit.name)
        self.obsref.convert(self.obs._internal_flux_unit.name)

    @staticmethod
    def _get_new_wave(sp):
        """Astropy version does not assume a default waveset
        (you either have it or you don't). This is a convenience
        method to duck-type ASTROLIB waveset behavior.
        """
        wave = sp.waveset
        if wave is None:
            wave = conf.waveset_array
        else:
            wave = wave.value
        return wave

    def _assert_allclose(self,
                         actual,
                         desired,
                         rtol=1e-07,
                         atol=sys.float_info.min):
        """``assert_allclose`` only report percentage but we
        also want to know some extra info conveniently."""
        if isinstance(actual, Iterable):
            ntot = len(actual)
        else:
            ntot = 1

        n = np.count_nonzero(
            abs(actual - desired) > atol + rtol * abs(desired))
        msg = 'obsmode: {0}\nspectrum: {1}\n(mismatch {2}/{3})'.format(
            self.obsmode, self.spectrum, n, ntot)
        assert_allclose(actual, desired, rtol=rtol, atol=atol, err_msg=msg)

    # TODO: Confirm whether non-default atol is acceptable.
    #       Have to use this value to avoid AssertionError for very
    #       small non-zero flux values like 1.8e-26 to 2e-311.
    def _compare_nonzero(self, new, old, thresh=0.01, atol=1e-29):
        """Compare normally when results from both are non-zero."""
        i = (new != 0) & (old != 0)

        # Make sure non-zero atol is not too high, otherwise just let it fail.
        if atol > (thresh * min(new.max(), old.max())):
            atol = sys.float_info.min

        self._assert_allclose(new[i], old[i], rtol=thresh, atol=atol)

    def _compare_zero(self, new, old, thresh=0.01):
        """Special handling for comparison when one of the results
        is zero. This is because ``rtol`` will not work."""
        i = ((new == 0) | (old == 0)) & (new != old)
        try:
            self._assert_allclose(new[i], old[i], rtol=thresh)
        except AssertionError as e:
            pytest.xfail(str(e))  # TODO: Will revisit later

    def test_band_wave(self, thresh=0.01):
        """Test bandpass waveset."""
        wave = self._get_new_wave(self.bp)
        self._assert_allclose(wave, self.bpref.wave, rtol=thresh)

    def test_spec_wave(self, thresh=0.01):
        """Test source spectrum waveset."""
        wave = self._get_new_wave(self.sp)

        # TODO: Failure due to different wavesets for blackbody; Ignore?
        try:
            self._assert_allclose(wave, self.spref.wave, rtol=thresh)
        except (AssertionError, ValueError) as e:
            self._has_obswave = False  # Skip obs waveset tests
            if 'bb(' in self.spectrum:
                pytest.xfail('Blackbody waveset implementations are different')
            elif 'unit(' in self.spectrum:
                pytest.xfail('Flat does not use default waveset anymore')
            else:
                raise

    def test_obs_wave(self, thresh=0.01):
        """Test observation waveset."""
        if not self._has_obswave:  # Nothing to test
            return

        # Native
        wave = self.obs.waveset.value

        # TODO: Failure due to different wavesets for blackbody; Ignore?
        try:
            self._assert_allclose(wave, self.obsref.wave, rtol=thresh)
        except (AssertionError, ValueError) as e:
            if 'bb(' in self.spectrum:
                pytest.xfail('Blackbody waveset implementations are different')
            elif 'unit(' in self.spectrum:
                self._has_obswave = False  # Skip binned flux test
                pytest.xfail('Flat does not use default waveset anymore')
            else:
                raise

        # Binned
        binset = self.obs.binset.value
        self._assert_allclose(binset, self.obsref.binwave, rtol=thresh)

    @pytest.mark.parametrize('thrutype', ['zero', 'nonzero'])
    def test_band_thru(self, thrutype, thresh=0.01):
        """Test bandpass throughput, which is always between 0 and 1."""
        wave = self.bpref.wave
        thru = self.bp(wave).value

        if thrutype == 'zero':
            self._compare_zero(thru, self.bpref.throughput, thresh=thresh)
        else:  # nonzero
            self._compare_nonzero(thru, self.bpref.throughput, thresh=thresh)

    @pytest.mark.parametrize('fluxtype', ['zero', 'nonzero'])
    def test_spec_flux(self, fluxtype, thresh=0.01):
        """Test flux for source spectrum in PHOTLAM."""
        wave = self.spref.wave
        flux = self.sp(wave).value

        if fluxtype == 'zero':
            self._compare_zero(flux, self.spref.flux, thresh=thresh)
        else:  # nonzero
            self._compare_nonzero(flux, self.spref.flux, thresh=thresh)

    @pytest.mark.parametrize('fluxtype', ['zero', 'nonzero'])
    def test_obs_flux(self, fluxtype, thresh=0.01):
        """Test flux for observation in PHOTLAM."""
        wave = self.obsref.wave
        flux = self.obs(wave).value

        # Native
        if fluxtype == 'zero':
            self._compare_zero(flux, self.obsref.flux, thresh=thresh)
        else:  # nonzero
            self._compare_nonzero(flux, self.obsref.flux, thresh=thresh)

        if not self._has_obswave:  # Do not compare binned flux
            return

        # Binned (cannot be resampled)
        binflux = self.obs.binflux.value
        if fluxtype == 'zero':
            self._compare_zero(binflux, self.obsref.binflux, thresh=thresh)
        else:  # nonzero
            try:
                self._compare_nonzero(binflux,
                                      self.obsref.binflux,
                                      thresh=thresh)
            except AssertionError as e:
                if 'unit(' in self.spectrum:
                    pytest.xfail('Flat does not use default waveset anymore:\n'
                                 '{0}'.format(str(e)))
                else:
                    raise

    def test_countrate(self, thresh=0.01):
        """Test observation countrate calculations."""
        ans = self.obsref.countrate()

        # Astropy version does not assume a default area.
        val = self.obs.countrate(conf.area).value

        self._assert_allclose(val, ans, rtol=thresh)

    def test_efflam(self, thresh=0.01):
        """Test observation effective wavelength."""
        ans = self.obsref.efflam()
        val = self.obs.effective_wavelength().value
        self._assert_allclose(val, ans, rtol=thresh)

    def teardown_class(self):
        """Reset config for both software."""
        for cfgname in self.tables:
            conf.reset(cfgname)

        S.setref()
Exemple #24
0
def calcMag(bp, temp):
    bb = SourceSpectrum(BlackBodyNorm1D, temperature=temp)
    obs = Observation(bb, bp)
    counts = obs.countrate(1.0)
    return -2.5 * np.log10(counts.value)
def test_spectra_rescaling():
    """Test the functionality for rescaling input spectra to a given
    magnitude in a given filter
    """
    # JWST primary mirror area in cm^2. Needed for countrate check
    # at the end
    primary_area = 25.326 * (u.m * u.m)

    # Create spectrum: one source to be normalized
    # and the other should not be
    waves = np.arange(0.4, 5.6, 0.01)
    flux = np.repeat(1e-16, len(waves))
    flux2 = np.repeat(4.24242424242e-18, len(waves))  # arbitrary value
    spectra = {
        1: {
            "wavelengths": waves * u.micron,
            "fluxes": flux * u.pct
        },
        2: {
            "wavelengths": waves * u.micron,
            "fluxes": flux2 * units.FLAM
        }
    }

    # Create source catalog containing scaling info
    catalog = Table()
    catalog['index'] = [1, 2]
    catalog['nircam_f322w2_magnitude'] = [18.] * 2
    catalog['niriss_f090w_magnitude'] = [18.] * 2
    #catalog['fgs_magnitude'] = [18.] * 2

    # Instrument info
    instrument = ['nircam', 'niriss']  # , 'fgs']
    filter_name = ['F322W2', 'F090W']  # , 'N/A']
    module = ['B', 'N']  # , 'F']
    detector = ['NRCA1', 'NIS']  # , 'GUIDER1']

    # Magnitude systems of renormalization magnitudes
    mag_sys = ['vegamag', 'abmag', 'stmag']

    # Loop over options and test each
    for inst, filt, mod, det in zip(instrument, filter_name, module, detector):

        # Extract the appropriate column from the catalog information
        magcol = [col for col in catalog.colnames if inst in col]
        sub_catalog = catalog['index', magcol[0]]

        # Filter throughput files
        filter_thru_file = get_filter_throughput_file(instrument=inst,
                                                      filter_name=filt,
                                                      nircam_module=mod,
                                                      fgs_detector=det)

        # Retrieve the correct gain value that goes with the fluxcal info
        if inst == 'nircam':
            gain = MEAN_GAIN_VALUES['nircam']['lwb']
        elif inst == 'niriss':
            gain = MEAN_GAIN_VALUES['niriss']
        elif inst == 'fgs':
            gain = MEAN_GAIN_VALUES['fgs'][det.lower()]

        # Create filter bandpass object, to be used in the final
        # comparison
        filter_tp = ascii.read(filter_thru_file)
        bp_waves = filter_tp['Wavelength_microns'].data * u.micron
        thru = filter_tp['Throughput'].data
        bandpass = SpectralElement(
            Empirical1D, points=bp_waves, lookup_table=thru) / gain

        # Check the renormalization in all photometric systems
        for magsys in mag_sys:
            rescaled_spectra = spec.rescale_normalized_spectra(
                spectra, sub_catalog, magsys, filter_thru_file, gain)

            # Calculate the countrate associated with the renormalized
            # spectrum through the requested filter
            for dataset in rescaled_spectra:
                if dataset == 1:
                    # This block is for the spectra that are rescaled
                    rescaled_spectrum = SourceSpectrum(
                        Empirical1D,
                        points=rescaled_spectra[dataset]['wavelengths'],
                        lookup_table=rescaled_spectra[dataset]['fluxes'])

                    obs = Observation(rescaled_spectrum,
                                      bandpass,
                                      binset=bandpass.waveset)
                    renorm_counts = obs.countrate(area=primary_area)

                    # Calculate the countrate associated with an object of
                    # matching magnitude
                    if inst != 'fgs':
                        mag_col = '{}_{}_magnitude'.format(
                            inst.lower(), filt.lower())
                    else:
                        mag_col = 'fgs_magnitude'
                    filt_info = spec.get_filter_info([mag_col], magsys)
                    magnitude = catalog[mag_col][dataset - 1]
                    photflam, photfnu, zeropoint, pivot = filt_info[mag_col]
                    check_counts = magnitude_to_countrate(
                        'imaging',
                        magsys,
                        magnitude,
                        photfnu=photfnu.value,
                        photflam=photflam.value,
                        vegamag_zeropoint=zeropoint)

                    if magsys != 'vegamag':
                        # As long as the correct gain is used, AB mag and ST mag
                        # count rates agree very well
                        tolerance = 0.0005
                    else:
                        # Vegamag count rates for NIRISS have a sligtly larger
                        # disagreement. Zeropoints were derived assuming Vega = 0.02
                        # magnitudes. This offset has been added to the rescaling
                        # function, but may not be exact.
                        tolerance = 0.0015

                    # This dataset has been rescaled, so check that the
                    # countrate from the rescaled spectrum matches that from
                    # the magnitude it was rescaled to
                    if isinstance(check_counts, u.quantity.Quantity):
                        check_counts = check_counts.value
                    if isinstance(renorm_counts, u.quantity.Quantity):
                        renorm_counts = renorm_counts.value

                    print(inst, filt, magsys, renorm_counts, check_counts,
                          renorm_counts / check_counts)
                    assert np.isclose(renorm_counts, check_counts, atol=0, rtol=tolerance), \
                        print('Failed assertion: ', inst, filt, magsys, renorm_counts, check_counts,
                              renorm_counts / check_counts)
                elif dataset == 2:
                    # Not rescaled. In this case Mirage ignores the magnitude
                    # value in the catalog, so we can't check against check_counts.
                    # Just make sure that the rescaling function did not
                    # change the spectrum at all
                    assert np.all(spectra[dataset]['fluxes'] ==
                                  rescaled_spectra[dataset]['fluxes'])
def scale_spectrum(spectrum, filter_name, amplitude):
    """
    Scales a SourceSpectrum to a value in a filter

    Parameters
    ----------
    spectrum : synphot.SourceSpectrum

    filter_name : str
        Name of a filter from
        - a local instrument package (available in ``rc.__search_path__``)
        - a generic filter name (see ``ter_curves_utils.FILTER_DEFAULTS``)
        - a spanish-vo filter service reference (e.g. ``"Paranal/HAWKI.Ks"``)

    amplitude : ``astropy.Quantity``, float
        The value that the spectrum should have in the given filter. Acceptable
        astropy quantities are:
        - u.mag : Vega magnitudes
        - u.ABmag : AB magnitudes
        - u.STmag : HST magnitudes
        - u.Jy : Jansky per filter bandpass
        Additionally the ``FLAM`` and ``FNU`` units from ``synphot.units`` can
        be used when passing the quantity for ``amplitude``:

    Returns
    -------
    spectrum : synphot.SourceSpectrum
        Input spectrum scaled to the given amplitude in the given filter

    Examples
    --------
    ::

        >>> from scopesim.source.source_templates import vega_spectrum
        >>> from scopesim.effects.ter_curves_utils as ter_utils
        >>>
        >>> spec = vega_spectrum()
        >>> vega_185 = ter_utils.scale_spectrum(spec, "Ks", -1.85 * u.mag)
        >>> ab_0 = ter_utils.scale_spectrum(spec, "Ks", 0 * u.ABmag)
        >>> jy_3630 = ter_utils.scale_spectrum(spec, "Ks", 3630 * u.Jy)

    """

    if isinstance(amplitude, u.Quantity):
        if amplitude.unit.physical_type == "spectral flux density":
            if amplitude.unit != u.ABmag:
                amplitude = amplitude.to(u.ABmag)
            ref_spec = ab_spectrum(amplitude.value)

        elif amplitude.unit.physical_type == "spectral flux density wav":
            if amplitude.unit != u.STmag:
                amplitude = amplitude.to(u.STmag)
            ref_spec = st_spectrum(amplitude.value)

        elif amplitude.unit == u.mag:
            ref_spec = vega_spectrum(amplitude.value)

        else:
            raise ValueError(f"Units of amplitude must be one of "
                             f"[u.mag, u.ABmag, u.STmag, u.Jy]: {amplitude}")
    else:
        ref_spec = vega_spectrum(amplitude)

    filt = get_filter(filter_name)
    ref_flux = Observation(ref_spec, filt).effstim(flux_unit=PHOTLAM)

    real_flux = Observation(spectrum, filt).effstim(flux_unit=PHOTLAM)
    scale_factor = ref_flux / real_flux
    spectrum *= scale_factor.value

    return spectrum
Exemple #27
0
    def scale_to_magnitude(self, amplitude, filter_curve=None):
        """
            Scales a Spectrum to a value in a filter
            copied from scopesim.effects.ter_curves.scale_spectrum with slight modifications
            Parameters
            ----------
            amplitude : ``astropy.Quantity``, float
                The value that the spectrum should have in the given filter. Acceptable
                astropy quantities are:
                - u.mag : Vega magnitudes
                - u.ABmag : AB magnitudes
                - u.STmag : HST magnitudes
                - u.Jy : Jansky per filter bandpass
                Additionally the ``FLAM`` and ``FNU`` units from ``synphot.units`` can
                be used when passing the quantity for ``amplitude``:

            filter_name : 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 a filename containing the curve (see ``Passband``)
                - a ``Passband`` or ``synphot.SpectralElement`` instance
            filter_file: str
                A file with a transmission curve


            Returns
            -------
            spectrum : a Spectrum
                Input spectrum scaled to the given amplitude in the given filter
        """

        if 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)

        if isinstance(amplitude, u.Quantity) is False:
            amplitude = amplitude * u.ABmag

    #     if amplitude.unit.physical_type == "spectral flux density":
    #         if amplitude.unit != u.ABmag:
    #             amplitude = amplitude.to(u.ABmag)
    #         ref_spec = self.flat_spectrum(amplitude=amplitude.value, system_name="AB")

    #    elif amplitude.unit.physical_type == "spectral flux density wav":
    #         if amplitude.unit != u.STmag:
    #             amplitude = amplitude.to(u.STmag)
    #         ref_spec = self.flat_spectrum(amplitude=amplitude.value, system_name="ST")

#            elif amplitude.unit == u.mag:
#               ref_spec = self.flat_spectrum(amplitude=amplitude.value, system_name="Vega")

#          else:
#             raise ValueError("Units of amplitude must be one of "
#                             "[u.mag, u.ABmag, u.STmag]: {}".format(amplitude))
#    else:

        ref_spec = self.flat_spectrum(amplitude=amplitude)

        ref_flux = Observation(ref_spec,
                               filter_curve).effstim(flux_unit=units.PHOTLAM)
        real_flux = Observation(self,
                                filter_curve).effstim(flux_unit=units.PHOTLAM)

        scale_factor = ref_flux / real_flux
        sp = self * scale_factor
        sp = self._restore_attr(sp)
        sp.meta.update({"magnitude": amplitude, "filter_curve": filter_curve})

        return sp