コード例 #1
0
ファイル: cannon.py プロジェクト: sdss/doppler
def prepare_cannon_model(model, spec, dointerp=False):
    """
    This prepares a Cannon model or list of models for an observed spectrum.
    The models are trimmed, rebinned, convolved to the spectrum LSF's and
    finally interpolated onto the spectrum's wavelength scale.

    The final interpolation can be omitted by setting dointerp=False (the default).
    This can be useful if the model is to interpolated multiple times on differen
    wavelength scales (e.g. for multiple velocities or logarithmic wavelength
    scales for cross-correlation).

    Parameters
    ----------
    model : Cannon model or list
       The Cannon model(s) to prepare for "spec".
    spec : Spec1D object
       The observed spectrum for which to prepare the Cannon model(s).
    dointerp : bool, optional
       Do the interpolation onto the observed wavelength scale.
       The default is False.

    Returns
    -------
    omodel : Cannon model or list
        The Cannon model or list of models prepared for "spec".

    Examples
    --------

    omodel = prepare_cannon_model(model,spec)

    """

    if spec.wave is None:
        raise Exception('No wavelength in observed spectrum')
    if spec.lsf is None:
        raise Exception('No LSF in observed spectrum')
    if type(model) is not list:
        if model.dispersion is None:
            raise Exception('No model wavelength information')

    if type(model) is list:
        outmodel = []
        for i in range(len(model)):
            model1 = model[i]
            omodel1 = prepare_cannon_model(model1, spec, dointerp=dointerp)
            outmodel.append(omodel1)
    else:

        # Convert wavelength from air->vacuum or vice versa
        if model.wavevac != spec.wavevac:
            # Air -> Vacuum
            if spec.wavevac is True:
                model.dispersion = astro.airtovac(model.dispersion)
                model.wavevac = True
            # Vacuum -> Air
            else:
                model.dispersion = astro.vactoair(model.dispersion)
                model.wavevac = False

        # Observed spectrum values
        wave = spec.wave
        ndim = wave.ndim
        if ndim == 2:
            npix, norder = wave.shape
        if ndim == 1:
            norder = 1
            npix = len(wave)
            wave = wave.reshape(npix, norder)
        # Loop over the orders
        outmodel = []
        for o in range(norder):
            w = wave[:, o]
            w0 = np.min(w)
            w1 = np.max(w)
            dw = dln.slope(w)
            dw = np.hstack((dw, dw[-1]))
            dw = np.abs(dw)
            meddw = np.median(dw)
            npix = len(w)

            if (np.min(model.dispersion) > w0) | (np.max(model.dispersion) <
                                                  w1):
                raise Exception(
                    'Model does not cover the observed wavelength range')

            # Trim
            nextend = int(np.ceil(len(w) * 0.25))  # extend 25% on each end
            nextend = np.maximum(nextend, 200)  # or 200 pixels
            if (np.min(model.dispersion) <
                (w0 - nextend * meddw)) | (np.max(model.dispersion) >
                                           (w1 + nextend * meddw)):
                tmodel = trim_cannon_model(model,
                                           w0=w0 - nextend * meddw,
                                           w1=w1 + nextend * meddw)
                #if (np.min(model.dispersion)<(w0-50*meddw)) | (np.max(model.dispersion)>(w1+50*meddw)):
                #    tmodel = trim_cannon_model(model,w0=w0-20*meddw,w1=w1+20*meddw)
                #if (np.min(model.dispersion)<(w0-1000.0*w0/cspeed)) | (np.max(model.dispersion)>(w1+1000.0*w1/cspeed)):
                #    # allow for up to 1000 km/s offset on each end
                #    tmodel = trim_cannon_model(model,w0=w0-1000.0*w0/cspeed,w1=w1+1000.0*w1/cspeed)
                tmodel.trim = True
            else:
                tmodel = cannon_copy(model)
                tmodel.trim = False

            # Rebin
            #  get LSF FWHM (A) for a handful of positions across the spectrum
            xp = np.arange(npix // 20) * 20
            fwhm = spec.lsf.fwhm(w[xp], xtype='Wave', order=o)
            # FWHM is in units of lsf.xtype, convert to wavelength/angstroms, if necessary
            if spec.lsf.xtype.lower().find('pix') > -1:
                dw1 = dln.interp(w, dw, w[xp], assume_sorted=False)
                fwhm *= dw1
            #  convert FWHM (A) in number of model pixels at those positions
            dwmod = dln.slope(tmodel.dispersion)
            dwmod = np.hstack((dwmod, dwmod[-1]))
            xpmod = interp1d(tmodel.dispersion,
                             np.arange(len(tmodel.dispersion)),
                             kind='cubic',
                             bounds_error=False,
                             fill_value=(np.nan, np.nan),
                             assume_sorted=False)(w[xp])
            xpmod = np.round(xpmod).astype(int)
            fwhmpix = fwhm / dwmod[xpmod]
            # need at least ~4 pixels per LSF FWHM across the spectrum
            #  using 3 affects the final profile shape
            nbin = np.round(np.min(fwhmpix) // 4).astype(int)
            if nbin == 0:
                import pdb
                nbin = 1
                print(spec.filename,
                      'Model has lower resolution than the observed spectrum',
                      fwhmpix.min())
                #raise Exception('Model has lower resolution than the observed spectrum',spec.filename,fwhmpix.min())
            if nbin > 1:
                rmodel = rebin_cannon_model(tmodel, nbin)
                rmodel.rebin = True
            else:
                rmodel = cannon_copy(tmodel)
                rmodel.rebin = False

            # Convolve
            lsf = spec.lsf.anyarray(rmodel.dispersion, xtype='Wave', order=o)

            #import pdb; pdb.set_trace()
            cmodel = convolve_cannon_model(rmodel, lsf)
            cmodel.convolve = True

            # Interpolate
            if dointerp is True:
                omodel = interp_cannon_model(cmodel, wout=spec.wave)
                omodel.interp = True
            else:
                omodel = cannon_copy(cmodel)
                omodel.interp = False

            # Order information
            omodel.norder = norder
            omodel.order = o

            #  # Copy continuum information
            #  if hasattr(model,'continuum'):
            #      omodel.continuum = cannon_copy(model.continuum)

            # Append to final output
            outmodel.append(omodel)

    # Single-element list
    if (type(outmodel) is list) & (len(outmodel) == 1):
        outmodel = outmodel[0]

    return outmodel
コード例 #2
0
ファイル: lsf.py プロジェクト: sdss/doppler
    def anyarray(self,x,xtype='pixels',order=0,original=True):
        """
        Return the LSF of the spectrum at specific locations or
        on a new wavelength array.

        Parameters
        ----------
        x : array
          The array of x-values for which to return the LSF.
        xtype : string, optional
          The type of x-values input, either 'wave' or 'pixels'.  The default
          is 'pixels'.
        order : int, optional
           The order for which to retrn the LSF array.  The default is 0.
        original : bool, optional
           If original=True, then the LSFs are returned on the original
           wavelength scale but at the centers given in "x".
           If the LSF is desired on a completely new wavelength scale
           (given by "x"), then orignal=False should be used instead.

        Returns
        -------
        lsf : array
           The 2D array of LSF values.

        Examples
        --------
        lsf = lsf.anyarray([100,200])

        """

        nx = len(x)
        # returns xsigma in units of self.xtype not necessarily xtype
        xsigma = self.sigma(x,xtype=xtype,order=order)
        
        # Get wavelength and pixel arrays
        if xtype.lower().find('pix') > -1:
            w = self.pix2wave(x,order=order)
        else:
            w = x.copy()
            x = self.wave2pix(w,order=order)
            
        # Convert sigma from wavelength to pixels, if necessary
        if self.xtype.lower().find('wave') > -1:
            wsigma = xsigma.copy()
            # On original wavelength scale
            if original is True:
                w1 = self.pix2wave(np.array(x)+1,order=order)
                dw = w1-w
            # New wavelength/pixel scale
            else:
                dw = dln.slope(w)
                dw = np.hstack((dw,dw[-1]))            
            xsigma = wsigma / dw

        # Figure out nLSF pixels needed, +/-3 sigma
        nlsf = np.int(np.round(np.max(xsigma)*6))
        if nlsf % 2 == 0: nlsf+=1                   # must be odd
        
        # Make LSF array
        lsf = np.zeros((nx,nlsf))
        xlsf = np.arange(nlsf)-nlsf//2
        xlsf2 = np.repeat(xlsf,nx).reshape((nlsf,nx)).T
        xsigma2 = np.repeat(xsigma,nlsf).reshape((nx,nlsf))
        lsf = np.exp(-0.5*xlsf2**2 / xsigma2**2) / (np.sqrt(2*np.pi)*xsigma2)
        lsf[lsf<0.] = 0.
        lsf /= np.tile(np.sum(lsf,axis=1),(nlsf,1)).T
        
        # should I use gaussbin????
        return lsf
コード例 #3
0
ファイル: lsf.py プロジェクト: sdss/doppler
    def array(self,order=None):
        """
        Return the full LSF for the spectrum.

        Parameters
        ----------
        order : int, optional
           The order for which to return the full LSF array, if there are multiple
           orders.  The default is None which means the LSF array for all orders is
           returned.

        Returns
        -------
        lsf : array
           The full LSF array for the spectrum (or just one order).

        Examples
        --------
        lsf = lsf.array()

        """
        
        # Return what we already have
        if self._array is not None:
            if (self.ndim==2) & (order is not None):
                # [Npix,Nlsf,Norder]
                return self._array[:,:,order]
            else:
                return self._array

        # Loop over orders and figure out how many Nlsf pixels we need
        #  must be same across all orders
        wave = self.wave.reshape(self.npix,self.norder)
        nlsfarr = np.zeros(self.norder,dtype=int)
        xsigma = np.zeros((self.npix,self.norder),dtype=np.float64)
        for o in range(self.norder):
            x = np.arange(self.npix)
            xsig = self.sigma(order=o)
            w = wave[:,o]
            
            # Convert sigma from wavelength to pixels, if necessary
            if self.xtype.lower().find('wave') > -1:
                wsig = xsig.copy()
                dw = dln.slope(w)
                dw = np.hstack((dw,dw[-1]))            
                xsig = wsig / dw
            xsigma[:,o] = xsig
                
            # Figure out nLSF pixels needed, +/-3 sigma
            nlsf = np.int(np.round(np.max(xsigma)*6))
            if nlsf % 2 == 0: nlsf+=1                   # must be odd
            nlsfarr[o] = nlsf
        nlsf = np.max(np.array(nlsfarr))

        # Make LSF array
        lsf = np.zeros((self.npix,nlsf,self.norder))
        # Loop over orders
        for o in range(self.norder):
            xlsf = np.arange(nlsf)-nlsf//2
            xlsf2 = np.repeat(xlsf,self.npix).reshape((nlsf,self.npix)).T
            lsf1 = np.zeros((self.npix,nlsf))
            xsig = xsigma[:,o]
            xsigma2 = np.repeat(xsig,nlsf).reshape((self.npix,nlsf))
            lsf1 = np.exp(-0.5*xlsf2**2 / xsigma2**2) / (np.sqrt(2*np.pi)*xsigma2)
            # should I use gaussbin????
            lsf1[lsf1<0.] = 0.
            lsf1 /= np.tile(np.sum(lsf1,axis=1),(nlsf,1)).T
            lsf[:,:,o] = lsf1
            
        # if only one order then reshape
        if self.ndim==1: lsf=lsf.reshape(self.npix,nlsf)
            
        self._array = lsf   # save for next time

        return lsf
コード例 #4
0
ファイル: utils.py プロジェクト: dnidever/doppler
def make_logwave_scale(wave, vel=1000.0):
    """
    Create a logarithmic wavelength scale for a given wavelength array.

    This is used by rv.fit() to create a logarithmic wavelength scale for
    cross-correlation.

    If the input wavelength array is 2D with multiple orders (trailing
    dimension), then the output wavelength array will extend beyond the
    boundaries of some of the orders.  These pixels will have to be
    "padded" with dummy/masked-out pixels.

    Parameters
    ----------
    wave : array
         Input wavelength array, 1D or 2D with multiple orders.
    vel : float, optional
         Maximum velocity shift to allow for.  Default is 1000.

    Returns
    -------
    fwave : array
         New logarithmic wavelength array.

    Example
    -------
    .. code-block:: python

         fwave = make_logwave_scale(wave)

    """

    # If the existing wavelength scale is logarithmic then use it, just extend
    # on either side
    vel = np.abs(vel)
    wave = np.float64(wave)
    nw = len(wave)
    wr = dln.minmax(wave)

    # Multi-order wavelength array
    if wave.ndim == 2:
        norder = wave.shape[1]

        # Ranges
        wr = np.empty((2, norder), np.float64)
        wlo = np.empty(norder, np.float64)
        whi = np.empty(norder, np.float64)
        for i in range(norder):
            wr[:, i] = dln.minmax(wave[:, i])
            wlo[i] = wr[0, i] - vel / cspeed * wr[0, i]
            whi[i] = wr[1, i] + vel / cspeed * wr[1, i]

        # For multi-order wavelengths, the final wavelength solution will extend
        # beyond the boundary of the original wavelength solution (at the end).
        # The extra pixels will have to be "padded" with masked out values.

        # We want the same logarithmic step for all order
        dw = np.log10(np.float64(wave[1:, :])) - np.log10(
            np.float64(wave[0:-1, :]))
        dwlog = np.median(np.abs(dw))  # positive

        # Extend at the ends
        if vel > 0:
            nlo = np.empty(norder, int)
            nhi = np.empty(norder, int)
            for i in range(norder):
                nlo[i] = np.int(
                    np.ceil((np.log10(np.float64(wr[0, i])) -
                             np.log10(np.float64(wlo[i]))) / dwlog))
                nhi[i] = np.int(
                    np.ceil((np.log10(np.float64(whi[i])) -
                             np.log10(np.float64(wr[1, i]))) / dwlog))
            # Use the maximum over all orders
            nlo = np.max(nlo)
            nhi = np.max(nhi)
        else:
            nlo = 0
            nhi = 0

        # Number of pixels for the input wavelength range
        n = np.empty(norder, int)
        for i in range(norder):
            if vel == 0:
                n[i] = np.int((np.log10(np.float64(wr[1, i])) -
                               np.log10(np.float64(wr[0, i]))) / dwlog)
            else:
                n[i] = np.int(
                    np.ceil((np.log10(np.float64(wr[1, i])) -
                             np.log10(np.float64(wr[0, i]))) / dwlog))
        # maximum over all orders
        n = np.max(n)

        # Final number of pixels
        nf = n + nlo + nhi

        # Final wavelength array
        fwave = np.empty((nf, norder), np.float64)
        for i in range(norder):
            fwave[:, i] = 10**((np.arange(nf) - nlo) * dwlog +
                               np.log10(np.float64(wr[0, i])))

        # Make sure the element that's associated with the first input wavelength is identical
        for i in range(norder):
            # Increasing input wavelength arrays
            if np.median(dw[:, i]) > 0:
                fwave[:, i] -= fwave[nlo, i] - wave[0, i]
            # Decreasing input wavelength arrays
            else:
                fwave[:, i] -= fwave[nlo, i] - wave[-1, i]

    # Single-order wavelength array
    else:
        # extend wavelength range by +/-vel km/s
        wlo = wr[0] - vel / cspeed * wr[0]
        whi = wr[1] + vel / cspeed * wr[1]

        # logarithmic step
        dwlog = np.median(dln.slope(np.log10(np.float64(wave))))

        # extend at the ends
        if vel > 0:
            nlo = np.int(
                np.ceil(
                    (np.log10(np.float64(wr[0])) - np.log10(np.float64(wlo))) /
                    dwlog))
            nhi = np.int(
                np.ceil(
                    (np.log10(np.float64(whi)) - np.log10(np.float64(wr[1]))) /
                    dwlog))
        else:
            nlo = 0
            nhi = 0

        # Number of pixels
        if vel == 0.0:
            n = np.int(
                (np.log10(np.float64(wr[1])) - np.log10(np.float64(wr[0]))) /
                dwlog)
        else:
            n = np.int(
                np.ceil(
                    (np.log10(np.float64(wr[1])) - np.log10(np.float64(wr[0])))
                    / dwlog))
        nf = n + nlo + nhi

        fwave = 10**((np.arange(nf) - nlo) * dwlog +
                     np.log10(np.float64(wr[0])))

        # Make sure the element that's associated with the first input wavelength is identical
        fwave -= fwave[nlo] - wave[0]

    # w=10**(w0log+i*dwlog)
    return fwave