示例#1
0
def ppxf_population(specerr, templates=None, velscale=None, start = None, 
                    goodpixels = None, plot=False, moments=4, degree=-1, vsyst=None, 
                    clean=False, mdegree=10, regul=None):
    '''
    Function that calls ppxf, designed to be called from multiprocessing (i.e. only 
    require 1 argument). Everything else is taken care of outside of this.
    '''
    
    galaxy = specerr[0]
    noise = specerr[1]
    
    # Only run ppxf if I have a non-nan's spectrum. Even one nan's breaks ppxf ... sigh.
    if not(np.any(np.isnan(galaxy))):

        pp = ppxf(templates, galaxy, noise, velscale, start,
                  goodpixels=goodpixels, plot=False, moments=moments, degree=degree,
                  vsyst=vsyst, clean=clean, mdegree=mdegree, regul=regul, quiet=True)
        
        # In a  perfect world, I would return pp. But that is a very massive variable.
        # 1 row = 300 spaxels = 5GB => 1 MUSE cube = 1.5TB !!!
        # Instead, just store what I need/trust. The best fit, the galaxy spectra (for 
        # sanity checks) and the sol array (with the kinematics information, that I 
        # sort-of trust.    
        return [pp.bestfit, pp.galaxy, pp.sol]
    
    else:
        return None
示例#2
0
def run_ppxf(wavelength_log, templates, galaxy, noise, velscale, start,
             goodpixels, dv):

    pp = ppxf.ppxf(templates,
                   galaxy,
                   noise,
                   velscale,
                   start,
                   bias=0,
                   moments=2,
                   goodpixels=goodpixels,
                   vsyst=dv,
                   quiet=True,
                   degree=-1,
                   mdegree=6,
                   oversample=20)

    rv = round(pp.sol[0], 1)
    sigma = round(pp.sol[1], 1)
    chi2 = pp.chi2
    error_rv = pp.error[0] * np.sqrt(chi2)
    error_sigma = pp.error[1] * np.sqrt(chi2)
    bestfit = pp.bestfit
    residuals = bestfit - galaxy

    zp1 = 1 + rv / c
    output_wavelengths = wavelength_log / zp1

    return rv, sigma, error_rv, error_sigma, chi2, bestfit, residuals, zp1, output_wavelengths
示例#3
0
    def mc_errors(self, nsim=200):
        """ Calculate the errors using MC simulations"""
        errs = np.zeros((nsim, len(self.error)))
        for i in range(nsim):
            y = self.bestfit + np.random.normal(0, self.noise, len(
                self.galaxy))

            noise = np.ones_like(self.galaxy) * self.noise
            sim = ppxf(self.bestfit_unbroad,
                       y,
                       noise,
                       velscale, [0, self.sol[1]],
                       goodpixels=self.goodpixels,
                       plot=False,
                       moments=4,
                       degree=-1,
                       mdegree=-1,
                       vsyst=self.vsyst,
                       lam=self.lam,
                       quiet=True,
                       bias=0.)
            errs[i] = sim.sol
        median = np.ma.median(errs, axis=0)
        error = 1.4826 * np.ma.median(np.ma.abs(errs - median), axis=0)
        # Here I am using always the maximum error between the simulated
        # and the values given by pPXF.
        self.error = np.maximum(error, self.error)
        return
示例#4
0
def run_ppxf(spectra, velscale):
    """ Run pPXF in a list of spectra"""
    print "Loading templates"
    templates, logLam2, delta = load_templates(velscale)
    for i, spec in enumerate(spectra):
        print "pPXF run of spectrum {0} ({1} of {2})".format(spec, i+1, 
              len(spectra))
        print "Preparing files..."
        # Read a galaxy spectrum and define the wavelength range
        hdu = pf.open(spec)
        spec_lin = hdu[0].data
        h1 = hdu[0].header
        lamRange1 = h1['CRVAL1'] + np.array([0.,h1['CDELT1']*(h1['NAXIS1']-1)])
        # Convolve our spectra to match MILES resolution
        FWHM_dif = np.sqrt(FWHM_tem**2 - FWHM_spec**2)
        sigma = FWHM_dif/2.355/delta # Sigma difference in pixels
        spec_lin = ndimage.gaussian_filter1d(spec_lin,sigma)
        # Rebin to logarithm scale
        galaxy, logLam1, velscale = util.log_rebin(lamRange1, spec_lin, 
                                                   velscale=velscale)
        noise = 0. * galaxy + 0.1
        dv = (logLam2[0]-logLam1[0])*c 
        start, goodPixels = read_setup_file(spec, logLam1)
        print "First interaction of pPXF.."
        pp0 = ppxf(templates, galaxy, noise, velscale, start,
                  goodpixels=goodPixels, plot=False, moments=4,
                  degree=6, mdegree=4, vsyst=dv)
        print "Calculating realistic noise for input..."
        rms0 = galaxy[goodPixels] - pp0.bestfit[goodPixels]
        noise0 = 1.4826 * np.median(np.abs(rms0 - np.median(rms0)))
        noise0 = 0. * galaxy + noise0
        print "Second run of pPXF..."
        pp = ppxf(templates, galaxy, noise0, velscale, pp0.sol,
                  goodpixels=goodPixels, plot=False, moments=4,
                  degree=6, mdegree=4, vsyst=dv, lam=lamRange1)
        print "Finished! Now saving results..."
        with open(spec.replace(".fits", ".pkl"), "w") as f:
            pickle.dump(pp, f)
    return
示例#5
0
def run_ppxf(wavelength_log, templates, galaxy, noise, velscale, start, goodpixels, dv):

     pp = ppxf.ppxf(templates, galaxy, noise, velscale, start, bias = 0, moments=2, goodpixels = goodpixels, vsyst=dv, quiet=True, degree = -1, mdegree = 6, oversample = 20)
                                                                  
     rv    =  round(pp.sol[0],1)
     sigma =  round(pp.sol[1],1)
     chi2  =  pp.chi2
     error_rv =  pp.error[0]*np.sqrt(chi2)
     error_sigma =  pp.error[1]*np.sqrt(chi2)
     bestfit = pp.bestfit
     residuals =  bestfit - galaxy

     zp1 = 1 + rv/c
     output_wavelengths = wavelength_log / zp1  

     return rv, sigma, error_rv, error_sigma, chi2, bestfit, residuals, zp1, output_wavelengths
示例#6
0
 def mc_errors(self, nsim=10):
     """ Calculate the errors using MC simulations"""
     errs = np.zeros((nsim, len(self.error)))
     templates, logLam2, delta = load_templates(velscale)
     for i in range(nsim):
         y = self.galaxy + np.random.normal(0, self.noise,
                                             len(self.bestfit))
         noise = np.ones_like(self.galaxy) * self.noise
         sim = ppxf(templates, y, noise, velscale, self.sol,
                    goodpixels=self.goodpixels, plot=False, moments=4,
                    degree=self.degree, mdegree=self.mdegree,
                    vsyst=self.vsyst, lam=self.lam, quiet=True)
         errs[i] = sim.sol
     self.mc_err = np.ma.std(errs, axis=0)
     self.erroor = np.maximum(self.error, self.mc_err)
     return
示例#7
0
def ppxf_population(specerr,
                    templates=None,
                    velscale=None,
                    start=None,
                    goodpixels=None,
                    plot=False,
                    moments=4,
                    degree=-1,
                    vsyst=None,
                    clean=False,
                    mdegree=10,
                    regul=None):
    '''
    Function that calls ppxf, designed to be called from multiprocessing (i.e. only 
    require 1 argument). Everything else is taken care of outside of this.
    '''

    galaxy = specerr[0]
    noise = specerr[1]

    # Only run ppxf if I have a non-nan's spectrum. Even one nan's breaks ppxf ... sigh.
    if not (np.any(np.isnan(galaxy))):

        pp = ppxf(templates,
                  galaxy,
                  noise,
                  velscale,
                  start,
                  goodpixels=goodpixels,
                  plot=False,
                  moments=moments,
                  degree=degree,
                  vsyst=vsyst,
                  clean=clean,
                  mdegree=mdegree,
                  regul=regul,
                  quiet=True)

        # In a  perfect world, I would return pp. But that is a very massive variable.
        # 1 row = 300 spaxels = 5GB => 1 MUSE cube = 1.5TB !!!
        # Instead, just store what I need/trust. The best fit, the galaxy spectra (for
        # sanity checks) and the sol array (with the kinematics information, that I
        # sort-of trust.
        return [pp.bestfit, pp.galaxy, pp.sol]

    else:
        return None
示例#8
0
def call_ppxf(datum,
              templates,
              vel_scale,
              delta_v,
              good_pixels,
              quiet,
              moments=2,
              degree=7,
              interp_func='nearest'):

    if moments == 2:
        priors = (datum.rv_prior, datum.sigma_prior)
    elif moments == 4:
        priors = (datum.rv_prior, datum.sigma_prior, datum.h3_prior,
                  datum.h4_prior)

    output = ppxf.ppxf(templates,
                       datum.log_fluxes,
                       datum.log_sigmas,
                       vel_scale,
                       moments=moments,
                       mdegree=degree,
                       degree=-1,
                       clean=True,
                       vsyst=delta_v,
                       start=priors,
                       quiet=quiet,
                       goodpixels=good_pixels)

    datum.rv = output.sol[0]
    datum.sigma = output.sol[1]
    if moments == 4:
        datum.h3 = output.sol[2]
        datum.h4 = output.sol[3]

    datum.log_fit_fluxes = output.bestfit
    datum.fit_wavelengths, datum.fit_fluxes = interp.logtolinear(
        datum.log_wavelengths,
        datum.log_fit_fluxes,
        function=interp_func,
        ratio=True)
    datum.rest_wavelengths = datum.fit_wavelengths / (
        1 + datum.rv * 1e3 / constants.c)

    return datum
示例#9
0
def check_ppxf(spec, velscale):
    """ Checking if velocity os star is zero"""
    templates, logLam2, delta, miles= stellar_templates(velscale)
    FWHM_dif = np.sqrt(FWHM_tem**2 - FWHM_spec**2)
    sigma = FWHM_dif/2.355/delta # Sigma difference in pixels
    star = pf.getdata(spec)
    star1 = ndimage.gaussian_filter1d(star, sigma)
    w = wavelength_array(spec)
    lamRange= np.array([w[0], w[-1]])
    galaxy, logLam1, velscale = util.log_rebin(lamRange, star1, \
                                               velscale=velscale)
    sn = snr(star1)
    noise = np.ones_like(galaxy) / sn
    dv = (logLam2[0]-logLam1[0])*c
    pp = ppxf(templates, galaxy, noise, velscale, [0,50],
        plot=True, moments=2, degree=5, mdegree=-1, vsyst=dv)
    plt.show()
    return
示例#10
0
    def mc_errors(self, nsim=200):
        """ Calculate the errors using MC simulations"""
        errs = np.zeros((nsim, len(self.error)))
        for i in range(nsim):
            y = self.bestfit + np.random.normal(0, self.noise,
                                                len(self.galaxy))

            noise = np.ones_like(self.galaxy) * self.noise
            sim = ppxf(self.bestfit_unbroad, y, noise, velscale,
                       [0, self.sol[1]],
                       goodpixels=self.goodpixels, plot=False, moments=4,
                       degree=-1, mdegree=-1,
                       vsyst=self.vsyst, lam=self.lam, quiet=True, bias=0.)
            errs[i] = sim.sol
        median = np.ma.median(errs, axis=0)
        error = 1.4826 * np.ma.median(np.ma.abs(errs - median), axis=0)
        # Here I am using always the maximum error between the simulated
        # and the values given by pPXF.
        self.error = np.maximum(error, self.error)
        return
示例#11
0
def ppxf_gas_kinematics_parallel_loop(bin_sci_j, ppxf_bestfit_j, gal_hdr, templates, velscale, sigma, lamRange1,
                                      logLam2, start, plot, moments, regul_err, reg_dim, component, bias, quiet):
    gal_data = fits.getdata(bin_sci_j, 0)
    gal_data_new = ndimage.gaussian_filter1d(gal_data, sigma)

    galaxy, logLam1, velscale = util.log_rebin(lamRange1, gal_data_new, velscale=velscale)
    noise = galaxy * 0 + 1  # Assume constant noise per pixel here

    # The galaxy and the template spectra do not have the same starting wavelength.
    # For this reason an extra velocity shift DV has to be applied to the template
    # to fit the galaxy spectrum. We remove this artificial shift by using the
    # keyword VSYST in the call to PPXF below, so that all velocities are
    # measured with respect to DV. This assume the redshift is negligible.
    # In the case of a high-redshift galaxy one should de-redshift its
    # wavelength to the rest frame before using the line below as described
    # in PPXF_KINEMATICS_EXAMPLE_SAURON.
    #
    c = 299792.458
    dv = (logLam2[0] - logLam1[0]) * c  # km/s

    t = clock()

    pp = ppxf(templates, galaxy, noise, velscale, start, plot=plot, moments=moments, degree=-1, mdegree=10,
              vsyst=dv, clean=False, regul=1. / regul_err, reg_dim=reg_dim, component=component, bias=bias,
              quiet=quiet)

    print ('pPXF sol of bin {}: {}'.format(j, pp.sol))
    print ('pPXF error of bin {}: {}'.format(j, pp.error))

    hdu_best = fits.PrimaryHDU()
    hdu_best.data = pp.bestfit
    hdu_best.header['CD1_1'] = gal_hdr['CD1_1']
    hdu_best.header['CDELT1'] = gal_hdr['CD1_1']
    hdu_best.header['CRPIX1'] = gal_hdr['CRPIX1']
    hdu_best.header['CRVAL1'] = gal_hdr['CRVAL1']
    hdu_best.header['NAXIS1'] = pp.bestfit.size
    hdu_best.header['CTYPE1'] = 'LINEAR'  # corresponds to sampling of values above
    hdu_best.header['DC-FLAG'] = '1'  # 0 = linear, 1= log-linear sampling
    hdu_best.writeto(ppxf_bestfit_j, clobber=True)

    return pp
示例#12
0
def ppxf_simulation_example():

    dir = 'spectra/'
    file = dir + 'Rbi1.30z+0.00t12.59.fits'
    hdu = pyfits.open(file)
    ssp = hdu[0].data
    h = hdu[0].header

    lamRange = h['CRVAL1'] + np.array([0., h['CDELT1'] * (h['NAXIS1'] - 1)])
    star, logLam, velscale = util.log_rebin(lamRange, ssp)

    # The finite sampling of the observed spectrum is modeled in detail:
    # the galaxy spectrum is obtained by oversampling the actual observed spectrum
    # to a high resolution. This represent the true spectrum, which is later resampled
    # to lower resolution to simulate the observations on the CCD. Similarly, the
    # convolution with a well-sampled LOSVD is done on the high-resolution spectrum,
    # and later resampled to the observed resolution before fitting with PPXF.

    factor = 10  # Oversampling integer factor for an accurate convolution
    starNew = ndimage.interpolation.zoom(
        star, factor,
        order=1)  # This is the underlying spectrum, known at high resolution
    star = rebin(
        starNew, factor
    )  # Make sure that the observed spectrum is the integral over the pixels

    vel = 0.3  # velocity in *pixels* [=V(km/s)/velScale]
    h3 = 0.1  # Adopted G-H parameters of the LOSVD
    h4 = -0.1
    sn = 60.  # Adopted S/N of the Monte Carlo simulation
    m = 300  # Number of realizations of the simulation
    sigmaV = np.linspace(
        0.8, 4, m)  # Range of sigma in *pixels* [=sigma(km/s)/velScale]

    result = np.zeros((m, 4))  # This will store the results
    t = clock()
    np.random.seed(123)  # for reproducible results

    for j in range(m):

        sigma = sigmaV[j]
        dx = int(
            abs(vel) +
            4.0 * sigma)  # Sample the Gaussian and GH at least to vel+4*sigma
        x = np.linspace(
            -dx, dx, 2 * dx * factor +
            1)  # Evaluate the Gaussian using steps of 1/factor pixels.
        w = (x - vel) / sigma
        w2 = w**2
        gauss = np.exp(-0.5 * w2) / (np.sqrt(2. * np.pi) * sigma * factor
                                     )  # Normalized total(gauss)=1
        h3poly = w * (2. * w2 - 3.) / np.sqrt(3.)  # H3(y)
        h4poly = (w2 * (4. * w2 - 12.) + 3.) / np.sqrt(24.)  # H4(y)
        losvd = gauss * (1. + h3 * h3poly + h4 * h4poly)

        galaxy = signal.fftconvolve(
            starNew, losvd, mode="same")  # Convolve the oversampled spectrum
        galaxy = rebin(
            galaxy, factor)  # Integrate spectrum into original spectral pixels
        noise = galaxy / sn  # 1sigma error spectrum
        galaxy = np.random.normal(galaxy,
                                  noise)  # Add noise to the galaxy spectrum
        start = np.array([
            vel + np.random.random(), sigma * np.random.uniform(0.85, 1.15)
        ]) * velscale  # Convert to km/s

        pp = ppxf(star,
                  galaxy,
                  noise,
                  velscale,
                  start,
                  goodpixels=np.arange(dx, galaxy.size - dx),
                  plot=False,
                  moments=4,
                  bias=0.5)
        result[j, :] = pp.sol

    print('Calculation time: %.2f s' % (clock() - t))

    plt.clf()
    plt.subplot(221)
    plt.plot(sigmaV * velscale, result[:, 0] - vel * velscale, '+k')
    plt.plot(sigmaV * velscale, sigmaV * velscale * 0, '-r')
    plt.ylim(-40, 40)
    plt.xlabel('$\sigma_{in}\ (km\ s^{-1})$')
    plt.ylabel('$V - V_{in}\ (km\ s^{-1}$)')

    plt.subplot(222)
    plt.plot(sigmaV * velscale, result[:, 1] - sigmaV * velscale, '+k')
    plt.plot(sigmaV * velscale, sigmaV * velscale * 0, '-r')
    plt.ylim(-40, 40)
    plt.xlabel('$\sigma_{in}\ (km\ s^{-1})$')
    plt.ylabel('$\sigma - \sigma_{in}\ (km\ s^{-1}$)')

    plt.subplot(223)
    plt.plot(sigmaV * velscale, result[:, 2], '+k')
    plt.plot(sigmaV * velscale, sigmaV * velscale * 0 + h3, '-r')
    plt.ylim(-0.2 + h3, 0.2 + h3)
    plt.xlabel('$\sigma_{in}\ (km\ s^{-1})$')
    plt.ylabel('$h_3$')

    plt.subplot(224)
    plt.plot(sigmaV * velscale, result[:, 3], '+k')
    plt.plot(sigmaV * velscale, sigmaV * velscale * 0 + h4, '-r')
    plt.ylim(-0.2 + h4, 0.2 + h4)
    plt.xlabel('$\sigma_{in}\ (km\ s^{-1})$')
    plt.ylabel('$h_4$')

    plt.tight_layout()
    plt.pause(0.01)
示例#13
0
def ppxf_kinematics(bin_sci, ppxf_file, ppxf_bestfit, template_fits, template_resolution, lam_range=[4173., 5404.],
                    vel_init=1500., sig_init=100., bias=0., plot=False, quiet=True, badPixels=[], clean=False):
    """
    Follow the pPXF usage example by Michile Cappellari
    INPUT: DIR_SCI_COMB (comb_fits_sci_{S/N}/bin_sci_{S/N}.fits), TEMPLATE_* (spectra/Mun1.30z*.fits)
    OUTPUT: PPXF_FILE (ppxf_output_sn30.txt), OUT_BESTFIT_FITS (ppxf_fits/ppxf_bestfit_sn30.fits)
    """

    if os.path.exists(ppxf_file):
        print('File {} already exists'.format(ppxf_file))
        return

    # Read a galaxy spectrum and define the wavelength range
    assert len(glob.glob(bin_sci.format('*'))) > 0, 'Binned spectra {} not found'.format(glob.glob(bin_sci.format('*')))

    # hdu = fits.open(in_file[0])
    # gal_lin = hdu[0].data
    # h1 = hdu[0].header

    with fits.open(bin_sci.format(0)) as gal_hdu:
        gal_lin = gal_hdu[0].data
        gal_hdr = gal_hdu[0].header

    # lamRange1 = h1['CRVAL1'] + np.array([0.,h1['CDELT1']*(h1['NAXIS1']-1)])
    # FWHM_gal = 4.2 # SAURON has an instrumental resolution FWHM of 4.2A.

    # lamRange1 is now variable lam_range (because I was lazy and didnt put headers into the binned spectra)
    FWHM_gal = 2.3  # GMOS IFU has an instrumental resolution FWHM of 2.3 A

    # If the galaxy is at a significant redshift (z > 0.03), one would need to apply a large velocity shift in PPXF to
    # match the template to the galaxy spectrum.This would require a large initial value for the velocity (V > 1e4 km/s)
    # in the input parameter START = [V,sig]. This can cause PPXF to stop! The solution consists of bringing the galaxy
    # spectrum roughly to the rest-frame wavelength, before calling PPXF. In practice there is no need to modify the
    # spectrum before the usual LOG_REBIN, given that a red shift corresponds to a linear shift of the log-rebinned
    # spectrum. One just needs to compute the wavelength range in the rest-frame and adjust the instrumental resolution
    # of the galaxy observations. This is done with the following three commented lines:
    #
    # z = 1.23 # Initial estimate of the galaxy redshift
    # lamRange1 = lamRange1/(1+z) # Compute approximate restframe wavelength range
    # FWHM_gal = FWHM_gal/(1+z)   # Adjust resolution in Angstrom

    # galaxy, logLam1, velscale = util.log_rebin(lamRange1, gal_lin)
    # galaxy = galaxy/np.median(galaxy) # Normalize spectrum to avoid numerical issues
    # noise = galaxy*0 + 0.0049           # Assume constant noise per pixel here

    galaxy, logLam1, velscale = util.log_rebin(lam_range, gal_lin)
    galaxy = galaxy / np.median(galaxy)  # Normalize spectrum to avoid numerical issues

    # Read the list of filenames from the Single Stellar Population library by Vazdekis (1999, ApJ, 513, 224). A subset
    # of the library is included for this example with permission. See http://purl.org/cappellari/software
    # for suggestions of more up-to-date stellar libraries.

    # vazdekis = glob.glob(dir + 'Rbi1.30z*.fits')
    # vazdekis.sort()
    # FWHM_tem = 1.8 # Vazdekis spectra have a resolution FWHM of 1.8A.

    template_spectra = glob.glob(template_fits)
    assert len(template_spectra) > 0, 'Template spectra not found: {}'.format(template_fits)
    FWHM_tem = template_resolution

    # Extract the wavelength range and logarithmically rebin one spectrum to the same velocity scale of the SAURON
    # galaxy spectrum, to determine the size needed for the array which will contain the template spectra.

    # hdu = fits.open(vazdekis[0])
    # ssp = hdu[0].data
    # h2 = hdu[0].header
    # lamRange2 = h2['CRVAL1'] + np.array([0.,h2['CDELT1']*(h2['NAXIS1']-1)])
    # sspNew, logLam2, velscale = util.log_rebin(lamRange2, ssp, velscale=velscale)
    # templates = np.empty((sspNew.size,len(vazdekis)))

    with fits.open(template_spectra[0]) as temp_hdu:
        ssp = temp_hdu[0].data
        h2 = temp_hdu[0].header
    lamRange2 = h2['CRVAL1'] + np.array([0., h2['CDELT1'] * (h2['NAXIS1'] - 1)])
    sspNew, logLam2, velscale = util.log_rebin(lamRange2, ssp, velscale=velscale)
    templates = np.empty((sspNew.size, len(template_spectra)))

    # Convolve the whole Vazdekis library of spectral templates with the quadratic difference between the SAURON and the
    # Vazdekis instrumental resolution. Logarithmically rebin and store each template as a column in the array TEMPLATES

    # Quadratic sigma difference in pixels Vazdekis --> SAURON
    # The formula below is rigorously valid if the shapes of the instrumental spectral profiles are well approximated
    # by Gaussians.

    # FROM GWENS IDL SCRIPT ----------------------------------------------------------
    # Example: BC03 spectra have a resolution of 3A at 5100A, this corresponds to sigma ~ 3.0/5100*3e5/2.35 = 75 km/s.
    # The GMOS IFU has an instrumental resolution of 2.3 A, this corresponds to sigma ~ 2.3/5100*3e5/2.35 = 56.8 km/s.
    # The quadratic difference is sigma = sqrt(56.8^2 - 75^2) = undefined
    # (the above reasoning can be applied if the shape of the instrumental spectral profiles can be well approximated
    # by a Gaussian).
    # For the lower resolution models, we must degrade the DATA to fit the models.
    # Thus: The quadratic difference is sigma = sqrt(75^2 - 56.8^2) = 49.0 km/s
    # ---------------------------------------------------------------------------------

    # FWHM_dif = np.sqrt(FWHM_gal ** 2 - template_resolution ** 2)
    FWHM_dif = np.sqrt(FWHM_tem ** 2 - FWHM_gal ** 2)
    sigma = FWHM_dif / 2.355 / h2['CDELT1']  # SIGMA DIFFERENCE IN PIXELS, 1.078435697220085

    # Logarithmically rebin the whole Mun library of spectra, and store each template as a column in the array TEMPLATES

    # for j in range(len(vazdekis)):
    # hdu = fits.open(vazdekis[j])
    # ssp = hdu[0].data
    # ssp = ndimage.gaussian_filter1d(ssp, sigma)
    # sspNew, logLam2, velscale = util.log_rebin(lamRange2, ssp, velscale=velscale)
    # templates[:, j] = sspNew / np.median(sspNew)  # Normalizes templates

    for j in range(len(template_spectra)):
        with fits.open(template_spectra[j]) as temp_hdu_j:
            ssp_j = temp_hdu_j[0].data
        ssp_j = ndimage.gaussian_filter1d(ssp_j, sigma)
        sspNew, logLam2, velscale = util.log_rebin(lamRange2, ssp_j, velscale=velscale)

        templates[:, j] = sspNew / np.median(sspNew)  # Normalizes templates

    # The galaxy and the template spectra do not have the same starting wavelength. For this reason an extra velocity
    # shift DV has to be applied to the template to fit the galaxy spectrum. We remove this artificial shift by using
    # the keyword VSYST in the call to PPXF below, so that all velocities are measured with respect to DV. This assume
    # the redshift is negligible.In the case of a high-redshift galaxy one should de-redshift its wavelength to the
    # rest frame before using the line below (see above).

    vel_list = []
    sig_list = []
    dV_list = []
    dsigma_list = []

    h3_list = []
    h4_list = []
    dh3_list = []
    dh4_list = []

    for j in range(len(glob.glob(bin_sci.format('*')))):
        b_gal = fits.getdata(bin_sci.format(j), 0)

        b_gal = ndimage.gaussian_filter1d(b_gal, sigma)

        galaxy, logLam1, velscale = util.log_rebin(lam_range, b_gal, velscale=velscale)
        noise = galaxy * 0 + 1  # Assume constant noise per pixel here

        c = 299792.458
        dv = (logLam2[0] - logLam1[0]) * c  # km/s

        # vel = 1500.  # Initial estimate of the galaxy velocity in km/s
        z = np.exp(vel_init / c) - 1  # Relation between velocity and redshift in pPXF

        goodPixels = util.determine_goodpixels(logLam1, lamRange2, z)
        if len(badPixels) > 0:
            indices = []
            for idx, pix in enumerate(goodPixels):
                if pix in badPixels:
                    indices.append(idx)
            goodPixels = np.delete(goodPixels, indices)

        # Here the actual fit starts. The best fit is plotted on the screen.
        # Gas emission lines are excluded from the pPXF fit using the GOODPIXELS keyword.

        start = [vel_init, sig_init]  # (km/s), starting guess for [V,sigma]
        t = clock()

        pp = ppxf(templates, galaxy, noise, velscale, start, goodpixels=goodPixels, plot=plot, moments=4,
                  degree=4, vsyst=dv, bias=bias, quiet=False, clean=clean)

        if not quiet:
            print("Formal errors:")
            print("     dV    dsigma   dh3      dh4")
            print("".join("%8.2g" % f for f in pp.error * np.sqrt(pp.chi2)))

        # If the galaxy is at significant redshift z and the wavelength has been de-redshifted with the three lines
        # "z = 1.23..." near the beginning of this procedure, the best-fitting redshift is now given by the following
        # commented line (equation 2 of Cappellari et al. 2009, ApJ, 704, L34;
        # http://adsabs.harvard.edu/abs/2009ApJ...704L..34C)
        # print, 'Best-fitting redshift z:', (z + 1)*(1 + sol[0]/c) - 1

        # Gwen obtains the velocity and sigma information from the SOL parameter
        # moments = 4 so sol = [vel, sig, h3, h4]
        vel_list.append(pp.sol[0])
        sig_list.append(pp.sol[1])
        dV_list.append((pp.error * np.sqrt(pp.chi2))[0])
        dsigma_list.append((pp.error * np.sqrt(pp.chi2))[1])

        h3_list.append(pp.sol[2])
        h4_list.append(pp.sol[3])
        dh3_list.append((pp.error * np.sqrt(pp.chi2))[2])
        dh4_list.append((pp.error * np.sqrt(pp.chi2))[3])

        hdu_best = fits.PrimaryHDU()
        hdu_best.data = pp.bestfit
        hdu_best.header['CD1_1'] = gal_hdr['CD1_1']
        hdu_best.header['CDELT1'] = gal_hdr['CD1_1']
        hdu_best.header['CRPIX1'] = gal_hdr['CRPIX1']
        hdu_best.header['CRVAL1'] = gal_hdr['CRVAL1']
        hdu_best.header['NAXIS1'] = pp.bestfit.size
        hdu_best.header['CTYPE1'] = 'LINEAR'  # corresponds to sampling of values above
        hdu_best.header['DC-FLAG'] = '1'  # 0 = linear, 1= log-linear sampling
        hdu_best.writeto(ppxf_bestfit.format(j), clobber=True)

    print('Elapsed time in PPXF: %.2f s' % (clock() - t))

    np.savetxt(ppxf_file,
               np.column_stack([vel_list, sig_list, h3_list, h4_list, dV_list, dsigma_list, dh3_list, dh4_list]),
               fmt=b'%10.6f  %10.6f  %10.6f  %10.6f  %10.6f  %10.6f  %10.6f  %10.6f',
               header='velocity    sigma       h3           h4         dV          dsigma       dh3         dh4')

    return vel_list
def ppxf_population_gas_example_sdss():

    # Read SDSS DR8 galaxy spectrum taken from here http://www.sdss3.org/dr8/
    # The spectrum is *already* log rebinned by the SDSS DR8
    # pipeline and log_rebin should not be used in this case.
    #
    file = 'spectra/NGC3522_SDSS_DR8.fits'
    hdu = pyfits.open(file)
    t = hdu[1].data
    z = float(hdu[1].header["Z"]) # SDSS redshift estimate

    # Only use the wavelength range in common between galaxy and stellar library.
    #
    mask = (t['wavelength'] > 3540) & (t['wavelength'] < 7409)
    flux = t['flux'][mask]
    galaxy = flux/np.median(flux)   # Normalize spectrum to avoid numerical issues
    wave = t['wavelength'][mask]

    # The noise level is chosen to give Chi^2/DOF=1 without regularization (REGUL=0).
    # A constant error is not a bad approximation in the fitted wavelength
    # range and reduces the noise in the fit.
    #
    noise = galaxy*0 + 0.01528           # Assume constant noise per pixel here

    # The velocity step was already chosen by the SDSS pipeline
    # and we convert it below to km/s
    #
    c = 299792.458 # speed of light in km/s
    velscale = np.log(wave[1]/wave[0])*c
    FWHM_gal = 2.76 # SDSS has an instrumental resolution FWHM of 2.76A.

    #------------------- Setup templates -----------------------

    stars_templates, lamRange_temp, logLam_temp = \
        setup_spectral_library(velscale, FWHM_gal)

    # The stellar templates are reshaped into a 2-dim array with each spectrum
    # as a column, however we save the original array dimensions, which are
    # needed to specify the regularization dimensions
    #
    reg_dim = stars_templates.shape[1:]
    stars_templates = stars_templates.reshape(stars_templates.shape[0], -1)

    # See the pPXF documentation for the keyword REGUL,
    # for an explanation of the following two lines
    #
    stars_templates /= np.median(stars_templates) # Normalizes stellar templates by a scalar
    regul_err = 0.004 # Desired regularization error

    # Construct a set of Gaussian emission line templates.
    # Estimate the wavelength fitted range in the rest frame.
    #
    lamRange_gal = np.array([np.min(wave), np.max(wave)])/(1 + z)
    gas_templates, line_names, line_wave = \
        util.emission_lines(logLam_temp, lamRange_gal, FWHM_gal)

    # Combines the stellar and gaseous templates into a single array
    # during the PPXF fit they will be assigned a different kinematic
    # COMPONENT value
    #
    templates = np.hstack([stars_templates, gas_templates])

    #-----------------------------------------------------------

    # The galaxy and the template spectra do not have the same starting wavelength.
    # For this reason an extra velocity shift DV has to be applied to the template
    # to fit the galaxy spectrum. We remove this artificial shift by using the
    # keyword VSYST in the call to PPXF below, so that all velocities are
    # measured with respect to DV. This assume the redshift is negligible.
    # In the case of a high-redshift galaxy one should de-redshift its
    # wavelength to the rest frame before using the line below as described
    # in PPXF_KINEMATICS_EXAMPLE_SAURON.
    #
    c = 299792.458
    dv = (np.log(lamRange_temp[0])-np.log(wave[0]))*c # km/s
    vel = c*z # Initial estimate of the galaxy velocity in km/s

    # Here the actual fit starts. The best fit is plotted on the screen.
    #
    # IMPORTANT: Ideally one would like not to use any polynomial in the fit
    # as the continuum shape contains important information on the population.
    # Unfortunately this is often not feasible, due to small calibration
    # uncertainties in the spectral shape. To avoid affecting the line strength of
    # the spectral features, we exclude additive polynomials (DEGREE=-1) and only use
    # multiplicative ones (MDEGREE=10). This is only recommended for population, not
    # for kinematic extraction, where additive polynomials are always recommended.
    #
    start = [vel, 180.] # (km/s), starting guess for [V,sigma]

    t = clock()

    # Assign component=0 to the stellar templates and
    # component=1 to the gas emission lines templates.
    # One can easily assign different kinematic components to different gas species
    # e.g. component=1 for the Balmer series, component=2 for the [OIII] doublet, ...)
    # Input a negative MOMENTS value to keep fixed the LOSVD of a component.
    #
    nTemps = stars_templates.shape[1]
    nLines = gas_templates.shape[1]
    component = [0]*nTemps + [1]*nLines
    moments = [4, 2] # fit (V,sig,h3,h4) for the stars and (V,sig) for the gas
    start = [start, start] # adopt the same starting value for both gas and stars

    pp = ppxf(templates, galaxy, noise, velscale, start,
              plot=False, moments=moments, degree=-1, mdegree=10,
              vsyst=dv, clean=False, regul=1./regul_err,
              reg_dim=reg_dim, component=component)

    # Plot fit results for stars and gas

    plt.clf()
    plt.subplot(211)
    plt.plot(wave, pp.galaxy, 'k')
    plt.plot(wave, pp.bestfit, 'b', linewidth=2)
    plt.xlabel("Observed Wavelength ($\AA$)")
    plt.ylabel("Relative Flux")
    plt.ylim([-0.1,1.3])
    plt.xlim([np.min(wave), np.max(wave)])
    plt.plot(wave, pp.galaxy-pp.bestfit, 'd', ms=4, color='LimeGreen', mec='LimeGreen') # fit residuals
    plt.axhline(y=-0, linestyle='--', color='k', linewidth=2)
    stars = pp.matrix[:,:nTemps].dot(pp.weights[:nTemps])
    plt.plot(wave, stars, 'r', linewidth=2) # overplot stellar templates alone
    gas = pp.matrix[:,-nLines:].dot(pp.weights[-nLines:])
    plt.plot(wave, gas+0.15, 'b', linewidth=2) # overplot emission lines alone

    # When the two Delta Chi^2 below are the same, the solution is the smoothest
    # consistent with the observed spectrum.
    #
    print('Desired Delta Chi^2: %.4g' % np.sqrt(2*galaxy.size))
    print('Current Delta Chi^2: %.4g' % ((pp.chi2 - 1)*galaxy.size))
    print('Elapsed time in PPXF: %.2f s' % (clock() - t))

    w = np.where(np.array(component) == 1)[0] # Extract weights of gas emissions
    print('++++++++++++++++++++++++++++++')
    print('Gas V=%.4g and sigma=%.2g km/s' % (pp.sol[1][0], pp.sol[1][1]))
    print('Emission lines peak intensity:')
    for name, weight, line in zip(line_names, pp.weights[w], pp.matrix[:,w].T):
        print('%12s: %.3g' % (name, weight*np.max(line)))
    print('------------------------------')

    # Plot stellar population mass distribution

    plt.subplot(212)
    weights = pp.weights[:np.prod(reg_dim)].reshape(reg_dim)/pp.weights.sum()
    plt.imshow(np.rot90(weights), interpolation='nearest', 
               cmap='gist_heat', aspect='auto', origin='upper', 
               extent=(np.log10(1.0), np.log10(17.7828), -1.9, 0.45))
    plt.colorbar()
    plt.title("Mass Fraction")
    plt.xlabel("log$_{10}$ Age (Gyr)")
    plt.ylabel("[M/H]")
    plt.tight_layout()
    plt.show()
示例#15
0
def ppxf_nifs_kinematics():

    file_dir = path.dirname(path.realpath(__file__))  # path of this procedure

    # Read a galaxy spectrum and define the wavelength range
    #
    file = file_dir + '/spectra/pgc12557_combined.fits'  # '/spectra/NGC4550_SAURON.fits'  # my current file location
    hdu = fits.open(file)
    gal_lin = hdu[1].data  # gal_lin = hdu[0].data
    # h1 = hdu[0].header
    h1 = hdu[
        1].header  # I need to use 1st extension header (0=general, 1=science, 2=variance, 3=data quality flags)
    '''
    print(h1)
    
    BITPIX  =                  -32 / array data type                                
    NAXIS   =                    3 / number of array dimensions                     
    NAXIS1  =                   71                                                  
    NAXIS2  =                   69                                                  
    NAXIS3  =                 2040                                 
    PCOUNT  =                    0 / number of parameters                           
    GCOUNT  =                    1 / number of groups                               
    EXTNAME = 'SCI     '           / Extension name                                 
    EXTVER  =                    1 / Extension version                              
    INHERIT =                    F / Inherits global header                         
    ORIGIN  = 'NOAO-IRAF FITS Image Kernel July 2003' / FITS file originator        
    OBJECT  = 'PGC12557'           / Name of the object observed                    
    DISPAXIS=                    3 / Dispersion axis                                
    PIXSCALE=                 0.05 / Pixel scale in arcsec/pixel                    
    CTYPE1  = 'LINEAR  '           / coordinate type for the dispersion axis        
    CTYPE2  = 'LINEAR  '           / coordinate type for the spatial axis           
    DC-FLAG =                    0 /                                                
    CD1_1   =                 0.05 /                                                
    CD2_2   =                 0.05 /                                                
    DCLOG1  = 'Transform'                                                           
    WCSDIM  =                    3 /                                                
    CRVAL1  =                2.987                                                  
    CRPIX1  =                  68.                                                  
    CRPIX2  =                   2.                                                  
    WAT1_001= 'wtype=linear axtype=xi' /                                            
    WAT2_001= 'wtype=linear axtype=eta' /                                           
    CTYPE3  = 'LINEAR  '           /                                                
    WAT3_001= 'wtype=linear axtype=wave' /                                          
    CRVAL3  =      19971.869140625 /                                                
    CD3_3   =         2.1321439743 /                                                
    AIRMASS =                1.374                                                  
    CRPIX3  =                   1.                                                  
    LTM1_1  =                   1.                                                  
    LTM2_2  =                   1.                                                  
    LTM3_3  =                   1.                                                  
    WAT0_001= 'system=world'
    END                                     
    '''
    '''
    # NOTE:
    Copied from util.log_rebin():
    lamRange: two elements vector containing the central wavelength
        of the first and last pixels in the spectrum, which is assumed
        to have constant wavelength scale! E.g. from the values in the
        standard FITS keywords: LAMRANGE = CRVAL1 + [0, CDELT1*(NAXIS1 - 1)].
        It must be LAMRANGE[0] < LAMRANGE[1].
    '''
    # lamRange1 = h1['CRVAL1'] + np.array([0., h1['CDELT1']*(h1['NAXIS1'] - 1)])  # original
    # lamRange1 = h1['CRVAL3'] + np.array([0., h1['CD3_3']*(h1['CRPIX3'])])  # [ 19971.86914062  19974.0012846 ]
    #  if I use CRPIX3, probably don't want CRPIX3 - 1 because CRPIX3 = 1., and need lamRange1[0] < lamRange1[1]
    lamRange1 = h1['CRVAL3'] + np.array(
        [0., h1['CD3_3'] *
         (h1['NAXIS3'] - 1)])  # [ 19971.86914062  24319.31070422]
    print(lamRange1, 'l1')

    # print(gal_lin[0][20]) all 0s
    # print((gal_lin[300][35])) NOT all 0s!
    # print(len(lamRange1))  # len(lamRange1) = 2, # len(gal_lin) = 2040, len(gal_lin[0]) = 69, len(gal_lin[0][1]) = 71
    # 2040 --> NAXIS 3, 69 --> NAXIS2, 71 --> NAXIS1
    # HMM: want gal_lin to be spectrum itself, which should just be 1d array
    # There's a len 2040 spectrum at each gal_lin[:,x,y]

    # CUT SPECTRUM TO WAVELENGTH RANGE 2.26 - 2.42
    # SO: gal_lin is an array len(2040) starting at lamRange1[0] and finishing at lamRange1[1], with each pixel in
    # between separated by h1['CD3_3']. So find [22600 - lamRange1[0]] / CD3_3, and that should give the number of
    # pixels between pixel 1 and the pixel corresponding roughly to 2.26 microns. Then find [24200 - lamRange1[1]] /
    # CD3_3, which should give the number of pixels between pixel 1 and the pixel corresponding to 2.42 microns.
    start = 22600.
    stop = 24200.
    cut1 = (start - lamRange1[0]) / h1['CD3_3']
    cut2 = (stop - lamRange1[0]) / h1['CD3_3']
    # print(cut1, cut2, 'cuts')  # 1232.62354281, 1983.04191009
    gal_lin = gal_lin[int(cut1):int(cut2)]  # [1233:1983]
    # start1 = h1['CRVAL3'] + h1['CD3_3'] * int(cut1)
    # stop1 = h1['CRVAL3'] + h1['CD3_3'] * int(cut2)
    # print(start1, stop1, 'me')
    lamRange1 = [
        h1['CRVAL3'] + h1['CD3_3'] * int(cut1),
        h1['CRVAL3'] + h1['CD3_3'] * int(cut2)
    ]
    print(lamRange1, 'l1, cut')
    # len(gal_lin) is now NOT 2040 but 1983 - 1233 = 750

    FWHM_gal = 4.2  # SAURON has an instrumental resolution FWHM of 4.2A.  # BUCKET: do I need this? If so what for?

    # If the galaxy is at significant redshift, one should bring the galaxy
    # spectrum roughly to the rest-frame wavelength, before calling pPXF
    # (See Sec2.4 of Cappellari 2017). In practice there is no
    # need to modify the spectrum in any way, given that a red shift
    # corresponds to a linear shift of the log-rebinned spectrum.
    # One just needs to compute the wavelength range in the rest-frame
    # and adjust the instrumental resolution of the galaxy observations.
    # This is done with the following three commented lines:
    #
    # z = 1.23 # Initial estimate of the galaxy redshift
    # lamRange1 = lamRange1/(1+z) # Compute approximate restframe wavelength range
    # FWHM_gal = FWHM_gal/(1+z)   # Adjust resolution in Angstrom

    # There's a len 2040 spectrum at each gal_lin[:,x,y]
    x = 33
    y = 35
    galaxy, logLam1, velscale = util.log_rebin(
        lamRange1, gal_lin[:, x, y])  # no input velscale --> function returns
    print(len(galaxy), 'len gal')  # 750 now because of cut to gal_lin!
    galaxy = galaxy / np.median(
        galaxy)  # Normalize spectrum to avoid numerical issues
    # print(galaxy)

    # BUCKET: constant noise/pix good assumption for me? If so what value do I use? TRY our noise! Maybe trust more?!
    # basically bootstrap the noise!
    # Do one fit with flat noise, then save the best fit spectrum and the residuals.
    # Then, iterate ~200 times. For these iterations, set bias = 0.0. Each iteration, for each pixel, use the spectrum
    # value as the center of a gaussian and use the residuals at that pixel value as the width of the gaussian. Draw
    # from the resultant distribution to make the new noise. For each iteration, save the output V, sigma, h3, h4, and
    # print each spectrum so that we can see it evolving (it should look more like a real spectrum, rather than smooth
    # curve without lines)
    noise = np.full_like(galaxy,
                         0.0047)  # Assume constant noise per pixel here

    # MEANTIME: why is output velocity close to systemic insted of close to 0, if I'm dealing with redshift already?
    # SET bias = 0
    #
    # print(noise.shape)  # 751,
    # print(galaxy.shape)  # 751,

    # Read the list of filenames from the Single Stellar Population library
    # by Vazdekis (2010, MNRAS, 404, 1639) http://miles.iac.es/. A subset
    # of the library is included for this example with permission
    vazdekis = glob.glob(
        file_dir +
        '/veltemps/*.fits')  # '/miles_models/Mun1.30Z*.fits')  # my new
    # BUCKET: what are FWHM of spectra in the veltemps library??
    FWHM_tem = 2.51  # Vazdekis+10 spectra have a constant resolution FWHM of 2.51A.
    # BUCKET: what spectral sampling compared to galaxy do we want for templates??

    # velscale_ratio = 2  # 1.23  # 2  # adopts 2x higher spectral sampling for templates than for galaxy
    # PROBLEM!! If velscale_ratio is not integer, we get issue later because we slice a list with velscale_ratio
    # so need to change velscale? But it's set by util.log_rebin originally! Only need this if oversampling the
    # templates, which I'm not doing

    # Extract the wavelength range and logarithmically rebin one spectrum
    # to a velocity scale 2x smaller than the SAURON galaxy spectrum, to determine
    # the size needed for the array which will contain the template spectra.
    #
    print(vazdekis[0], 'template name')
    hdu = fits.open(
        vazdekis[0]
    )  # do for just one template to determine size needed for array containing all templates
    ssp = hdu[
        1].data  # was hdu[0], but that's generic header rather than science header
    h2 = hdu[1].header  # was hdu[0]
    '''
    for line in h2:
        print(line, h2[line])
    
    XTENSION IMAGE
    BITPIX -32
    NAXIS 1
    NAXIS1 1721
    PCOUNT 0
    GCOUNT 1
    EXTNAME SCI
    EXTVER 1
    INHERIT False
    ORIGIN NOAO-IRAF FITS Image Kernel July 2003
    OBJECT BD-01 3097
    DATE 2015-02-15T13:32:06
    IRAF-TLM 2015-02-15T13:32:30
    NFPAD 2015-02-14T14:40:41
    GEM-TLM 2015-02-14T14:40:41
    DISPAXIS 1
    PIXSCALE 0.043
    NSCUTSEC [1:2040,1145:1213]
    NSCUTSPC 13
    FIXPIX Feb 14  8:44 Bad pixel file is tmpdq20234_650
    CTYPE1 LINEAR
    CD1_1 2.13
    CTYPE2 LINEAR
    CD2_2 0.04570113
    DCLOG1 Transform
    DC-FLAG 0
    WCSDIM 3
    CRVAL1 20628.29
    CRPIX1 1.0
    CRPIX2 -28.0
    WAXMAP01 1 0 0 29 0 0
    WAT1_001 wtype=linear axtype=wave
    WAT2_001 wtype=linear axtype=eta
    CTYPE3 LINEAR
    WAT3_001 wtype=linear axtype=xi
    CRVAL3 1.751
    CD3_3 0.103
    LTV2 -29.0
    LTM1_1 1.0
    LTM2_2 1.0
    LTM3_3 1.0
    WAT0_001 system=image
    EXPTIME 25.0
    IMCMB001 xatfbrsnN20070508S0187.fits[SCI]
    IMCMB002 xatfbrsnN20070508S0190.fits[SCI]
    IMCMB003 xatfbrsnN20070508S0191.fits[SCI]
    IMCMB004 xatfbrsnN20070508S0194.fits[SCI]
    NCOMBINE 4
    GAINORIG 1.0
    RONORIG 0.0
    GAIN 2.666667
    RDNOISE 0.0
    '''
    # lamRange2 = h2['CRVAL1'] + np.array([0., h2['CDELT1']*(h2['NAXIS1'] - 1)])  # original
    lamRange2 = h2['CRVAL1'] + np.array([0., h2['CD1_1'] * (h2['NAXIS1'] - 1)
                                         ])  # BUCKET want NAXIS - 1?
    # lamRange2 = h2['CRVAL1'] + np.array([0., h2['CD1_1']*(h2['CRPIX1'])])  # or use CRPIX1??
    print(lamRange2, 'l2')  # [ 20628.29  24291.89]
    # print((lamRange2[1] - lamRange2[0])/h2['CD1_1'], 'num of steps in lam2')  # 1720.
    # print(len(ssp), 'len ssp')  # 1721
    sspNew, logLam2, velscale_temp = util.log_rebin(
        lamRange2, ssp, velscale=velscale)  # /velscale_ratio)
    # print(len(sspNew), 'len sspnew')  # 622 hmmmm NEED THIS TO BE >= (len(galaxy)=750)  # FIXED, now 1791
    templates = np.empty((sspNew.size, len(vazdekis)))

    # Convolve the whole Vazdekis library of spectral templates
    # with the quadratic difference between the SAURON and the
    # Vazdekis instrumental resolution. Logarithmically rebin
    # and store each template as a column in the array TEMPLATES.

    # Quadratic sigma difference in pixels Vazdekis --> SAURON
    # The formula below is rigorously valid if the shapes of the
    # instrumental spectral profiles are well approximated by Gaussians.
    #
    FWHM_dif = np.sqrt(FWHM_gal**2 - FWHM_tem**2)
    # sigma = FWHM_dif/2.355/h2['CDELT1']  # Sigma difference in pixels
    sigma = FWHM_dif / 2.355 / h2['CD1_1']  # Sigma difference in pixels

    for j, file in enumerate(
            vazdekis
    ):  # now for each template file; so why do the thing above with just 1??
        hdu = fits.open(file)
        # ssp = hdu[0].data
        ssp = hdu[1].data
        ssp = ndimage.gaussian_filter1d(ssp, sigma)
        # ndimage.gaussian_filter takes input array (ssp) and filters it. Sigma = standard deviation of Gaussian kernel
        # used to filter the array
        # note: discrete convolution is defined (for any 2 arrays a, v): (a*v)[n] == sum m(-inf to inf) of a[m]*v[n-m]
        # where a is the original curve and v is the gaussian
        # print(len(ssp))  # 1721 --> currently by default being resampled to be 116, want it to be resampled to be 71
        sspNew, logLam2, velscale_temp = util.log_rebin(
            lamRange2, ssp, velscale=velscale)  # /velscale_ratio)
        # print(velscale_temp, 'vt')  # 78.82967746
        # print(velscale, 'v')  # 78.82967746
        # print(len(sspNew))  # need this to be >= len(galaxy)  # now 1791
        templates[:, j] = sspNew / np.median(sspNew)  # Normalizes templates

    # print(len(templates[0]))  # len(templates)=29, len(templates[0] = 19)
    # The galaxy and the template spectra do not have the same starting wavelength.
    # For this reason an extra velocity shift DV has to be applied to the template
    # to fit the galaxy spectrum. We remove this artificial shift by using the
    # keyword VSYST in the call to PPXF below, so that all velocities are
    # measured with respect to DV. This assume the redshift is negligible.
    # In the case of a high-redshift galaxy one should de-redshift its
    # wavelength to the rest frame before using the line below (see above).
    #
    c = 299792.458
    dv = (logLam2[0] - logLam1[0]) * c  # km/s
    '''
    if velscale_ratio > 1:
        dv = (np.mean(logLam2[:velscale_ratio]) - logLam1[0])*c  # km/s
    else:
        dv = (logLam2[0] - logLam1[0])*c  # km/s
    '''

    z = 0.016561  # z = 0.0015  # Initial redshift estimate of the galaxy
    goodPixels = util.determine_goodpixels(logLam1, lamRange2, z)

    # Here the actual fit starts. The best fit is plotted on the screen.
    # Gas emission lines are excluded from the pPXF fit using the GOODPIXELS keyword.
    #
    vel = c * np.log(1 + z)  # eq.(8) of Cappellari (2017)
    start = [vel, 200.]  # [vel, 200.]  # (km/s), starting guess for [V, sigma]
    t = clock()
    #  print(galaxy.shape[0])  # = len(galaxy) = 750
    # print(goodPixels)

    # print(max(noise[:, x, y]), min(noise[:, x, y]), np.median(noise[:, x, y]), np.mean(noise[:, x, y]))
    # 0.00106575 -2.77079e-05 4.62628e-05 5.89877e-05
    pp = ppxf(templates,
              galaxy,
              noise,
              velscale,
              start,
              goodpixels=goodPixels,
              plot=True,
              moments=4,
              degree=4,
              vsyst=dv,
              velscale_ratio=1)  # velscale_ratio=velscale_ratio)

    print("Formal errors:")
    print("     dV    dsigma   dh3      dh4")
    print("".join("%8.2g" % f for f in pp.error * np.sqrt(pp.chi2)))

    print('Elapsed time in pPXF: %.2f s' % (clock() - t))

    # If the galaxy is at significant redshift z and the wavelength has been
    # de-redshifted with the three lines "z = 1.23..." near the beginning of
    # this procedure, the best-fitting redshift is now given by the following
    # commented line (equation 2 of Cappellari et al. 2009, ApJ, 704, L34;
    # http://adsabs.harvard.edu/abs/2009ApJ...704L..34C)
    #
    # print('Best-fitting redshift z:', (z + 1)*(1 + pp.sol[0]/c) - 1)
    return pp.bestfit, pp.galaxy, pp.sol  # output_spectrum, output_noise  # for use in noise_in
	def __init__(self, galaxy='ngc3557', slit_h=4.5, slit_w=2, slit_pa=30, 
		method='Rampazzo_aperture', r1=0, r2=None, debug=False):
		print galaxy
		if 'Rampazzo' in method:
			from Rampazzo import rampazzo
			# Default is nuclear region: 0<r<R_e/16
			if r2 is None:
				r2 = get_R_e(galaxy)/16
			data = rampazzo(galaxy, method='aperture', slit_h=slit_h, r1=r1, r2=r2, 
				debug=debug)

			if 'gradient' in method:
				if '2' in method: data.method = 'gradient2'
				else: data.method = 'gradient'

		elif method == 'Ogando':
			from Ogando import ogando
			data = ogando(galaxy, debug=debug, slit_h=slit_h, slit_w=slit_w, 
				slit_pa=slit_pa)

		elif method == 'Miles':
			from Miles import miles
			data = miles(galaxy)

		gal_spec, gal_noise = data.get_spec()

		gal_spec, lam, cut = apply_range(gal_spec, window=201, repeats=3, 
			lam=data.lam, set_range=np.array([4200,10000]), return_cuts=True)
		gal_noise = gal_noise[cut]
		lamRange = np.array([lam[0],lam[-1]])/(1+data.z)
## ----------================= Templates ====================---------
		FWHM_gal = 2.5 # VIMOS documentation (and fits header)
		FWHM_gal = FWHM_gal/(1+data.z) # Adjust resolution in Angstrom

		stellar_templates = get_stellar_templates(galaxy, FWHM_gal)
		velscale = stellar_templates.velscale

		e_templates = get_emission_templates(gas, lamRange, 
			stellar_templates.logLam_template, FWHM_gal)

		if gas:
			templates = np.column_stack((stellar_templates.templates, e_templates.templates))
		else:
			templates = stellar_templates.templates
		component = [0]*len(stellar_templates.templatesToUse) + e_templates.component
		templatesToUse = np.append(stellar_templates.templatesToUse, 
			e_templates.templatesToUse)
		element = ['stellar'] + e_templates.element

		start = [[data.vel, data.sig]] * (max(component) + 1)
		moments = [stellar_moments] + [gas_moments] * max(component)
## ----------============== Final calibrations ==============---------
		## smooth spectrum to fit with templates resolution
		if FWHM_gal < stellar_templates.FWHM_tem:
			sigma = stellar_templates.FWHM_dif/2.355/data.f[0].header['CDELT3']
			gal_spec = ndimage.gaussian_filter1d(gal_spec, sigma)
			gal_noise = np.sqrt(ndimage.gaussian_filter1d(gal_noise**2, sigma))
		
		## rebin spectrum logarthmically
		gal_spec_log, logLam_bin, _ = util.log_rebin(lamRange, gal_spec, velscale=velscale)
		gal_noise_log, logLam_bin, _ = util.log_rebin(lamRange, gal_noise**2, 
			velscale=velscale)
		gal_noise_log = np.sqrt(gal_noise_log)

		gal_noise_log = gal_noise_log + 0.0000000000001



		dv = (stellar_templates.logLam_template[0]-logLam_bin[0])*c # km/s
		# Find the pixels to ignore to avoid being distracted by gas emission
		#; lines or atmospheric absorbsion line.  
		goodPixels = determine_goodpixels(logLam_bin,stellar_templates.lamRange_template,
			data.vel, data.z, gas=gas!=0) 
		lambdaq = np.exp(logLam_bin)
## ----------=================== pPXF =======================---------
		pp = ppxf(templates, gal_spec_log, gal_noise_log, velscale, start, 
			goodpixels=goodPixels, moments=moments, degree=-1, vsyst=dv, 
			component=component, lam=lambdaq, plot=not quiet, quiet=quiet, mdegree=10)
		self.pp = pp

		# Only use stellar templates (Remove emission lines)
		stellar_spec = gal_spec_log - \
			pp.matrix[:, -e_templates.ntemp:].dot(pp.weights[-e_templates.ntemp:])
		conv_spec = pp.matrix[:, :-e_templates.ntemp].dot(pp.weights[:-e_templates.ntemp])

		# Generate the unconvolved spectra ('0 km/s' resolution)
		unc_lam = stellar_templates.wav
		unc_spec = stellar_templates.lin_templates.dot(pp.weights[:stellar_templates.ntemp])
		unc_spec *= np.polynomial.legendre.legval(np.linspace(-1,1,
			len(unc_spec)), np.append(1, pp.mpolyweights))
## ----------============== Absorption Line =================---------

		lines = ['G4300', 'Fe4383', 'Ca4455', 'Fe4531', 'H_beta', 'Fe5015', 'Mg_b']
		self.result = {}
		self.uncert = {}
		for line in lines:		
			ab, ab_uncert = absorption(line, lambdaq, stellar_spec, noise=gal_noise_log, 
				unc_lam=unc_lam, unc_spec=unc_spec,	conv_spec=conv_spec)#, 
				#lick=True)

			if method == 'Ogando':
				# Aperture correction
				ab_file = '%s/Documents/useful_files/ab_linelist.dat' % (cc.home_dir)
				i1, i2, b1, b2, r1, r2, units = np.genfromtxt(ab_file, unpack=True, 
					usecols=(1,2,3,4,5,6,7), skip_header=2, skip_footer=2)
				ls = np.genfromtxt(ab_file, unpack=True, dtype=str, usecols=(8), 
					skip_header=2, skip_footer=2)
				l = np.where(ls == line)[0][0]
				index = [i1[l], i2[l]]

				# Convert to mag
				ab = -2.5 * np.log(1 - ab/(index[1]-index[0]))

				# Aperture Correction: beta values taken from paper
				beta = {'H_beta':0.002, 'Fe5015':-0.012, 'Mg_b':-0.031} 
				# If line is not observed by Ogando
				if line not in beta.keys():
					self.result[line] = np.nan
					self.uncert[line] = np.nan
					continue
				H = 70.0 # value used by Bolonga group.
				r_ab = np.degrees(1.19/1000 * H/(c * data.z)) * 60 * 60 # 1.19 kpc -> arcsec
				# Correction is def in eq (9) with r_ab and r_norm def in eq (1) - not 
				#	100% sure if I've got them correct.
				ab = ab - beta[line] * np.log(1.025 * np.sqrt(slit_w*r_ab/np.pi)/slit_h)

				# Back to Angstroms
				ab = (index[1]-index[0]) * (1 - np.exp(ab/-2.5))

			elif 'Rampazzo' in method:
				self.r = data.r
			self.result[line] = ab
			self.uncert[line] = ab_uncert

		if debug:
			for line in lines:
				print '%s:	%.3f +/- %.3f' % (line, self.result[line], self.uncert[line])
示例#17
0
def ppxfit(ncompfit, brot, bfract, mom):

    velscale = 110.
    file = 'NGC0528-V500.rscube.fits'
    hdu = pyfits.open(file)
    gal_lin = hdu[0].data
    h1 = hdu[0].header

    medfl = np.loadtxt("medgalpy.txt")
    x = medfl[:, 0]
    y = medfl[:, 1]
    sig = medfl[:, 2]
    noise = medfl[:, 3]

    bins = np.loadtxt("voronoi_2d_binning_output.txt", skiprows=1)
    x = bins[:, 0]
    y = bins[:, 1]
    binnum = bins[:, 2]

    binco = np.loadtxt("bins.txt")
    xbin = binco[:, 0]
    ybin = binco[:, 1]

    file = 'galaxybinspy.fits'  # spectra arranged horizontally
    hdu = pyfits.open(file)
    gal_bin = hdu[0].data
    gs = gal_bin.shape
    nbins = gs[0]
    xcut = 0.0
    ycut = 0.0
    delta = h1['CDELT3']
    lamRange1 = h1['CRVAL3'] + np.array(
        [xcut * delta, delta * ((h1['NAXIS3'] - 1) - ycut)])
    FWHM_gal = 6.0  # CALIFA has an instrumental resolution FWHM of 6A.

    galaxyz, logLam1, velscale = util.log_rebin(lamRange1,
                                                gal_bin[0, :],
                                                velscale=velscale)

    galaxy = np.empty((galaxyz.size, nbins))
    noise = np.empty((galaxyz.size, nbins))

    for j in range(nbins):
        galaxy[:, j], logLam1, velscale = util.log_rebin(lamRange1,
                                                         gal_bin[j, :],
                                                         velscale=velscale)
        galaxy[:, j] = galaxy[:, j] / np.median(
            galaxy[:, j])  # Normalize spectrum to avoid numerical issues
        noise[:,
              j] = galaxy[:,
                          j] * 0 + 0.0049  # Assume constant noise per pixel here

    #dir='/home/ppxmt3/astro/MILES/'
    dir = 'galspec/'
    miles = glob.glob(dir + 'Mun*.fits')
    miles.sort()
    FWHM_tem = 2.5  # Miles spectra have a resolution FWHM of 1.8A.

    age = np.empty(len(miles))
    met = np.empty(len(miles))
    #age=np.chararray(len(miles),itemsize=7)
    #met=np.chararray(len(miles),itemsize=5)

    for j in range(len(miles)):
        ast = miles[j][22:29]
        mst = miles[j][17:21]
        pm = miles[j][16:17]
        if pm == 'p': pmn = '+'
        elif pm == 'm': pmn = '-'
        mstpm = (pmn, mst)
        #met[j,:]=miles[j][16:19]
        age[j] = float(ast)
        met[j] = float("".join(mstpm))

    #age2,inda=np.unique(age,return_inverse=True)
    #met2,ind=np.unique(met,return_inverse=True)

    #c=1
    #for i in range(len(age2)/2):
        #indout=np.where(age==age2[c])[0]
        ##print(indout)
        #miles=np.delete(miles,indout)
        #age=np.delete(age,indout)
        #c=c+2

        # Extract the wavelength range and logarithmically rebin one spectrum
        # to the same velocity scale of the CALIFA galaxy spectrum, to determine
        # the size needed for the array which will contain the template spectra.

    hdu = pyfits.open(miles[0])
    ssp = hdu[0].data
    h2 = hdu[0].header
    lamRange2 = h2['CRVAL1'] + np.array(
        [0., h2['CDELT1'] * (h2['NAXIS1'] - 1)])
    sspNew, logLam2, velscale = util.log_rebin(lamRange2,
                                               ssp,
                                               velscale=velscale)

    # Convolve the whole miles library of spectral templates
    # with the quadratic difference between the CALIFA and the
    # miles instrumental resolution. Logarithmically rebin
    # and store each template as a column in the array TEMPLATES.

    # Quadratic sigma difference in pixels miles --> CALIFA
    # The formula below is rigorously valid if the shapes of the
    # instrumental spectral profiles are well approximated by Gaussians.
    #
    FWHM_dif = np.sqrt(FWHM_gal**2 - FWHM_tem**2)
    sigma = FWHM_dif / 2.355 / h2['CDELT1']  # Sigma difference in pixels

    #==========================================================================================================================
    #One component fit - saves veloctiy values in 'NGC528_onecompkin.txt' to be used as initial estimates for two component fit

    if ncompfit == 1:

        templates = np.empty((sspNew.size, len(miles)))
        for j in range(len(miles)):
            hdu = pyfits.open(miles[j])
            ssp = hdu[0].data
            ssp = ndimage.gaussian_filter1d(ssp, sigma)
            sspNew, logLam2, velscale = util.log_rebin(lamRange2,
                                                       ssp,
                                                       velscale=velscale)
            templates[:,
                      j] = sspNew / np.median(sspNew)  # Normalizes templates

        c = 299792.458
        dv = (logLam2[0] - logLam1[0]) * c  # km/s

        vel = 4750.  # Initial estimate of the galaxy velocity in km/s
        z = np.exp(
            vel / c) - 1  # Relation between velocity and redshift in pPXF
        goodPixels = util.determine_goodpixels(logLam1, lamRange2, z)

        start = np.zeros(2)
        output = np.zeros((nbins, 5))
        output[:, 0] = xbin[:]
        output[:, 1] = ybin[:]

        start[:] = [vel, 3. * velscale]  # (km/s), starting guess for [V,sigma]

        for j in range(nbins):
            print('On ', j + 1, ' out of ', nbins)
            print(start)
            pp = ppxf(templates,
                      galaxy[:, j],
                      noise[:, j],
                      velscale,
                      start,
                      goodpixels=goodPixels,
                      degree=4,
                      vsyst=dv,
                      plot=True,
                      moments=mom)
            kinem = np.loadtxt("ppxfout.txt")
            if mom == 2:
                output[j, 2] = kinem[0]  #vel
                output[j, 3] = kinem[1]  #sigma
                output[j, 4] = kinem[2]  #chisq
            if mom == 4:
                output[j, 2] = kinem[0]  #vel
                output[j, 3] = kinem[1]  #sigma
                output[j, 4] = kinem[4]  #chisq

        np.savetxt('NGC528_onecompkinm2nch.txt', output, fmt="%10.3g")

#=========================================================================================================================
#Two component fit

    elif ncompfit == 2:

        # To determine flux fraction of bulge. Set bfract to 0 to disable

        # 'bulgediskblock.fits' is created by running GALFIT to get a galfit.01 file of best fit parameters, then using
        # '>galfit -o3 galfit.01' to get cube of galaxy image, bulge image and disk image

        if bfract == 1:
            file = 'bulgediskblock.fits'
            hdu = pyfits.open(file)
            galb = hdu[1].data
            bulge = hdu[2].data
            disk = hdu[3].data

            # Bin bulge and disk images into same binning as datacube
            nbins = xbin.shape[0]
            avbulge = np.zeros(nbins)
            avdisk = np.zeros(nbins)
            avtot = np.zeros(nbins)
            binflux = np.zeros(nbins)
            x = x.astype(int)
            y = y.astype(int)
            for j in range(nbins):
                b = np.where(binnum == j)[0]
                valbin = b.size
                if valbin == 1:
                    avbulge[j] = bulge[y[b], x[b]]
                    avdisk[j] = disk[y[b], x[b]]
                    avtot[j] = galb[x[b], y[b]]
                else:
                    avbulge[j] = np.median(bulge[y[b], x[b]])
                    avdisk[j] = np.median(disk[y[b], x[b]])
                    avtot[j] = np.median(galb[x[b], y[b]])

            bulge_fraction = avbulge / (avbulge + avdisk)

            hdu = pyfits.PrimaryHDU(bulge_fraction)
            hdu.writeto('bulge_fraction.fits', clobber=True)


#====================================================================================

        templates = np.empty((sspNew.size, 2 * len(miles)))
        ssparr = np.empty((ssp.size, len(miles)))

        for j in range(len(miles)):
            hdu = pyfits.open(miles[j])
            ssparr[:, j] = hdu[0].data
            ssp = hdu[0].data
            ssp = ndimage.gaussian_filter1d(ssp, sigma)
            sspNew, logLam2, velscale = util.log_rebin(lamRange2,
                                                       ssp,
                                                       velscale=velscale)
            templates[:,
                      j] = sspNew / np.median(sspNew)  # Normalizes templates
        for j in range(len(miles), 2 * len(miles)):
            hdu = pyfits.open(miles[j - len(miles)])
            ssp = hdu[0].data
            ssp = ndimage.gaussian_filter1d(ssp, sigma)
            sspNew, logLam2, velscale = util.log_rebin(lamRange2,
                                                       ssp,
                                                       velscale=velscale)
            templates[:,
                      j] = sspNew / np.median(sspNew)  # Normalizes templates

        component = np.zeros((2 * len(miles)), dtype=np.int)
        component[0:len(miles)] = 0
        component[len(miles):] = 1

        c = 299792.458
        dv = (logLam2[0] - logLam1[0]) * c  # km/s
        vel = 4750.  # Initial estimate of the galaxy velocity in km/s
        z = np.exp(
            vel / c) - 1  # Relation between velocity and redshift in pPXF
        goodPixels = util.determine_goodpixels(logLam1, lamRange2, z)

        kin = np.loadtxt('NGC528_onecompkinnch.txt')
        xbin = kin[:, 0]
        ybin = kin[:, 1]
        vpxf = kin[:, 2]
        spxf = kin[:, 3]
        occh = kin[:, 4]
        velbulge = vel
        sigdisk = 50.

        file = 'bulge_fraction.fits'  # Read out bulge fraction for each bin
        hdu = pyfits.open(file)
        bulge_fraction = hdu[0].data

        bvel = np.zeros(nbins)
        bsig = np.zeros(nbins)
        bh3 = np.zeros(nbins)
        bh4 = np.zeros(nbins)
        dvel = np.zeros(nbins)
        dsig = np.zeros(nbins)
        dh3 = np.zeros(nbins)
        dh4 = np.zeros(nbins)
        bwt = np.zeros(nbins)
        dwt = np.zeros(nbins)
        output = np.zeros((nbins, 10 + (2 * (mom - 2))))
        popoutput = np.zeros((nbins, 6))
        output[:, 0] = xbin[:]
        output[:, 1] = ybin[:]
        popoutput[:, 0] = xbin[:]
        popoutput[:, 1] = ybin[:]

        bmet = np.zeros(nbins)
        bage = np.zeros(nbins)
        dage = np.zeros(nbins)
        dmet = np.zeros(nbins)
        count = 0
        for j in range(2, nbins - 1):
            print('Bin number:', j + 1, 'out of', nbins)
            print('Bulge fraction:', bulge_fraction[j])
            if spxf[j] > 350: spxf[j] = 350.
            if abs(vpxf[j] - 4750.) > 300: vpxf[j] = 4750.

            #start = np.array([[vpxf[j], spxf[j]],[vpxf[j],sigdisk]]) # (km/s), starting guess for [V,sigma]

            start = np.array([[velbulge, spxf[j]], [vpxf[j], sigdisk]
                              ])  # (km/s), starting guess for [V,sigma]
            print('Starting velocity estimates:', start[0, 0], start[0, 1],
                  start[1, 0], start[1, 1])
            print('Xbin:', xbin[j], 'Ybin:', ybin[j])
            t = clock()
            pp = ppxf(templates,
                      galaxy[:, j],
                      noise[:, j],
                      velscale,
                      start,
                      bulge_fraction=bulge_fraction[j],
                      goodpixels=goodPixels,
                      moments=[mom, mom],
                      degree=4,
                      vsyst=dv,
                      component=component,
                      brot=1,
                      plot=True)  #brot=0 for nonrotating, brot=1 for rotating
            #Kinematics

            kinem = np.loadtxt("ppxfout.txt")
            wts = np.loadtxt("ppxfoutwt.txt")

            if mom == 2:
                output[j, 2] = kinem[0, 0]  #bvel
                output[j, 3] = kinem[0, 1]  #bsig
                output[j, 4] = kinem[1, 0]  #dvel
                output[j, 5] = kinem[1, 1]  #dsig
                output[j, 6] = wts[0]  #bulge weight
                output[j, 7] = wts[1]  #disk weight
                output[j, 8] = wts[2]  #chisqn
                output[j, 9] = wts[3]  #chisq

            if mom == 4:
                output[j, 2] = kinem[0, 0]  #bvel
                output[j, 3] = kinem[0, 1]  #bsig
                output[j, 4] = kinem[0, 2]  #bh3
                output[j, 5] = kinem[0, 3]  #bh4
                output[j, 6] = kinem[1, 0]  #dvel
                output[j, 7] = kinem[1, 1]  #dsig
                output[j, 8] = kinem[1, 2]  #dh3
                output[j, 9] = kinem[1, 3]  #dh4
                output[j, 10] = wts[0]  #bulge weight
                output[j, 11] = wts[1]  #disk weight
                output[j, 12] = wts[2]  #chisqn
                output[j, 13] = wts[3]  #chisq
                print(wts[0], wts[1])
                print('Chisq difference from one comp fit (pos = improved)',
                      occh[j] - wts[2])
                if occh[j] > wts[2]: count = count + 1
            bwt[j] = wts[0]
            dwt[j] = wts[1]

            #Populations

            #wtsb=np.loadtxt("ppxfoutwtsb.txt")
            #wtsd=np.loadtxt("ppxfoutwtsd.txt")
            #shwb=wtsb.shape
            #shwd=wtsd.shape

            #if len(shwb) > 1:
        #bulgewt=np.array(wtsb[0,:])
        #bulgewt=bulgewt/bulgewt.sum()
        #bulgewtin=np.array(wtsb[1,:],dtype=int)
        #else:
        #bulgewt=1.
        #bulgewtin=np.int(wtsb[1])
        #if len(shwd) > 1:
        #diskwt=np.array(wtsd[0,:])
        #diskwt=diskwt/diskwt.sum()
        #diskwtin=np.array(wtsd[1,:],dtype=int)
        #else:
        #diskwt=1.
        #diskwtin=np.int(wtsd[1])

        #bage[j]=np.dot(bulgewt,age[bulgewtin])
        #bmet[j]=np.dot(bulgewt,met[bulgewtin])
        #dage[j]=np.dot(diskwt,age[diskwtin])
        #dmet[j]=np.dot(diskwt,met[diskwtin])

        #popoutput[j,2]=bage[j]
        #popoutput[j,3]=bmet[j]
        #popoutput[j,4]=dage[j]
        #popoutput[j,5]=dmet[j]

        #Plots

        #ssparr=templates
        #print(gal_bin.shape)
        #gal_bin=galaxy
        #print(gal_bin.shape)
        #bulgespec=ssparr[:,bulgewtin].dot(bulgewt)
        #diskspec=ssparr[:,diskwtin].dot(diskwt)
        #diskspec=diskspec/np.median(diskspec)
        #bulgespec=bulgespec/np.median(bulgespec)
        #plt.xlabel("Wavelength")
        #plt.ylabel("Counts")
        #plt.plot(5*(gal_bin[goodPixels,j]/np.median(gal_bin[goodPixels,j])), 'k')
        #plt.plot(3*(bulgespec[goodPixels]), 'r')
        #plt.plot(2*(diskspec[goodPixels]), 'b')
        #plt.plot(5*(bulgespec[goodPixels]+diskspec[goodPixels])/np.median(bulgespec[goodPixels]+diskspec[goodPixels]), 'g')
        #plt.savefig('outfit')

        np.savetxt('NGC528conskinnchcheckbrot.txt', output, fmt="%10.3g")
def ppxf_example_sky_and_symmetric_losvd():

    file_dir = path.dirname(path.realpath(__file__))  # path of this procedure

    # Solar metallicity, Age=12.59 Gyr
    hdu = fits.open(file_dir + '/miles_models/Mun1.30Zp0.00T12.5893.fits')
    ssp = hdu[0].data
    h = hdu[0].header

    lamRange = h['CRVAL1'] + np.array([0., h['CDELT1'] * (h['NAXIS1'] - 1)])
    velscale = 70.  # km/s
    star, logLam, velscale = util.log_rebin(lamRange, ssp, velscale=velscale)
    star /= np.mean(star)

    # Adopted input parameters =================================================

    vel = 200. / velscale  # Velocity of 1st spectrum in pixels (2nd has -vel)
    sigma = 300. / velscale  # Dispersion of both spectra in pixels
    h3 = 0.1  # h3 of 1st spectrum (2nd has -h3)
    h4 = 0.1
    sn = 40.
    moments = 4
    deg = 4
    vshift = 10  # Adopted systemic velocity in pixels
    vsyst = vshift * velscale  # Adopted systemic velocity in km/s

    # Generate input Sky =======================================================
    # For illustration, the sky is modelled as two Gaussian emission lines

    n = star.size
    x = np.arange(n)
    sky1 = np.exp(-0.5 * (x - 1000)**2 / 100)
    sky2 = np.exp(-0.5 * (x - 2000)**2 / 100)

    # Generate input LOSVD =====================================================

    dx = int(abs(vel) + 5 * sigma)
    v = np.linspace(-dx, dx, 2 * dx + 1)
    w = (v - vel) / sigma
    w2 = w**2
    gauss = np.exp(-0.5 * w2)
    gauss /= np.sum(gauss)
    h3poly = w * (2 * w2 - 3) / np.sqrt(3)
    h4poly = (w2 * (4 * w2 - 12) + 3) / np.sqrt(24)
    losvd = gauss * (1 + h3 * h3poly + h4 * h4poly)

    # Generate first synthetic spectrum ========================================
    # The template is convolved with the LOSVD

    x = np.linspace(-1, 1, n)
    galaxy1 = signal.fftconvolve(star, losvd, mode="same")
    galaxy1 = np.roll(galaxy1, vshift)  # Mimic nonzero systemic velocity
    galaxy1 *= legendre.legval(
        x, np.append(1,
                     np.random.uniform(-0.1, 0.1,
                                       deg - 1)))  # Multiplicative polynomials
    galaxy1 += legendre.legval(x,
                               np.random.uniform(-0.1, 0.1,
                                                 deg))  # Additive polynomials
    galaxy1 += sky1 + 2 * sky2  # Add two sky lines
    galaxy1 = np.random.normal(galaxy1, 1 / sn)  # Add noise

    # Generate symmetric synthetic spectrum ====================================
    # The same template is convolved with a reversed LOSVD
    # and different polynomials and sky lines are included

    galaxy2 = signal.fftconvolve(star, np.flip(losvd, 0), mode="same")
    galaxy2 = np.roll(galaxy2, vshift)  # Mimic nonzero systemic velocity
    galaxy2 *= legendre.legval(
        x, np.append(1,
                     np.random.uniform(-0.1, 0.1,
                                       deg - 1)))  # Multiplicative polynomials
    galaxy2 += legendre.legval(x,
                               np.random.uniform(-0.1, 0.1,
                                                 deg))  # Additive polynomials
    galaxy2 += 2 * sky1 + sky2  # Add two sky lines
    galaxy2 = np.random.normal(galaxy2, 1 / sn)  # Add noise

    # Load spectral templates ==================================================

    vazdekis = glob.glob(file_dir + '/miles_models/Mun1.30Z*.fits')
    templates = np.empty((n, len(vazdekis)))
    for j, file in enumerate(vazdekis):
        hdu = fits.open(file)
        ssp = hdu[0].data
        sspNew, logLam2, velscale = util.log_rebin(lamRange,
                                                   ssp,
                                                   velscale=velscale)
        templates[:, j] = sspNew / np.median(sspNew)  # Normalize templates

    # Do the fit ===============================================================

    # Input both galaxy spectra simultaneously to pPXF
    galaxy = np.column_stack([galaxy1, galaxy2])

    # Use two sky templates for each galaxy spectrum
    sky = np.column_stack([sky1, sky2])

    # Randomized starting guess
    vel0 = vel + np.random.uniform(-1, 1)
    sigma0 = sigma * np.random.uniform(0.8, 1.2)
    start = np.array([vel0, sigma0]) * velscale  # Convert to km/s
    goodpixels = np.arange(50, n - 50)

    print(
        "\nThe input values are: Vel=%0.0f, sigma=%0.0f, h3=%0.1f, h4=%0.1f\n"
        % (vel * velscale, sigma * velscale, h3, h4))

    t = clock()

    pp = ppxf(templates,
              galaxy,
              np.full_like(galaxy, 1 / sn),
              velscale,
              start,
              goodpixels=goodpixels,
              plot=1,
              moments=moments,
              vsyst=vsyst,
              mdegree=deg,
              degree=deg,
              sky=sky)

    print('Elapsed time in pPXF: %.2f s' % (clock() - t))
    plt.pause(1)
示例#19
0
        norm=np.median(templates)
        templates /= norm
        full /= norm
        
        dv = c*np.log(lamRange_temp[0]/wave[0])  # km/s
        
        goodpixels = util.determine_goodpixels(log_wave, lamRange_temp, z)

        start = [c*np.log(1 + z), 0.01]#3*velscale]
        fixed = [True, True]#, False, False]
        fixed = [False, False]
        print start
        
        pp = ppxf(templates, galaxy, noise, velscale, start,
          goodpixels=goodpixels, plot=False, moments=2, degree=-1,
          vsyst=dv, clean=False, regul=1,
          mdegree=0, reddening=0.1,lam=wave,
          fixed=fixed
          )

        # noise *= np.sqrt(pp.chi2)
        # pp = ppxf(templates, galaxy, noise, velscale, start,
        #           goodpixels=goodpixels, plot=False, moments=2, degree=-1,
        #           vsyst=dv, clean=False, regul=0.,
        #           mdegree=0, reddening=None,lam=wave
        #           )
        
        pp.grid={'Z':metal_grid,'logAge':logAge_grid}
        title=os.path.basename(filename).split('.')[0]
        outdir='output_bc03s'
        if not os.path.exists(outdir):
            os.mkdir(outdir)
示例#20
0
    lambB = lambB[0:2830]
    flux_t, lamb_t = glue_BR(spectrum_B, lambB, spectrum_R, lambR)
    spec_vor.append(flux_t)

    specNew, logLam, velscale = util.log_rebin([lamb_t[0], lamb_t[-1]], flux_t, oversample=False,flux=False)

    galaxy = specNew[np.where((np.exp(logLam) >= wave_limit[0]) & (np.exp(logLam) <= wave_limit[1]))]


    ##### pPXF: Here the actual fit starts. The best fit is plotted on the screen.

    noise = np.sqrt(np.var(galaxy))
    noise = np.full_like(galaxy,noise)

    fig = plt.figure(num=0, figsize=(12.6, 5))
    pp = ppxf(templates, galaxy, noise, velscale, start, plot=False, moments=moments, degree=-1,\
                  mdegree=10, vsyst=dv, clean=False, component=component, quiet=True)

    gas = np.array(component) == 1  # Select weights of gas emissions only
    stars = pp.matrix[:, ~gas].dot(pp.weights[~gas])
    gas = pp.matrix[:, gas].dot(pp.weights[gas])
    w = np.where(np.array(component) == 1)[0]

    filename = destdir+'/bins/'+'pPXFoutput_'+str(bin)+'.csv'
    dat = Table([wave, pp.galaxy, pp.bestfit, stars, gas], names=('wave', 'galaxy','bestfit', 'stars', 'gas'))
    dat.write(filename, format='csv', overwrite=True)

    fig, ax = plt.subplots(figsize = (17,15))
    plt.subplot(211)
    plt.xlabel("Observed Wavelength ($\AA$)")
    plt.ylabel("Flux")
    plt.xlim([np.min(wave), np.max(wave)])
示例#21
0
	def run(self):
		if self.params.start is None:
			if not self.params.narrow_broad:
				start = [[self.vel, self.sig]] * (max(self.component) + 1)
			else:
				start = [[self.vel, self.sig]] * (max(self.component)/2 + 1)
				# Start narrow line component at half of broad line
				start.extend([[self.vel, self.sig/2]] * (max(self.component)/2))
		else:
			start = self.params.start
			for i, e in enumerate(self.element):
				if start[i][0] is None:
					start[i][0] = self.vel
				if start[i][1] is None:
					if 'n_' not in e:
						start[i][1] = self.sig
					else:
						start[i][1] = self.sig/2.
		
		moments = [self.params.stellar_moments] + [self.params.gas_moments] * \
			max(self.component)
	## ----------============== The bestfit part ===============---------
		ppxf.__init__(self, self.templates, self.bin_log, self.bin_log_noise, 
			self.velscale, start, goodpixels=self.goodPixels, 
			mdegree=self.params.mdegree, moments=moments, 
			degree=self.params.degree, vsyst=self.dv, component=self.component, 
			lam=self.lambdaq, plot=not self.params.quiet, quiet=self.params.quiet, 
			produce_plot=False)
		if self.params.gas == 0: 
			self.sol = [self.sol]
			self.error = [self.error]

	## ----------===============================================---------
	## ----------================= The MC part =================---------
	## ----------===============================================---------
		if self.params.use_all_temp is not None:
			self.MCstellar_kin = np.zeros((self.params.reps, 
				abs(self.params.stellar_moments)))
			self.MCstellar_kin_err = np.zeros((self.params.reps, 
				abs(self.params.stellar_moments)))
		self.MCbestfit_uncert = np.zeros((3, len(self.galaxy)))
		MCbestfit_mean = np.zeros(len(self.galaxy))

		if self.params.gas:
			self.MCgas_kin = np.zeros((max(self.component), self.params.reps,
				abs(self.params.gas_moments)))
			self.MCgas_kin_err = np.zeros((max(self.component), self.params.reps, 
				abs(self.params.gas_moments)))

			n_lines = len(self.e_templates.templatesToUse)
			# self.MCgas_weights = np.zeros((n_lines, self.params.reps))

			self.MCgas_uncert_spec = np.zeros((n_lines, 3, len(self.galaxy)))
			MCgas_mean_spec = np.zeros((n_lines, len(self.galaxy)))

			if self.params.reps == 0:
				self.MCgas_uncert_spec = np.zeros((n_lines, len(self.galaxy)))
		else:
			self.MCgas_kin = None
			self.MCgas_kin_err = None
			# self.MCgas_weights = None

		for rep in range(self.params.reps):
			random = np.random.randn(len(self.bin_log_noise))
			if self.params.use_residuals and rep == 0:
				_, residuals, _ = moving_weighted_average(self.lam, 
					self.bestfit - self.bin_lin, step_size=3., interp=True)
				add_noise = random * np.sqrt(self.bin_log_noise**2 + residuals**2)
			elif self.params.use_residuals:
				_, residuals, _ = moving_weighted_average(self.lam, 
					ppMC.bestfit - self.bin_lin, step_size=3., interp=True)
				add_noise = random * np.sqrt(self.bin_log_noise**2 + residuals**2)
			else:
				add_noise = random * np.abs(self.bin_log_noise)
			self.bin_log = self.bestfit + add_noise

			ppMC = ppxf(self.templates, self.bin_log, self.bin_log_noise, 
				self.velscale, start, goodpixels=self.goodPixels, moments=moments, 
				degree=self.params.degree, vsyst=self.dv, lam=self.lambdaq, 
				plot=not self.params.quiet, quiet=self.params.quiet, bias=0.1, 
				component=self.component, mdegree=self.params.mdegree)

			if self.params.gas == 0: 
				ppMC.sol = [ppMC.sol]
				ppMC.error = [ppMC.error]

			if self.params.use_all_temp is not None:
				self.MCstellar_kin[rep,:] = ppMC.sol[0][
					0:abs(self.params.stellar_moments)]
				self.MCstellar_kin_err[rep,:] = ppMC.error[0][0:
					abs(self.params.stellar_moments)]

			# Find uncertainty in bestfit
			new_mean_bestfit_spec = ((rep + 1) * MCbestfit_mean + ppMC.bestfit)/(
				rep + 2)
			if rep < 3:
				# Save spec until 3 reps have been completed
				self.MCbestfit_uncert[rep, :] = ppMC.bestfit
			else:
				# Finding sigma_N from x_N, mean_N, mean_(N-1) and sigma_(N-1)
				# NB: rep = N-1 due to being zero-based
				self.MCbestfit_uncert = np.sqrt(((ppMC.bestfit - 
					new_mean_bestfit_spec) * (ppMC.bestfit - MCbestfit_mean) + 
					rep * self.MCbestfit_uncert**2)/(rep + 1))
			MCbestfit_mean = np.array(new_mean_bestfit_spec)
			if rep == 2:
				# Calc std at 3rd rep
				self.MCbestfit_uncert = np.std(self.MCbestfit_uncert, axis=0)

			# Gas kinematics from all reps
			for g in range(len(self.element) - 1):
				self.MCgas_kin[g,rep,:] = ppMC.sol[g+1][0:abs(self.params.gas_moments)]
				self.MCgas_kin_err[g,rep,:] = ppMC.error[g+1][
					0:abs(self.params.gas_moments)]

			# Find uncertainty in fitted emission lines
			for i, n in enumerate(self.e_templates.templatesToUse):
				e_line_spec = ppMC.matrix[:, -n_lines + i] * ppMC.weights[
					-n_lines + i]
				new_mean_gas_spec = ((rep + 1) * MCgas_mean_spec[i, :] + 
					e_line_spec)/(rep + 2)

				if rep < 3 or self.params.reps == 0:
					self.MCgas_uncert_spec[i, rep, :] = e_line_spec
				else:
					self.MCgas_uncert_spec[i,:] = np.sqrt(((e_line_spec - 
						new_mean_gas_spec) * (e_line_spec - MCgas_mean_spec[i,:]) + 
						rep * self.MCgas_uncert_spec[i,:]**2)/(rep + 1))
				MCgas_mean_spec[i, :] = np.array(new_mean_gas_spec)

			if rep == 2 and self.params.gas != 0:
				self.MCgas_uncert_spec = np.std(self.MCgas_uncert_spec, axis=1)

		if self.params.produce_plot:
			self.fig, self.ax = create_plot(self).produce
示例#22
0
def ppxf_population_gas_example_sdss():

    # Read SDSS DR8 galaxy spectrum taken from here http://www.sdss3.org/dr8/
    # The spectrum is *already* log rebinned by the SDSS DR8
    # pipeline and log_rebin should not be used in this case.
    #
    file = 'spectra/NGC3522_SDSS_DR8.fits'
    hdu = fits.open(file)
    t = hdu[1].data
    z = float(hdu[1].header["Z"])  # SDSS redshift estimate

    # Only use the wavelength range in common between galaxy and stellar library.
    #
    mask = (t['wavelength'] > 3540) & (t['wavelength'] < 7409)
    flux = t['flux'][mask]
    galaxy = flux / np.median(
        flux)  # Normalize spectrum to avoid numerical issues
    wave = t['wavelength'][mask]

    # The noise level is chosen to give Chi^2/DOF=1 without regularization (REGUL=0).
    # A constant error is not a bad approximation in the fitted wavelength
    # range and reduces the noise in the fit.
    #
    noise = galaxy * 0 + 0.01528  # Assume constant noise per pixel here

    # The velocity step was already chosen by the SDSS pipeline
    # and we convert it below to km/s
    #
    c = 299792.458  # speed of light in km/s
    velscale = c * np.log(wave[1] / wave[0])
    FWHM_gal = 2.76  # SDSS has an approximate instrumental resolution FWHM of 2.76A.

    #------------------- Setup templates -----------------------

    stars_templates, lamRange_temp, logLam_temp = \
        setup_spectral_library(velscale, FWHM_gal)

    # The stellar templates are reshaped into a 2-dim array with each spectrum
    # as a column, however we save the original array dimensions, which are
    # needed to specify the regularization dimensions
    #
    reg_dim = stars_templates.shape[1:]
    stars_templates = stars_templates.reshape(stars_templates.shape[0], -1)

    # See the pPXF documentation for the keyword REGUL,
    # for an explanation of the following two lines
    #
    stars_templates /= np.median(
        stars_templates)  # Normalizes stellar templates by a scalar
    regul_err = 0.004  # Desired regularization error

    # Construct a set of Gaussian emission line templates.
    # Estimate the wavelength fitted range in the rest frame.
    #
    lamRange_gal = np.array([np.min(wave), np.max(wave)]) / (1 + z)
    gas_templates, line_names, line_wave = \
        util.emission_lines(logLam_temp, lamRange_gal, FWHM_gal)

    # Combines the stellar and gaseous templates into a single array
    # during the PPXF fit they will be assigned a different kinematic
    # COMPONENT value
    #
    templates = np.column_stack([stars_templates, gas_templates])

    #-----------------------------------------------------------

    # The galaxy and the template spectra do not have the same starting wavelength.
    # For this reason an extra velocity shift DV has to be applied to the template
    # to fit the galaxy spectrum. We remove this artificial shift by using the
    # keyword VSYST in the call to PPXF below, so that all velocities are
    # measured with respect to DV. This assume the redshift is negligible.
    # In the case of a high-redshift galaxy one should de-redshift its
    # wavelength to the rest frame before using the line below as described
    # in PPXF_KINEMATICS_EXAMPLE_SAURON.
    #
    c = 299792.458
    dv = c * np.log(lamRange_temp[0] / wave[0])  # km/s
    vel = c * np.log(1 + z)  # Relation between redshift and velocity in pPXF

    # Here the actual fit starts. The best fit is plotted on the screen.
    #
    # IMPORTANT: Ideally one would like not to use any polynomial in the fit
    # as the continuum shape contains important information on the population.
    # Unfortunately this is often not feasible, due to small calibration
    # uncertainties in the spectral shape. To avoid affecting the line strength of
    # the spectral features, we exclude additive polynomials (DEGREE=-1) and only use
    # multiplicative ones (MDEGREE=10). This is only recommended for population, not
    # for kinematic extraction, where additive polynomials are always recommended.
    #
    start = [vel, 180., 0, 0]  # (km/s), starting guess for [V,sigma]

    t = clock()

    # Assign component=0 to the stellar templates and
    # component=1 to the gas emission lines templates.
    # One can easily assign different kinematic components to different gas species
    # e.g. component=1 for the Balmer series, component=2 for the [OIII] doublet, ...)
    # Input a negative MOMENTS value to keep fixed the LOSVD of a component.
    #
    nTemps = stars_templates.shape[1]
    nLines = gas_templates.shape[1]
    component = [0] * nTemps + [1] * nLines
    moments = [4, 2]  # fit (V,sig,h3,h4) for the stars and (V,sig) for the gas
    start = [start,
             start]  # adopt the same starting value for both gas and stars

    pp = ppxf(templates,
              galaxy,
              noise,
              velscale,
              start,
              plot=False,
              moments=moments,
              degree=-1,
              mdegree=10,
              vsyst=dv,
              clean=False,
              regul=1. / regul_err,
              reg_dim=reg_dim,
              component=component)

    # Plot fit results for stars and gas

    plt.clf()
    plt.subplot(211)
    plt.plot(wave, pp.galaxy, 'k')
    plt.plot(wave, pp.bestfit, 'b', linewidth=2)
    plt.xlabel("Observed Wavelength ($\AA$)")
    plt.ylabel("Relative Flux")
    plt.ylim([-0.1, 1.3])
    plt.xlim([np.min(wave), np.max(wave)])
    plt.plot(wave,
             pp.galaxy - pp.bestfit,
             'd',
             ms=4,
             color='LimeGreen',
             mec='LimeGreen')  # fit residuals
    plt.axhline(y=-0, linestyle='--', color='k', linewidth=2)
    stars = pp.matrix[:, :nTemps].dot(pp.weights[:nTemps])
    plt.plot(wave, stars, 'r', linewidth=2)  # overplot stellar templates alone
    gas = pp.matrix[:, -nLines:].dot(pp.weights[-nLines:])
    plt.plot(wave, gas + 0.15, 'b',
             linewidth=2)  # overplot emission lines alone

    # When the two Delta Chi^2 below are the same, the solution is the smoothest
    # consistent with the observed spectrum.
    #
    print('Desired Delta Chi^2: %.4g' % np.sqrt(2 * galaxy.size))
    print('Current Delta Chi^2: %.4g' % ((pp.chi2 - 1) * galaxy.size))
    print('Elapsed time in PPXF: %.2f s' % (clock() - t))

    w = np.array(component) == 1  # Extract weights of gas emissions only
    print('++++++++++++++++++++++++++++++')
    print('Gas V=%.4g and sigma=%.2g km/s' %
          (pp.sol[1][0], pp.sol[1][1]))  # component=1
    print('Emission lines peak intensity:')
    for name, weight, line in zip(line_names, pp.weights[w], pp.matrix[:,
                                                                       w].T):
        print('%12s: %.3g' % (name, weight * np.max(line)))
    print('------------------------------')

    # Plot stellar population mass distribution

    plt.subplot(212)
    weights = pp.weights[:np.prod(reg_dim)].reshape(reg_dim) / pp.weights.sum()
    plt.imshow(np.rot90(weights),
               interpolation='nearest',
               cmap='gist_heat',
               aspect='auto',
               origin='upper',
               extent=(np.log10(1.0), np.log10(17.7828), -1.9, 0.45))
    plt.colorbar()
    plt.title("Mass Fraction")
    plt.xlabel("log$_{10}$ Age (Gyr)")
    plt.ylabel("[M/H]")
    plt.tight_layout()
    plt.show()
def ppxf_example_two_components():

    file_dir = path.dirname(path.realpath(__file__))  # path of this procedure

    hdu = fits.open(file_dir + '/miles_models/Mun1.30Zp0.00T12.5893.fits'
                    )  # Solar metallicitly, Age=12.59 Gyr
    gal_lin = hdu[0].data
    h1 = hdu[0].header
    lamRange1 = h1['CRVAL1'] + np.array(
        [0., h1['CDELT1'] * (h1['NAXIS1'] - 1)])
    c = 299792.458  # speed of light in km/s
    velscale = c * h1['CDELT1'] / max(
        lamRange1)  # Do not degrade original velocity sampling
    model1, logLam1, velscale = util.log_rebin(lamRange1,
                                               gal_lin,
                                               velscale=velscale)
    model1 /= np.median(model1)

    hdu = fits.open(file_dir + '/miles_models/Mun1.30Zp0.00T01.0000.fits'
                    )  # Solar metallicitly, Age=1.00 Gyr
    gal_lin = hdu[0].data
    model2, logLam1, velscale = util.log_rebin(lamRange1,
                                               gal_lin,
                                               velscale=velscale)
    model2 /= np.median(model2)

    model = np.column_stack([model1, model2])
    galaxy = np.empty_like(model)

    # These are the input values in spectral pixels
    # for the (V,sigma) of the two kinematic components
    #
    vel = np.array([0., 250.]) / velscale
    sigma = np.array([200., 100.]) / velscale

    # The synthetic galaxy model consists of the sum of two
    # SSP spectra with age of 1Gyr and 13Gyr respectively
    # with different velocity and dispersion
    #
    for j in range(len(vel)):
        dx = int(abs(vel[j]) +
                 4. * sigma[j])  # Sample the Gaussian at least to vel+4*sigma
        v = np.linspace(-dx, dx, 2 * dx + 1)
        losvd = np.exp(-0.5 * ((v - vel[j]) / sigma[j])**2)  # Gaussian LOSVD
        losvd /= np.sum(losvd)  # normaize LOSVD
        galaxy[:, j] = signal.fftconvolve(model[:, j], losvd, mode="same")
        galaxy[:, j] /= np.median(model[:, j])
    galaxy = np.sum(galaxy, axis=1)
    sn = 100.
    noise = galaxy / sn
    galaxy = np.random.normal(galaxy, noise)  # add noise to galaxy

    # Adopts two templates per kinematic component
    #
    templates = np.column_stack([model1, model2, model1, model2])

    # Start both kinematic components from the same guess.
    # With multiple stellar kinematic components
    # a good starting guess is essential
    #
    start = [np.mean(vel) * velscale, np.mean(sigma) * velscale]
    start = [start, start]
    goodPixels = np.arange(20, 6000)

    t = clock()

    plt.clf()
    plt.subplot(211)
    plt.title("Two components pPXF fit")
    print("+++++++++++++++++++++++++++++++++++++++++++++")

    pp = ppxf(templates,
              galaxy,
              noise,
              velscale,
              start,
              goodpixels=goodPixels,
              plot=True,
              degree=4,
              moments=[2, 2],
              component=[0, 0, 1, 1])

    plt.subplot(212)
    plt.title("Single component pPXF fit")
    print("---------------------------------------------")

    start = start[0]
    pp = ppxf(templates,
              galaxy,
              noise,
              velscale,
              start,
              goodpixels=goodPixels,
              plot=True,
              degree=4,
              moments=2)

    print("=============================================")
    print("Total elapsed time %.2f s" % (clock() - t))

    plt.tight_layout()
    plt.pause(1)
def ppxf_simulation_example():

    hdu = fits.open('miles_models/Mun1.30Zp0.00T12.5893.fits')  # Solar metallicitly, Age=12.59 Gyr
    ssp = hdu[0].data
    h = hdu[0].header

    lamRange = h['CRVAL1'] + np.array([0.,h['CDELT1']*(h['NAXIS1']-1)])
    c = 299792.458 # speed of light in km/s
    velscale = c*h['CDELT1']/max(lamRange)   # Do not degrade original velocity sampling
    star, logLam, velscale = util.log_rebin(lamRange, ssp, velscale=velscale)

    # The finite sampling of the observed spectrum is modeled in detail:
    # the galaxy spectrum is obtained by oversampling the actual observed spectrum
    # to a high resolution. This represent the true spectrum, which is later resampled
    # to lower resolution to simulate the observations on the CCD. Similarly, the
    # convolution with a well-sampled LOSVD is done on the high-resolution spectrum,
    # and later resampled to the observed resolution before fitting with PPXF.

    factor = 10                    # Oversampling integer factor for an accurate convolution
    starNew = ndimage.interpolation.zoom(star, factor, order=3) # This is the underlying spectrum, known at high resolution
    star = rebin(starNew, factor)        # Make sure that the observed spectrum is the integral over the pixels

    np.random.seed(133)  # for reproducible results

    h3 = 0.1       # Adopted G-H parameters of the LOSVD
    h4 = -0.1
    sn = 30.        # Adopted S/N of the Monte Carlo simulation
    m = 300        # Number of realizations of the simulation
    moments = 4
    velV = np.random.rand(m)  # velocity in *pixels* [=V(km/s)/velScale]
    sigmaV = np.linspace(0.5, 4, m) # Range of sigma in *pixels* [=sigma(km/s)/velScale]

    result = np.zeros((m, moments)) # This will store the results
    t = clock()

    for j, (vel, sigma) in enumerate(zip(velV, sigmaV)):

        dx = int(abs(vel) + 4.0*sigma)   # Sample the Gaussian and GH at least to vel+4*sigma
        x = np.linspace(-dx, dx, 2*dx*factor + 1) # Evaluate the Gaussian using steps of 1/factor pixels.
        w = (x - vel)/sigma
        w2 = w**2
        gauss = np.exp(-0.5*w2)
        gauss /= np.sum(gauss)  # Normalized total(gauss)=1
        h3poly = w*(2.*w2 - 3.)/np.sqrt(3.)           # H3(y)
        h4poly = (w2*(4.*w2 - 12.) + 3.)/np.sqrt(24.) # H4(y)
        losvd = gauss *(1. + h3*h3poly + h4*h4poly)

        galaxy = signal.fftconvolve(starNew, losvd, mode="same") # Convolve the oversampled spectrum
        galaxy = rebin(galaxy, factor) # Integrate spectrum into original spectral pixels
        noise = galaxy/sn        # 1sigma error spectrum
        galaxy = np.random.normal(galaxy, noise) # Add noise to the galaxy spectrum
        start = np.array([vel + np.random.random(), sigma*np.random.uniform(0.85, 1.15), 0, 0])*velscale # Convert to km/s

        pp = ppxf(star, galaxy, noise, velscale, start,
                  goodpixels=np.arange(dx, galaxy.size - dx), plot=False,
                  moments=moments, bias=0.3, oversample=None)
        result[j,:] = pp.sol

    print('Calculation time: %.2f s' % (clock()-t))

    plt.clf()
    plt.subplot(221)
    plt.plot(sigmaV*velscale, (result[:,0]/velscale - velV)/sigmaV, '+k')
    plt.axhline(0, color='r')
    plt.axvline(velscale, linestyle='dashed')
    plt.axvline(2*velscale, linestyle='dashed')
    plt.ylim(-0.3, 0.3)
    plt.xlabel(r'$\sigma_{\rm in}\ (km\ s^{-1})$')
    plt.ylabel(r'$(V - V_{\rm in})/\sigma_{\rm in}$')
    plt.text(2.05*velscale, -0.2, r'2$\times$velscale')

    plt.subplot(222)
    plt.plot(sigmaV*velscale, np.log10(result[:,1]/(velscale*sigmaV)), '+k')
    plt.axhline(0, color='r')
    plt.axvline(velscale, linestyle='dashed')
    plt.axvline(2*velscale, linestyle='dashed')
    plt.ylim(-0.15, 0.15)
    plt.xlabel(r'$\sigma_{in}\ (km\ s^{-1})$')
    plt.ylabel(r'$\log(\sigma/\sigma_{\rm in})$')
    plt.text(2.05*velscale, -0.1, r'2$\times$velscale')

    plt.subplot(223)
    plt.plot(sigmaV*velscale, result[:,2], '+k')
    plt.axhline(h3, color='r')
    plt.axhline(0, linestyle='dotted', color='limegreen')
    plt.axvline(velscale, linestyle='dashed')
    plt.axvline(2*velscale, linestyle='dashed')
    plt.ylim(-0.15+h3, 0.15+h3)
    plt.xlabel(r'$\sigma_{\rm in}\ (km\ s^{-1})$')
    plt.ylabel('$h_3$')
    plt.text(2.05*velscale, h3 - 0.1, r'2$\times$velscale')

    plt.subplot(224)
    plt.plot(sigmaV*velscale, result[:,3], '+k')
    plt.axhline(h4, color='r')
    plt.axhline(0, linestyle='dotted', color='limegreen')
    plt.axvline(velscale, linestyle='dashed')
    plt.axvline(2*velscale, linestyle='dashed')
    plt.ylim(-0.15+h4, 0.15+h4)
    plt.xlabel(r'$\sigma_{\rm in}\ (km\ s^{-1})$')
    plt.ylabel('$h_4$')
    plt.text(2.05*velscale, h4 - 0.1, r'2$\times$velscale')

    plt.tight_layout()
    plt.pause(0.01)
def ppxf_kinematics_example_sauron():

    # Read a galaxy spectrum and define the wavelength range
    #
    dir = 'spectra/'
    file = dir + 'NGC4550_SAURON.fits'

    hdu = pyfits.open(file)
    gal_lin = hdu[0].data
    h1 = hdu[0].header

    lamRange1 = h1['CRVAL1'] + np.array([0.,h1['CDELT1']*(h1['NAXIS1']-1)])
    FWHM_gal = 4.2 # SAURON has an instrumental resolution FWHM of 4.2A.

    # If the galaxy is at a significant redshift (z > 0.03), one would need to apply
    # a large velocity shift in PPXF to match the template to the galaxy spectrum.
    # This would require a large initial value for the velocity (V > 1e4 km/s)
    # in the input parameter START = [V,sig]. This can cause PPXF to stop!
    # The solution consists of bringing the galaxy spectrum roughly to the
    # rest-frame wavelength, before calling PPXF. In practice there is no
    # need to modify the spectrum before the usual LOG_REBIN, given that a
    # red shift corresponds to a linear shift of the log-rebinned spectrum.
    # One just needs to compute the wavelength range in the rest-frame
    # and adjust the instrumental resolution of the galaxy observations.
    # This is done with the following three commented lines:
    #
    # z = 1.23 # Initial estimate of the galaxy redshift
    # lamRange1 = lamRange1/(1+z) # Compute approximate restframe wavelength range
    # FWHM_gal = FWHM_gal/(1+z)   # Adjust resolution in Angstrom

    galaxy, logLam1, velscale = util.log_rebin(lamRange1, gal_lin)
    galaxy = galaxy/np.median(galaxy) # Normalize spectrum to avoid numerical issues
    noise = galaxy*0 + 0.0049           # Assume constant noise per pixel here

    # Read the list of filenames from the Single Stellar Population library
    # by Vazdekis (1999, ApJ, 513, 224). A subset of the library is included
    # for this example with permission. See http://purl.org/cappellari/software
    # for suggestions of more up-to-date stellar libraries.
    #
    vazdekis = glob.glob(dir + 'Rbi1.30z*.fits')
    vazdekis.sort()
    FWHM_tem = 1.8 # Vazdekis spectra have a resolution FWHM of 1.8A.

    # Extract the wavelength range and logarithmically rebin one spectrum
    # to the same velocity scale of the SAURON galaxy spectrum, to determine
    # the size needed for the array which will contain the template spectra.
    #
    hdu = pyfits.open(vazdekis[0])
    ssp = hdu[0].data
    h2 = hdu[0].header
    lamRange2 = h2['CRVAL1'] + np.array([0.,h2['CDELT1']*(h2['NAXIS1']-1)])
    sspNew, logLam2, velscale = util.log_rebin(lamRange2, ssp, velscale=velscale)
    templates = np.empty((sspNew.size,len(vazdekis)))

    # Convolve the whole Vazdekis library of spectral templates
    # with the quadratic difference between the SAURON and the
    # Vazdekis instrumental resolution. Logarithmically rebin
    # and store each template as a column in the array TEMPLATES.

    # Quadratic sigma difference in pixels Vazdekis --> SAURON
    # The formula below is rigorously valid if the shapes of the
    # instrumental spectral profiles are well approximated by Gaussians.
    #
    FWHM_dif = np.sqrt(FWHM_gal**2 - FWHM_tem**2)
    sigma = FWHM_dif/2.355/h2['CDELT1'] # Sigma difference in pixels

    for j in range(len(vazdekis)):
        hdu = pyfits.open(vazdekis[j])
        ssp = hdu[0].data
        ssp = ndimage.gaussian_filter1d(ssp,sigma)
        sspNew, logLam2, velscale = util.log_rebin(lamRange2, ssp, velscale=velscale)
        templates[:,j] = sspNew/np.median(sspNew) # Normalizes templates

    # The galaxy and the template spectra do not have the same starting wavelength.
    # For this reason an extra velocity shift DV has to be applied to the template
    # to fit the galaxy spectrum. We remove this artificial shift by using the
    # keyword VSYST in the call to PPXF below, so that all velocities are
    # measured with respect to DV. This assume the redshift is negligible.
    # In the case of a high-redshift galaxy one should de-redshift its
    # wavelength to the rest frame before using the line below (see above).
    #
    c = 299792.458
    dv = (logLam2[0]-logLam1[0])*c # km/s

    vel = 450. # Initial estimate of the galaxy velocity in km/s
    z = np.exp(vel/c) - 1   # Relation between velocity and redshift in pPXF
    goodPixels = util.determine_goodpixels(logLam1, lamRange2, z)

    # Here the actual fit starts. The best fit is plotted on the screen.
    # Gas emission lines are excluded from the pPXF fit using the GOODPIXELS keyword.
    #
    start = [vel, 180.] # (km/s), starting guess for [V,sigma]
    t = clock()

    pp = ppxf(templates, galaxy, noise, velscale, start,
              goodpixels=goodPixels, plot=True, moments=4,
              degree=4, vsyst=dv)

    print("Formal errors:")
    print("     dV    dsigma   dh3      dh4")
    print("".join("%8.2g" % f for f in pp.error*np.sqrt(pp.chi2)))

    print('Elapsed time in PPXF: %.2f s' % (clock() - t))
示例#26
0
def ppxf_kinematics_gas(bin_sci, ppxf_file, ppxf_bestfit, template_fits, template_res,
                        vel_init=1500., sig_init=100., bias=0.6, plot=False, quiet=True):
    """
    Follow the pPXF usage example by Michile Cappellari
    INPUT: DIR_SCI_COMB (comb_fits_sci_{S/N}/bin_sci_{S/N}.fits), TEMPLATE_* (spectra/Mun1.30z*.fits)
    OUTPUT: PPXF_FILE (ppxf_output_sn30.txt), OUT_BESTFIT_FITS (ppxf_fits/ppxf_bestfit_sn30.fits)
    """

    if os.path.exists(ppxf_file):
        print('File {} already exists'.format(ppxf_file))
        return

    # Read in the galaxy spectrum, logarithmically rebin
    #
    assert len(glob.glob(bin_sci.format('*'))) > 0, 'Binned spectra {} not found'.format(glob.glob(bin_sci.format('*')))
    with fits.open(bin_sci.format(0)) as gal_hdu:
        gal_data = gal_hdu[0].data
        gal_hdr = gal_hdu[0].header
    lamRange1 = gal_hdr['CRVAL1'] + np.array([1. - gal_hdr['CRPIX1'], gal_hdr['NAXIS1'] - gal_hdr['CRPIX1']]) \
                                    * gal_hdr['CD1_1']

    galaxy, logLam1, velscale = util.log_rebin(lamRange1, gal_data)
    galaxy = galaxy / np.median(galaxy)  # Normalize spectrum to avoid numerical issues
    wave = np.exp(logLam1)

    # The noise level is chosen to give Chi^2/DOF=1 without regularization (REGUL=0).
    # A constant error is not a bad approximation in the fitted wavelength
    # range and reduces the noise in the fit.
    #
    noise = galaxy * 0 + 0.01528  # Assume constant noise per pixel here

    c = 299792.458  # speed of light in km/s
    FWHM_gal = 2.3  # GMOS IFU has an instrumental resolution FWHM of 2.3 A

    # stars_templates, lamRange_temp, logLam_temp = setup_spectral_library(velscale, FWHM_gal)

    # Read in template galaxy spectra
    #
    template_spectra = glob.glob(template_fits)
    assert len(template_spectra) > 0, 'Template spectra not found: {}'.format(template_fits)
    with fits.open(template_spectra[0]) as temp_hdu:
        ssp = temp_hdu[0].data
        h2 = temp_hdu[0].header
    lamRange2 = h2['CRVAL1'] + np.array([0., h2['CDELT1'] * (h2['NAXIS1'] - 1)])
    sspNew, logLam2, velscale = util.log_rebin(lamRange2, ssp, velscale=velscale)
    stars_templates = np.empty((sspNew.size, len(template_spectra)))
    FWHM_tem = template_res

    # FWHM_dif = np.sqrt(FWHM_gal ** 2 - template_res ** 2)
    FWHM_dif = np.sqrt(FWHM_tem ** 2 - FWHM_gal ** 2)
    sigma = FWHM_dif / 2.355 / h2['CDELT1']  # SIGMA DIFFERENCE IN PIXELS, 1.078435697220085

    for j in range(len(template_spectra)):
        with fits.open(template_spectra[j]) as temp_hdu_j:
            ssp_j = temp_hdu_j[0].data
        ssp_j = ndimage.gaussian_filter1d(ssp_j, sigma)
        sspNew, logLam2, velscale = util.log_rebin(lamRange2, ssp_j, velscale=velscale)
        stars_templates[:, j] = sspNew / np.median(sspNew)  # Normalizes templates

    # we save the original array dimensions, which are needed to specify the regularization dimensions
    #
    reg_dim = stars_templates.shape[1:]

    # See the pPXF documentation for the keyword REGUL,
    # for an explanation of the following two lines
    #
    regul_err = 0.004  # Desired regularization error

    # Construct a set of Gaussian emission line templates.
    # Estimate the wavelength fitted range in the rest frame.
    #
    z = np.exp(vel_init / c) - 1  # Relation between velocity and redshift in pPXF
    lamRange_gal = np.array([lamRange1[0], lamRange1[-1]]) / (1 + z)
    gas_templates, line_names, line_wave = util.emission_lines(logLam2, lamRange_gal, FWHM_gal)

    # Combines the stellar and gaseous templates into a single array
    # during the PPXF fit they will be assigned a different kinematic COMPONENT value
    #
    templates = np.hstack([stars_templates, gas_templates])


    # Here the actual fit starts. The best fit is plotted on the screen.
    #
    # IMPORTANT: Ideally one would like not to use any polynomial in the fit
    # as the continuum shape contains important information on the population.
    # Unfortunately this is often not feasible, due to small calibration
    # uncertainties in the spectral shape. To avoid affecting the line strength of
    # the spectral features, we exclude additive polynomials (DEGREE=-1) and only use
    # multiplicative ones (MDEGREE=10). This is only recommended for population, not
    # for kinematic extraction, where additive polynomials are always recommended.
    #
    # start = [vel, 180.]  # (km/s), starting guess for [V,sigma]
    start = [vel_init, sig_init]  # (km/s), starting guess for [V,sigma]

    # Assign component=0 to the stellar templates and
    # component=1 to the gas emission lines templates.
    # One can easily assign different kinematic components to different gas species
    # e.g. component=1 for the Balmer series, component=2 for the [OIII] doublet, ...)
    # Input a negative MOMENTS value to keep fixed the LOSVD of a component.
    #
    nTemps = stars_templates.shape[1]
    nLines = gas_templates.shape[1]
    component = [0] * nTemps + [1] * nLines
    moments = [4, 2]  # fit (V,sig,h3,h4) for the stars and (V,sig) for the gas
    start = [start, start]  # adopt the same starting value for both gas and stars

    bin_sci_list = glob.glob(bin_sci.format('*'))

    pp_sol = np.zeros([moments[0] + moments[1], len(bin_sci_list)])
    pp_error = np.zeros([moments[0] + moments[1], len(bin_sci_list)])

    for j in range(len(bin_sci_list)):

        gal_data = fits.getdata(bin_sci.format(j), 0)
        gal_data_new = ndimage.gaussian_filter1d(gal_data, sigma)

        galaxy, logLam1, velscale = util.log_rebin(lamRange1, gal_data_new, velscale=velscale)
        noise = galaxy * 0 + 1  # Assume constant noise per pixel here

        # The galaxy and the template spectra do not have the same starting wavelength.
        # For this reason an extra velocity shift DV has to be applied to the template
        # to fit the galaxy spectrum. We remove this artificial shift by using the
        # keyword VSYST in the call to PPXF below, so that all velocities are
        # measured with respect to DV. This assume the redshift is negligible.
        # In the case of a high-redshift galaxy one should de-redshift its
        # wavelength to the rest frame before using the line below as described
        # in PPXF_KINEMATICS_EXAMPLE_SAURON.
        #
        c = 299792.458
        dv = (logLam2[0] - logLam1[0]) * c  # km/s

        t = clock()

        pp = ppxf(templates, galaxy, noise, velscale, start, plot=plot, moments=moments, degree=-1, mdegree=10,
                  vsyst=dv, clean=False, regul=1. / regul_err, reg_dim=reg_dim, component=component, bias=bias,
                  quiet=quiet)

        # Save the velocity, sigma, h3, h4 information for both stellar and gas to a table
        for k, sol in enumerate(pp.sol[0]):
            pp_sol[k, j] = pp.sol[0][k]
            pp_error[k, j] = pp.error[0][k]
        for k, sol in enumerate(pp.sol[1]):
            pp_sol[k + len(pp.sol[0]), j] = pp.sol[1][k]
            pp_error[k + len(pp.error[0]), j] = pp.error[1][k]

        # Plot fit results for stars and gas
        #
        if plot:
            plt.clf()
            # plt.subplot(211)
            plt.plot(wave, pp.galaxy, 'k')
            plt.plot(wave, pp.bestfit, 'b', linewidth=2)
            plt.xlabel("Observed Wavelength ($\AA$)")
            plt.ylabel("Relative Flux")
            plt.ylim([-0.1, 1.3])
            plt.xlim([np.min(wave), np.max(wave)])
            plt.plot(wave, pp.galaxy - pp.bestfit, 'd', ms=4, color='LimeGreen', mec='LimeGreen')  # fit residuals
            plt.axhline(y=-0, linestyle='--', color='k', linewidth=2)
            stars = pp.matrix[:, :nTemps].dot(pp.weights[:nTemps])
            plt.plot(wave, stars, 'r', linewidth=2)  # overplot stellar templates alone
            gas = pp.matrix[:, -nLines:].dot(pp.weights[-nLines:])
            plt.plot(wave, gas + 0.15, 'b', linewidth=2)  # overplot emission lines alone
            plt.legend()
            plt.show()

            # When the two Delta Chi^2 below are the same, the solution is the smoothest
            # consistent with the observed spectrum.
            #
            print('Desired Delta Chi^2: %.4g' % np.sqrt(2 * galaxy.size))
            print('Current Delta Chi^2: %.4g' % ((pp.chi2 - 1) * galaxy.size))
            print('Elapsed time in PPXF: %.2f s' % (clock() - t))

            w = np.where(np.array(component) == 1)[0]  # Extract weights of gas emissions
            print('++++++++++++++++++++++++++++++')
            print('Gas V=%.4g and sigma=%.2g km/s' % (pp.sol[1][0], pp.sol[1][1]))
            print('Emission lines peak intensity:')
            for name, weight, line in zip(line_names, pp.weights[w], pp.matrix[:, w].T):
                print('%12s: %.3g' % (name, weight * np.max(line)))
            print('------------------------------')

            # Plot stellar population mass distribution

            # plt.subplot(212)
            # weights = pp.weights[:np.prod(reg_dim)].reshape(reg_dim) / pp.weights.sum()
            # plt.imshow(np.rot90(weights), interpolation='nearest', cmap='gist_heat', aspect='auto', origin='upper',
            # extent=(np.log10(1.0), np.log10(17.7828), -1.9, 0.45))
            # plt.colorbar()
            # plt.title("Mass Fraction")
            # plt.xlabel("log$_{10}$ Age (Gyr)")
            # plt.ylabel("[M/H]")
            # plt.tight_layout()

            plt.legend()
            plt.show()

        if not quiet:
            print("Formal errors:")
            print("     dV    dsigma   dh3      dh4")
            print("".join("%8.2g" % f for f in pp.error * np.sqrt(pp.chi2)))

        hdu_best = fits.PrimaryHDU()
        hdu_best.data = pp.bestfit
        hdu_best.header['CD1_1'] = gal_hdr['CD1_1']
        hdu_best.header['CDELT1'] = gal_hdr['CD1_1']
        hdu_best.header['CRPIX1'] = gal_hdr['CRPIX1']
        hdu_best.header['CRVAL1'] = gal_hdr['CRVAL1']
        hdu_best.header['NAXIS1'] = pp.bestfit.size
        hdu_best.header['CTYPE1'] = 'LINEAR'  # corresponds to sampling of values above
        hdu_best.header['DC-FLAG'] = '1'  # 0 = linear, 1= log-linear sampling
        hdu_best.writeto(ppxf_bestfit.format(j), clobber=True)

    np.savetxt(ppxf_file,
               np.column_stack(
                   [pp_sol[0], pp_error[0], pp_sol[1], pp_error[1], pp_sol[2], pp_error[2],
                    pp_sol[3], pp_error[3], pp_sol[4], pp_error[4], pp_sol[5], pp_error[5]]),
               fmt=b'%10.4f  %10.4f  %10.4f  %10.4f  %10.4f  %10.4f  %10.4f  %10.4f %10.4f  %10.4f  %10.4f  %10.4f',
               header='Stellar pop:                                          Gas: \n'
                      'vel  d_vel  sigma  d_sigma  h3  d_h3  h4  d_h4        vel  d_vel  sigma  d_sigma')
示例#27
0
def ppxf_simulation(ppxf_bestfit, lam_range, target_sn, bias=0.6, spaxel=0):
    """
    2. Perform a fit of your kinematics *without* penalty (PPXF keyword BIAS=0).
       The solution will be noisy and may be affected by spurious solutions,
       however this step will allow you to check the expected mean ranges in
       the Gauss-Hermite parameters [h3,h4] for the galaxy under study;

        see ppxf_output_sn30_bias0.txt
        mean(h3), std(h3), max(h3) = -0.2555, 0.09090, 0.007468
         # ignoring endpoints, max(h3[1:-1]) = -0.01376
        mean(h4), std(h4), max(h4) = -0.07712, 0.1423, 0.136607
         # ignoring endpoints, max(h4[1,-1]) = 0.13594
        max(dvel), min(dvel), max(dvel)-np.min(dvel) = 119.2918, 4.08643, 115.20544
        mean(vel) = 1543.0359
        max(sig), min(sig) = 180.3, 36.23



    3. Perform a Monte Carlo simulation of your spectra, following e.g. the
       included ppxf_simulation_example.pro routine. Adopt as S/N in the simulation
       the chosen value (S/N)_min and as input [h3,h4] the maximum representative
       values measured in the non-penalized pPXF fit of the previous step;

    4. Choose as penalty (BIAS) the *largest* value such that, for sigma > 3*velScale,
       the mean difference between the output [h3,h4] and the input [h3,h4]
       is well within the rms scatter of the simulated values
       (see e.g. Fig.2 of Emsellem et al. 2004, MNRAS, 352, 721).
    """

    # dir = 'spectra/'
    # file = dir + 'Rbi1.30z+0.00t12.59.fits'
    # hdu = pyfits.open(file)
    # ssp = hdu[0].data
    # h = hdu[0].header

    bestfit_file = ppxf_bestfit.format(spaxel)
    assert os.path.exists(bestfit_file), 'Best fit spectra not found: {}'.format(bestfit_file)

    with fits.open(bestfit_file) as best_hdu:
        ssp = best_hdu[0].data
        h = best_hdu[0].header

    # lamRange = h['CRVAL1'] + np.array([0., h['CDELT1'] * (h['NAXIS1'] - 1)])
    # star, logLam, velscale = util.log_rebin(lamRange, ssp)

    star, logLam, velscale = util.log_rebin(lam_range, ssp)

    # The finite sampling of the observed spectrum is modeled in detail: the galaxy spectrum is obtained by oversampling
    # the actual observed spectrum to a high resolution. This represents the true spectrum, which is later resampled
    # to lower resolution to simulate the observations on the CCD. Similarly, the convolution with a well-sampled LOSVD
    # is done on the high-resolution spectrum, and later resampled to the observed resolution before fitting with PPXF.

    factor = 10  # Oversampling integer factor for an accurate convolution
    starNew = ndimage.interpolation.zoom(star, factor, order=1)  # The underlying spectrum, known at high resolution
    star = rebin(starNew, factor)  # Make sure that the observed spectrum is the integral over the pixels

    # vel = 0.3  # velocity in *pixels* [=V(km/s)/velScale]
    # h3 = 0.1  # Adopted G-H parameters of the LOSVD
    # h4 = -0.1
    # sn = 60.  # Adopted S/N of the Monte Carlo simulation
    # m = 300  # Number of realizations of the simulation
    # sigmaV = np.linspace(0.8, 4, m)  # Range of sigma in *pixels* [=sigma(km/s)/velScale]

    print('bestfit {} : velscale = {}'.format(spaxel, velscale))
    # bestfit 0 : velscale = 57.44245804
    # max(sig), min(sig) = 164.026343, 11.306016 [km/s] =  2.87765514,  0.19835116 [pix]

    vel = 0.3  # mean vel = 1556.4989, I have no idea why this value is 0.3 ...
    h3 = -0.003146
    h4 = -0.003662
    sn = target_sn
    m = 300  # Number of realizations of the simulation
    sigmaV = np.linspace(0.8, 4, m)  # Range of sigma in *pixels* [=sigma(km/s)/velScale]
    # sigmaV = np.linspace(0.198, 2.877, m)

    result = np.zeros((m, 4))  # This will store the results
    t = clock()
    np.random.seed(123)  # for reproducible results

    for j in range(m):
        sigma = sigmaV[j]
        dx = int(abs(vel) + 4.0 * sigma)  # Sample the Gaussian and GH at least to vel+4*sigma
        x = np.linspace(-dx, dx, 2 * dx * factor + 1)  # Evaluate the Gaussian using steps of 1/factor pixels.
        w = (x - vel) / sigma
        w2 = w ** 2
        gauss = np.exp(-0.5 * w2) / (np.sqrt(2. * np.pi) * sigma * factor)  # Normalized total(gauss)=1
        h3poly = w * (2. * w2 - 3.) / np.sqrt(3.)  # H3(y)
        h4poly = (w2 * (4. * w2 - 12.) + 3.) / np.sqrt(24.)  # H4(y)
        losvd = gauss * (1. + h3 * h3poly + h4 * h4poly)

        galaxy = signal.fftconvolve(starNew, losvd, mode="same")  # Convolve the oversampled spectrum
        galaxy = rebin(galaxy, factor)  # Integrate spectrum into original spectral pixels
        noise = galaxy / sn  # 1sigma error spectrum
        galaxy = np.random.normal(galaxy, noise)  # Add noise to the galaxy spectrum
        start = np.array(
            [vel + np.random.random(), sigma * np.random.uniform(0.85, 1.15)]) * velscale  # Convert to km/s

        pp = ppxf(star, galaxy, noise, velscale, start, goodpixels=np.arange(dx, galaxy.size - dx),
                  plot=False, moments=4, bias=bias, quiet=True)
        result[j, :] = pp.sol

    print('Calculation time: %.2f s' % (clock() - t))

    plt.clf()
    plt.subplot(221)
    plt.plot(sigmaV * velscale, result[:, 0] - vel * velscale, '+k')
    plt.plot(sigmaV * velscale, np.ones(len(sigmaV * velscale)) * np.mean(result[:, 0] - vel * velscale), '-b')
    plt.plot(sigmaV * velscale, sigmaV * velscale * 0, '-r')
    plt.ylim(-40, 40)
    plt.xlabel('$\sigma_{in}\ (km\ s^{-1})$')
    plt.ylabel('$V - V_{in}\ (km\ s^{-1}$)')

    plt.subplot(222)
    plt.plot(sigmaV * velscale, result[:, 1] - sigmaV * velscale, '+k')
    plt.plot(sigmaV * velscale, np.ones(len(sigmaV * velscale)) * np.mean(result[:, 1] - sigmaV * velscale), '-b')
    plt.plot(sigmaV * velscale, sigmaV * velscale * 0, '-r')
    plt.ylim(-40, 40)
    plt.xlabel('$\sigma_{in}\ (km\ s^{-1})$')
    plt.ylabel('$\sigma - \sigma_{in}\ (km\ s^{-1}$)')

    plt.subplot(223)
    plt.plot(sigmaV * velscale, result[:, 2], '+k')
    plt.plot(sigmaV * velscale, sigmaV * velscale * 0 + h3, '-r')
    plt.plot(sigmaV * velscale, np.ones(len(sigmaV * velscale)) * np.mean(result[:, 2]), '-b')
    plt.ylim(-0.2 + h3, 0.2 + h3)
    plt.xlabel('$\sigma_{in}\ (km\ s^{-1})$')
    plt.ylabel('$h_3$')

    plt.subplot(224)
    plt.plot(sigmaV * velscale, result[:, 3], '+k')
    plt.plot(sigmaV * velscale, sigmaV * velscale * 0 + h4, '-r')
    plt.plot(sigmaV * velscale, np.ones(len(sigmaV * velscale)) * np.mean(result[:, 3]), '-b')
    plt.ylim(-0.2 + h4, 0.2 + h4)
    plt.xlabel('$\sigma_{in}\ (km\ s^{-1})$')
    plt.ylabel('$h_4$')

    plt.tight_layout()
    plt.show()
示例#28
0
def ppxf_simulation_example():

    dir = 'spectra/'
    file = dir + 'Rbi1.30z+0.00t12.59.fits'
    hdu = pyfits.open(file)
    ssp = hdu[0].data
    h = hdu[0].header

    lamRange = h['CRVAL1'] + np.array([0.,h['CDELT1']*(h['NAXIS1']-1)])
    star, logLam, velscale = util.log_rebin(lamRange, ssp)

    # The finite sampling of the observed spectrum is modeled in detail:
    # the galaxy spectrum is obtained by oversampling the actual observed spectrum
    # to a high resolution. This represent the true spectrum, which is later resampled
    # to lower resolution to simulate the observations on the CCD. Similarly, the
    # convolution with a well-sampled LOSVD is done on the high-resolution spectrum,
    # and later resampled to the observed resolution before fitting with PPXF.

    factor = 10                    # Oversampling integer factor for an accurate convolution
    starNew = ndimage.interpolation.zoom(star,factor,order=1) # This is the underlying spectrum, known at high resolution
    star = rebin(starNew,factor)        # Make sure that the observed spectrum is the integral over the pixels

    vel = 0.3      # velocity in *pixels* [=V(km/s)/velScale]
    h3 = 0.1       # Adopted G-H parameters of the LOSVD
    h4 = -0.1
    sn = 60.        # Adopted S/N of the Monte Carlo simulation
    m = 300        # Number of realizations of the simulation
    sigmaV = np.linspace(0.8,4,m) # Range of sigma in *pixels* [=sigma(km/s)/velScale]

    result = np.zeros((m,4)) # This will store the results
    t = clock()
    np.random.seed(123) # for reproducible results

    for j in range(m):

        sigma = sigmaV[j]
        dx = int(abs(vel)+4.0*sigma)   # Sample the Gaussian and GH at least to vel+4*sigma
        x = np.linspace(-dx,dx,2*dx*factor+1) # Evaluate the Gaussian using steps of 1/factor pixels.
        w = (x - vel)/sigma
        w2 = w**2
        gauss = np.exp(-0.5*w2)/(np.sqrt(2.*np.pi)*sigma*factor) # Normalized total(gauss)=1
        h3poly = w*(2.*w2 - 3.)/np.sqrt(3.)           # H3(y)
        h4poly = (w2*(4.*w2 - 12.) + 3.)/np.sqrt(24.) # H4(y)
        losvd = gauss *(1. + h3*h3poly + h4*h4poly)

        galaxy = signal.fftconvolve(starNew,losvd,mode="same") # Convolve the oversampled spectrum
        galaxy = rebin(galaxy,factor) # Integrate spectrum into original spectral pixels
        noise = galaxy/sn        # 1sigma error spectrum
        galaxy = np.random.normal(galaxy, noise) # Add noise to the galaxy spectrum
        start = np.array([vel+np.random.random(), sigma*np.random.uniform(0.85,1.15)])*velscale # Convert to km/s

        pp = ppxf(star, galaxy, noise, velscale, start,
                  goodpixels=np.arange(dx,galaxy.size-dx),
                  plot=False, moments=4, bias=0.5)
        result[j,:] = pp.sol

    print('Calculation time: %.2f s' % (clock()-t))

    plt.clf()
    plt.subplot(221)
    plt.plot(sigmaV*velscale, result[:,0]-vel*velscale, '+k')
    plt.plot(sigmaV*velscale, sigmaV*velscale*0, '-r')
    plt.ylim(-40, 40)
    plt.xlabel('$\sigma_{in}\ (km\ s^{-1})$')
    plt.ylabel('$V - V_{in}\ (km\ s^{-1}$)')

    plt.subplot(222)
    plt.plot(sigmaV*velscale, result[:,1]-sigmaV*velscale, '+k')
    plt.plot(sigmaV*velscale, sigmaV*velscale*0, '-r')
    plt.ylim(-40, 40)
    plt.xlabel('$\sigma_{in}\ (km\ s^{-1})$')
    plt.ylabel('$\sigma - \sigma_{in}\ (km\ s^{-1}$)')

    plt.subplot(223)
    plt.plot(sigmaV*velscale, result[:,2], '+k')
    plt.plot(sigmaV*velscale, sigmaV*velscale*0+h3, '-r')
    plt.ylim(-0.2+h3, 0.2+h3)
    plt.xlabel('$\sigma_{in}\ (km\ s^{-1})$')
    plt.ylabel('$h_3$')

    plt.subplot(224)
    plt.plot(sigmaV*velscale, result[:,3], '+k')
    plt.plot(sigmaV*velscale, sigmaV*velscale*0+h4, '-r')
    plt.ylim(-0.2+h4, 0.2+h4)
    plt.xlabel('$\sigma_{in}\ (km\ s^{-1})$')
    plt.ylabel('$h_4$')

    plt.tight_layout()
    plt.pause(0.01)
示例#29
0
def interface_sets(nstart, nend):

    full_time = clock()

    ####### setting up some variables for magnitudes and indices calculation ########

    c = 299792.458

    filter_list = 'sdss_manga_pipeline.res'
    zero_point = 'AB'
    redshift = 0.0

    indices_list = 'MaNGA_range.def'

    print('     > gathering info')

    ######## setting up directories and files ########

    data_dir = '/mnt/lustre/smg/stellar_libraries/MaNGA/'
    output_dir = '/mnt/lustre/smg/stellar_libraries/MaNGA/parameters/she-ra/beta_tests/combo_test/'

    file_spectra = 'MaStar_spectra_ebv_gaia_rb_isopars'

    ##### templates information #####

    templates_dir = '/mnt/lustre/smg/stellar_libraries/atlas9marcs/'
    templates_parameters = 'templates_grid_parameters_R2000.fits'

    header = fits.open(templates_dir + templates_parameters)

    templ_id, templ_teff, templ_logg, templ_metal = header[1].data[
        'NAME'], header[1].data['TEFF'], header[1].data['LOGG'], header[
            1].data['METAL']
    templ_gmag, templa_rmag, templ_imag = header[1].data['Gmag'], header[
        1].data['Rmag'], header[1].data['Imag']

    tot_templ = len(templ_id)

    estimate_teff = interp1d(templ_gmag - templ_imag,
                             templ_teff,
                             bounds_error=False,
                             fill_value='extrapolate')

    ######## reading spectra file ########

    print('     > reading information from the spectra file')

    header = fits.open(data_dir + 'spectra/' + file_spectra + '.fits')

    mangaid, mastarid, starid = header[1].data['mangaid'], header[1].data[
        'mastarid'], header[1].data['id']
    plate, ifudesign, mjd = header[1].data['plate'], header[1].data[
        'ifudesign'], header[1].data['mjd']
    ra, dec = header[1].data['objra'], header[1].data['objdec']
    wave, flux = header[1].data['wave'], header[1].data['flux']

    minlogg = header[1].data['minlogg']
    bad_info = np.where(minlogg <= -8)
    minlogg[bad_info] = 'NaN'

    nspectra = int(header[1].header['naxis2'])

    naxis1 = int(np.shape(wave)[1])

    crval1 = min(wave[0, :])
    crval2 = max(wave[0, :])
    cdelt1 = abs(crval1 - crval2) / naxis1

    new_wave = np.linspace(start=crval1, stop=crval2, num=naxis1)

    ######## setting up arrays ########

    parameters = np.zeros((nspectra, 9))
    selected_parameters = np.zeros((nspectra, 3))
    ppxf_info = np.zeros((nspectra, 3))

    indices = np.zeros((nspectra, 42))
    mags = np.zeros((nspectra, 5))

    #############################################################################################################################################

    ######## running things for each spectrum ########

    print('     > working on each spectrum')

    for i in range(nstart, nend + 1):

        start_time = clock()

        new_flux = np.interp(new_wave, wave[i, :], flux[i, :])
        new_flux = new_flux * 1e-17  # to have 10^-17 erg/s/cm2/Angstrom as units

        ######## calculating the E(B-V) from Schlegel+98 as a function of coordinates ########

        print('          > calculating the E(B-V) from Schlegel+98')
        ebv = get_dust_radec(ra[i], dec[i], 'ebv')

        ######## calculating the reddening as a function of wavelength ########

        print('          > calculating the reddening')
        reddening = dust_allen_py(ebv, new_wave)

        ######## in order to correct, the flux needs to be divided by the reddening ########

        corrected_flux = new_flux / reddening

        bad_flux = np.isnan(corrected_flux) | np.isinf(corrected_flux) | (
            corrected_flux <= 0.0)
        corrected_flux[bad_flux] = 0.0

        ######## calculating the indices ########
        print(
            '          > calculating indices within the MaNGA wavelength range'
        )
        indices[i, :], err_indices = calculate_indices(new_wave,
                                                       corrected_flux,
                                                       indices_list,
                                                       mastarid[i],
                                                       ef=0,
                                                       rv=0,
                                                       plot=False,
                                                       sim=False)

        ######## calculating the magnitudes ########
        print('          > calculating SDSS magnitudes')
        mags[i, :] = calculate_magnitudes(new_wave, corrected_flux,
                                          filter_list, zero_point, redshift)

        ######## calculating the first guess for the stellar parameters ########

        print(
            '          > calculating the first set of teff, logg, and metallicity'
        )

        parameters[i, 0] = estimate_teff(
            mags[i, 1] - mags[i, 3])  # Teff as a function of (g-i)
        parameters[i, 1] = minlogg[i]  # from isoparsfit - isochrone fitting
        parameters[i, 2] = -1.0  # open metallicity

        bad_data = np.isnan(parameters[i, 0]) | np.isinf(
            parameters[i, 0]) | np.isnan(parameters[i, 1]) | np.isinf(
                parameters[i, 1]) | np.isnan(parameters[i, 2]) | np.isinf(
                    parameters[i, 2])
        if bad_data:
            parameters[i, :] = -999
            ppxf_info[i, :] = -999
            ##### making a preliminary output file #####
            print('          > making a preliminary output file')
            f = open(output_dir + 'prelim_output/' + mastarid[i] + '_she-ra',
                     'w')
            f.write('%r %r %r %i %i %i %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n' \
                    %(mangaid[i], mastarid[i], starid[i], \
                    plate[i], ifudesign[i], mjd[i], \
                    ra[i],dec[i], \
                    parameters[i,0],parameters[i,1],parameters[i,2], \
                    parameters[i,3],parameters[i,4],parameters[i,5], \
                    parameters[i,6],parameters[i,7],parameters[i,8], \
                    ppxf_info[i,0], ppxf_info[i,1], ppxf_info[i,2], \
                    mags[i,0], mags[i,1], mags[i,2], mags[i,3], mags[i,4], \
                    indices[i,0], indices[i,1], indices[i,2], indices[i,3], indices[i,4], \
                    indices[i,5], indices[i,6], indices[i,7], indices[i,8], indices[i,9], \
                    indices[i,10], indices[i,11], indices[i,12], indices[i,13], indices[i,14], \
                    indices[i,15], indices[i,16], indices[i,17], indices[i,18], indices[i,19], \
                    indices[i,20], indices[i,21], indices[i,22], indices[i,23], indices[i,24], \
                    indices[i,25], indices[i,26], indices[i,27], indices[i,28], indices[i,29], \
                    indices[i,30], indices[i,31], indices[i,32], indices[i,33], indices[i,34], \
                    indices[i,35], indices[i,36], indices[i,37], indices[i,38], indices[i,39], \
                    indices[i,40], indices[i,41]))
            f.close()
            print('     > spectrum ' + str(i) + ' done')
            continue

        print('               > Teff  = ' + str(parameters[i, 0]) + ' K')
        print('               > logg  = ' + str(parameters[i, 1]) + ' dex')
        print('               > metal = ' + str(parameters[i, 2]) +
              ' dex')  # not a real prior

        ######## calculating the second guess for the stellar parameters ########

        if (parameters[i, 0] >= 12000): teff_guess = 2000
        if (parameters[i, 0] < 12000): teff_guess = 1000

        logg_guess = 0.80  # fixed value
        metal_guess = 2.00  # open metallicity

        selected_parameters[i, 0] = parameters[i, 0]
        selected_parameters[i, 1] = parameters[i, 1]
        selected_parameters[i, 2] = parameters[i, 2]

        if (parameters[i, 0] <= min(templ_teff)):
            selected_parameters[i, 0] = min(templ_teff) + teff_guess
        if (parameters[i, 0] >= max(templ_teff)):
            selected_parameters[i, 0] = max(templ_teff) - teff_guess

        if ((parameters[i, 0] >= 12000) & (parameters[i, 1] <= 3.5)):
            selected_parameters[i, 1] = 3.5
        if ((parameters[i, 0] >= 8000) & (parameters[i, 0] <= 12000) &
            (parameters[i, 1] <= 2.0)):
            selected_parameters[i, 1] = 2.5
        if ((parameters[i, 0] >= 6000) & (parameters[i, 0] <= 8000) &
            (parameters[i, 1] <= 1.0)):
            selected_parameters[i, 1] = 1.5
        if ((parameters[i, 0] >= 2500) & (parameters[i, 0] <= 6000) &
            (parameters[i, 1] <= 0.0)):
            selected_parameters[i, 1] = 0.0
        if ((parameters[i, 0] >= 2500) & (parameters[i, 0] <= 12000) &
            (parameters[i, 1] >= 5.0)):
            selected_parameters[i, 1] = 5.0

        if (parameters[i, 2] <= min(templ_metal)):
            selected_parameters[i, 2] = min(templ_metal) + metal_guess
        if (parameters[i, 2] >= max(templ_metal)):
            selected_parameters[i, 2] = max(templ_metal) - metal_guess

        ok_set = np.where(
            (np.abs(templ_teff - selected_parameters[i, 0]) <= teff_guess)
            & (np.abs(templ_logg - selected_parameters[i, 1]) <= logg_guess)
            & (np.abs(templ_metal - selected_parameters[i, 2]) <= metal_guess))

        templ_set_id = templ_id[ok_set]
        templ_set_teff = templ_teff[ok_set]
        templ_set_logg = templ_logg[ok_set]
        templ_set_metal = templ_metal[ok_set]

        if not templ_set_id.size:
            parameters[i, 3] = -999
            parameters[i, 4] = -999
            parameters[i, 5] = -999
            parameters[i, 6] = -999
            parameters[i, 7] = -999
            parameters[i, 8] = -999
            ##### making a preliminary output file #####
            print('          > making a preliminary output file')
            f = open(output_dir + 'prelim_output/' + mastarid[i] + '_she-ra',
                     'w')
            f.write('%r %r %r %i %i %i %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n' \
                    %(mangaid[i], mastarid[i], starid[i], \
                    plate[i], ifudesign[i], mjd[i], \
                    ra[i],dec[i], \
                    parameters[i,0],parameters[i,1],parameters[i,2], \
                    parameters[i,3],parameters[i,4],parameters[i,5], \
                    parameters[i,6],parameters[i,7],parameters[i,8], \
                    ppxf_info[i,0], ppxf_info[i,1], ppxf_info[i,2], \
                    mags[i,0], mags[i,1], mags[i,2], mags[i,3], mags[i,4], \
                    indices[i,0], indices[i,1], indices[i,2], indices[i,3], indices[i,4], \
                    indices[i,5], indices[i,6], indices[i,7], indices[i,8], indices[i,9], \
                    indices[i,10], indices[i,11], indices[i,12], indices[i,13], indices[i,14], \
                    indices[i,15], indices[i,16], indices[i,17], indices[i,18], indices[i,19], \
                    indices[i,20], indices[i,21], indices[i,22], indices[i,23], indices[i,24], \
                    indices[i,25], indices[i,26], indices[i,27], indices[i,28], indices[i,29], \
                    indices[i,30], indices[i,31], indices[i,32], indices[i,33], indices[i,34], \
                    indices[i,35], indices[i,36], indices[i,37], indices[i,38], indices[i,39], \
                    indices[i,40], indices[i,41]))
            f.close()
            print('     > spectrum ' + str(i) + ' done')
            continue

        ##### setting up the MaStar for pPXF #####

        velscale = c * cdelt1 / max(
            new_wave)  # do not degrade original velocity sampling
        log_corrected_flux, log_new_wave, velscale = util.log_rebin(
            [min(new_wave), max(new_wave)], corrected_flux, velscale=velscale)

        ##### setting up the selected templates #####

        templ_set = len(templ_set_id)
        templates = np.zeros((len(log_corrected_flux), templ_set))

        for j in range(0, templ_set):
            hdu = fits.open(templates_dir + 'grid/' + templ_set_id[j] +
                            '.fits')
            template_wave = np.linspace(start=hdu[0].header['CRVAL1'],
                                        stop=hdu[0].header['CRVAL2'],
                                        num=hdu[0].header['NAXIS1'])
            template_flux = np.interp(new_wave, template_wave, hdu[0].data)

            bad_flux = np.isnan(template_flux) | np.isinf(template_flux) | (
                template_flux <= 0.0)
            template_flux[bad_flux] = 0.0

            log_template_flux, log_template_wave, velscale = util.log_rebin(
                [min(new_wave), max(new_wave)],
                template_flux,
                velscale=velscale)
            log_template_flux /= np.median(log_template_flux)

            templates[:, j] = log_template_flux

        ##### setting up the variables to run pPXF #####

        start = [0, 10]

        noise = np.ones_like(log_corrected_flux)

        sol = ppxf(templates,
                   log_corrected_flux,
                   noise,
                   velscale,
                   start,
                   lam=np.exp(log_new_wave),
                   degree=10,
                   moments=2,
                   quiet=True)

        sol.plot()
        plt.title(mastarid[i])
        #plt.show()
        plt.savefig(output_dir + 'fsf/' + mastarid[i] + '_bestfit.png')
        plt.close()

        ##### gathering the chi2, vel and sigma from the pPXF output #####

        print(
            '          > gathering the information from the full-spectrum fitting'
        )

        ppxf_info[i, 0] = sol.chi2
        ppxf_info[i, 1] = sol.sol[0]
        ppxf_info[i, 2] = sol.sol[1]

        print('               > chi2/dof  = ' + str(ppxf_info[i, 0]))
        print('               > vel       = ' + str(ppxf_info[i, 1]) +
              ' km s-1')
        print('               > sigma     = ' + str(ppxf_info[i, 2]) +
              ' km s-1')

        prelim_teff = []
        prelim_logg = []
        prelim_metal = []

        ppxf_weights = sol.weights[:]

        ##### calculating the weighted parameters #####

        for j in range(0, templ_set):
            prelim_teff.append(sol.weights[j] * templ_set_teff[j] /
                               np.sum(sol.weights[:]))
            prelim_logg.append(sol.weights[j] * templ_set_logg[j] /
                               np.sum(sol.weights[:]))
            prelim_metal.append(sol.weights[j] * templ_set_metal[j] /
                                np.sum(sol.weights[:]))

        parameters[i, 3] = np.sum(prelim_teff[:])
        parameters[i, 4] = np.sum(prelim_logg[:])
        parameters[i, 5] = np.sum(prelim_metal[:]) + 0.3

        bad_data = np.isnan(parameters[i, 3]) | np.isinf(
            parameters[i, 3]) | np.isnan(parameters[i, 4]) | np.isinf(
                parameters[i, 4]) | np.isnan(parameters[i, 5]) | np.isinf(
                    parameters[i, 5])
        if bad_data:
            parameters[i, 3] = -999
            parameters[i, 4] = -999
            parameters[i, 5] = -999
            parameters[i, 6] = -999
            parameters[i, 7] = -999
            parameters[i, 8] = -999
            ##### making a preliminary output file #####
            print('          > making a preliminary output file')
            f = open(output_dir + 'prelim_output/' + mastarid[i] + '_she-ra',
                     'w')
            f.write('%r %r %r %i %i %i %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n' \
                    %(mangaid[i], mastarid[i], starid[i], \
                    plate[i], ifudesign[i], mjd[i], \
                    ra[i],dec[i], \
                    parameters[i,0],parameters[i,1],parameters[i,2], \
                    parameters[i,3],parameters[i,4],parameters[i,5], \
                    parameters[i,6],parameters[i,7],parameters[i,8], \
                    ppxf_info[i,0], ppxf_info[i,1], ppxf_info[i,2], \
                    mags[i,0], mags[i,1], mags[i,2], mags[i,3], mags[i,4], \
                    indices[i,0], indices[i,1], indices[i,2], indices[i,3], indices[i,4], \
                    indices[i,5], indices[i,6], indices[i,7], indices[i,8], indices[i,9], \
                    indices[i,10], indices[i,11], indices[i,12], indices[i,13], indices[i,14], \
                    indices[i,15], indices[i,16], indices[i,17], indices[i,18], indices[i,19], \
                    indices[i,20], indices[i,21], indices[i,22], indices[i,23], indices[i,24], \
                    indices[i,25], indices[i,26], indices[i,27], indices[i,28], indices[i,29], \
                    indices[i,30], indices[i,31], indices[i,32], indices[i,33], indices[i,34], \
                    indices[i,35], indices[i,36], indices[i,37], indices[i,38], indices[i,39], \
                    indices[i,40], indices[i,41]))
            f.close()
            print('     > spectrum ' + str(i) + ' done')
            continue

        print(
            '          > calculating the second set of teff, logg, and metallicity'
        )

        print('               > Teff  = ' + str(parameters[i, 3]) + ' K')
        print('               > logg  = ' + str(parameters[i, 4]) + ' dex')
        print('               > metal = ' + str(parameters[i, 5]) + ' dex')

        ##### gathering the parameters of the template with the largest weight #####

        print(
            '          > calculating the third set of teff, logg, and metallicity'
        )

        ok_heavy = np.where(ppxf_weights == max(ppxf_weights))

        parameters[i, 6] = templ_set_teff[ok_heavy]
        parameters[i, 7] = templ_set_logg[ok_heavy]
        parameters[i, 8] = templ_set_metal[ok_heavy] + 0.3

        print('               > Teff  = ' + str(parameters[i, 6]) + ' K')
        print('               > logg  = ' + str(parameters[i, 7]) + ' dex')
        print('               > metal = ' + str(parameters[i, 8]) + ' dex')

        print('          > elapsed time for one MaStar %.2f s' %
              (clock() - start_time))

        ##### making a preliminary output file #####

        print('          > making a preliminary output file')
        f = open(output_dir + 'prelim_output/' + mastarid[i] + '_she-ra', 'w')
        f.write('%r %r %r %i %i %i %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n' \
                %(mangaid[i], mastarid[i], starid[i], \
                plate[i], ifudesign[i], mjd[i], \
                ra[i],dec[i], \
                parameters[i,0],parameters[i,1],parameters[i,2], \
                parameters[i,3],parameters[i,4],parameters[i,5], \
                parameters[i,6],parameters[i,7],parameters[i,8], \
                ppxf_info[i,0], ppxf_info[i,1], ppxf_info[i,2], \
                mags[i,0], mags[i,1], mags[i,2], mags[i,3], mags[i,4], \
                indices[i,0], indices[i,1], indices[i,2], indices[i,3], indices[i,4], \
                indices[i,5], indices[i,6], indices[i,7], indices[i,8], indices[i,9], \
                indices[i,10], indices[i,11], indices[i,12], indices[i,13], indices[i,14], \
                indices[i,15], indices[i,16], indices[i,17], indices[i,18], indices[i,19], \
                indices[i,20], indices[i,21], indices[i,22], indices[i,23], indices[i,24], \
                indices[i,25], indices[i,26], indices[i,27], indices[i,28], indices[i,29], \
                indices[i,30], indices[i,31], indices[i,32], indices[i,33], indices[i,34], \
                indices[i,35], indices[i,36], indices[i,37], indices[i,38], indices[i,39], \
                indices[i,40], indices[i,41]))
        f.close()

        print('     > spectrum ' + str(i) + ' done')
        #sys.stdout.write("     > spectrum = %d %s \r" % (i+1,'done'))
        #sys.stdout.flush()

    print('     > all spectra done')
    print('     > elapsed time for all MaStar %.2f s' % (clock() - full_time))
示例#30
0
def find_best_template(wl_obs, flux, err, hdr, spectral_library):
    import numpy as np
    import matplotlib.pyplot as plt
    from scipy.interpolate import splrep,splev
    from time import clock
    t2 = clock()

    # Read a spectrum and define the wavelength range
    obs_spectrum = flux
    obs_spectrum_header = hdr
    obs_error_spectrum = err

    obs_lambda_range = np.array([min(wl), max(wl)])
    z = 0.0 # Initial estimate of the galaxy redshift
    obs_lambda_range = obs_lambda_range/(1+z) # Compute approximate restframe wavelength range

    #Get median of positive values
    m = np.median(obs_error_spectrum[obs_error_spectrum > 0])
    # Assign the median to the negative elements
    obs_error_spectrum[obs_error_spectrum <= 0] = m




    #logarithmically rebin while conserving flux    
    tell_obs, obs_lambda, velscale = util.log_rebin(obs_lambda_range, obs_spectrum)
    tell_obs_err, obs_lambda, velscale = util.log_rebin(obs_lambda_range, obs_error_spectrum)   
    
    #Normalize to avoid numerical issues
    norm = np.median(tell_obs)
    tell_obs = tell_obs/norm
    tell_obs_err = tell_obs_err/norm

    # Load and prepare Model stellar library
    # Extract the wavelength range and logarithmically rebin one spectrum
    # to the same velocity scale of the target spectrum, to determine
    # the size needed for the array which will contain the template spectra.

    hdu = fits.open(spectral_library[0])
    library_spectrum = hdu[0].data
    library_spectrum_header = hdu[0].header
    wl_lib = np.e**(np.arange((library_spectrum_header['NAXIS1']))*library_spectrum_header['CDELT1']+library_spectrum_header['CRVAL1'])

    #Make empty template holder
    f = splrep(wl_lib,library_spectrum, k=1)
    hdu_wave_short = splev(wl_obs,f)
    lib_lambda_range = np.array([min(wl_obs),max(wl_obs)])
    tell_lib, lib_lambda, velscale = util.log_rebin(lib_lambda_range, hdu_wave_short, velscale=velscale)
    templates = np.empty((tell_lib.size,len(spectral_library)))

    # Convolve the whole library of spectral templates 
    # with the quadratic difference between the target and the 
    # library instrumental resolution. Logarithmically rebin 
    # and store each template as a column in the array TEMPLATES.
    
    for j in range(len(spectral_library)):
        t = clock() 
        hdu = fits.open(spectral_library[j])
        library_spectrum = hdu[0].data

        # Interpolate template spectrum to match input spectrum
        f = splrep(wl_lib,library_spectrum,k=1)
        interpolated_library_spectrum = splev(wl_obs,f)

        # Logarithmically rebin template spectra
        tell_lib, lib_lambda, velscale = util.log_rebin(lib_lambda_range,interpolated_library_spectrum, velscale=velscale)
        templates[:,j] = tell_lib/np.median(tell_lib) # Normalizes templates
        if j % 60 == 0:
            print 'Approximated remaining time (s) for setup of template spectra: '+ str(len(spectral_library)*(clock() - t) -  j*(clock() - t)) + 's' 

    #Excluding areas of strong telluric absorbtion from fitting
    if obs_spectrum_header['HIERARCH ESO SEQ ARM'] == "UVB":        
        mask = (obs_lambda < np.log(5500)) & (obs_lambda > np.log(3100))
        goodPixels = np.where(mask == True)[0]
    elif obs_spectrum_header['HIERARCH ESO SEQ ARM'] == "VIS":
        mask = (obs_lambda > np.log(5500)) & (obs_lambda < np.log(6350)) | (obs_lambda > np.log(6380)) & (obs_lambda < np.log(6860)) | (obs_lambda > np.log(7045)) & (obs_lambda < np.log(7140)) | (obs_lambda > np.log(7355)) & (obs_lambda < np.log(7570)) | (obs_lambda > np.log(7710)) & (obs_lambda < np.log(8090)) | (obs_lambda > np.log(8400)) & (obs_lambda < np.log(8900)) | (obs_lambda > np.log(9900)) & (obs_lambda < np.log(10100))
        goodPixels = np.where(mask == True)[0]     
    elif obs_spectrum_header['HIERARCH ESO SEQ ARM'] == "NIR":   
        mask = (obs_lambda > np.log(10000)) & (obs_lambda < np.log(10950)) | (obs_lambda > np.log(12240)) & (obs_lambda < np.log(12500)) | (obs_lambda > np.log(12800)) & (obs_lambda < np.log(12950)) | (obs_lambda > np.log(15300)) & (obs_lambda < np.log(17100)) | (obs_lambda > np.log(21000)) & (obs_lambda < np.log(23700))
        goodPixels = np.where(mask == True)[0]
        

    # Initial parameters for LOSVD
    c = 299792.458
    dv = 0# (logLam2[0]-logLam1[0])*c # km/s
    vel = 0 # Initial estimate of the LOSVD km/s
    start = [vel, 10.] # (km/s), starting guess for [V,sigma]
    
    # Here the actual fit starts.
    pp = ppxf.ppxf(templates, tell_obs, tell_obs_err, velscale, start,
                       goodpixels=goodPixels, plot=True, moments=4,
                       degree=5, mdegree=5, vsyst=dv, regul = 10)

    print "Formal errors:"    
    print "     dV    dsigma   dh3      dh4"
    print "".join("%8.2g" % f for f in pp.error*np.sqrt(pp.chi2))
    
    print 'elapsed time (s) for ppxf: ', clock() - t2   
    

    
    print 'Best-fitting redshift z:', (z + 1)*(1 + pp.sol/c) - 1
    print 'Best-fitting error on redshift z:',((z + 1)*(1 + pp.sol/c) - 1) - ((z + 1)*(1 + pp.error*np.sqrt(pp.chi2)/c) - 1)


    print 'Rebinning to linear axes'
    import spec   
    obj_spec = spec.resamplespec(wl_obs,np.e**obs_lambda,pp.galaxy, oversamp = 1000)
    template_fit = spec.resamplespec(wl_obs,np.e**obs_lambda,pp.bestfit, oversamp =1000)
    # plt.plot(wl_obs,obj_spec, color='black', linestyle = 'steps-mid')
    # plt.plot(wl_obs, template_fit, color='red')

    return obj_spec,template_fit,obs_spectrum_header
示例#31
0
def mcmc(galaxy, z=0.01, vel=0.0, sig=200.0, discard=2, set_range=[4200,10000]):
	print '     MCMC'
	
	results = np.zeros((2,repeats))

	for i in range(2):
		templates, bin_log, noise, velscale, start, goodpixels,	moments, degree, dv, \
			lambdaq, plot, quiet = setup(galaxy, z=z, vel=vel, sig=sig, discard=discard, 
			set_range=set_range)

		pp = ppxf(templates, bin_log, noise, velscale, start, 
			goodpixels=goodpixels, moments=moments, degree=degree, vsyst=dv, 
			lam=lambdaq, plot=plot, quiet=quiet)
		z = (z + 1)*np.sqrt((1 + pp.sol[0]/c)/(1 - pp.sol[0]/c)) - 1 

	templates, bin_log, noise, velscale, start, goodpixels,	moments, \
		degree, dv, lambdaq, plot, quiet = 	setup(galaxy, z=z, vel=vel, 
		sig=sig, discard=discard, set_range=set_range)

	chi = 1000000
	for i in range(repeats):
		v_sav = vel 
		sigma_sav = sig
		chi_sav = chi
		if not quiet:
			print 'i=',i
			print "input v: ",v_sav
			print "input sig: ",sigma_sav
			print "input chi2: ",chi_sav

		start = [vel,sig]

		if i != repeats-1:
			pp = ppxf(templates, bin_log, noise, velscale, start, 
				goodpixels=goodpixels, moments=moments, degree=degree, 
				vsyst=dv, lam=lambdaq, plot=not quiet, quiet=quiet)
		else:
			pp = ppxf(templates, bin_log, noise, velscale, start, 
				goodpixels=goodpixels, moments=moments, degree=degree, 
				vsyst=dv, lam=lambdaq, plot=False, quiet=quiet)

		vel = pp.sol[0]
		sig = pp.sol[1]
		chi = pp.chi2

		results[:,i] = [vel,sig]
		if abs(chi) > abs(chi_sav) or np.random.uniform() > threshold:
			vel = v_sav + np.random.uniform(low=-1, high=1)*local_step 
			sig = sigma_sav + np.random.uniform(low=-1, high=1)*local_step 
			chi = chi_sav

	vel = np.mean(results[0,:])
	sig = np.mean(results[1,:])

	if not quiet:
		print "Mean vel: :",vel
		print "MEAN vel dispersion: ",sig

# ----------===============================================---------
# ----------================= Save Result =================---------
# ----------===============================================---------

	dir = '%s/Data/vimos'  %(cc.base_dir)

	fig, ax = plt.subplots()
	ax.scatter(results[0,:],results[1,:])
	ax.scatter(vel,sig, marker='*')
	ax.set_xlabel("velocity")
	ax.set_ylabel("velocity dispersion")
	ax.set_title("MCMC for initial conditions")

	fig.savefig('%s/analysis/%s/MCMC_initial_fit.png' %(dir,galaxy))
	if plot: 
		plt.show()



	data_file = "%s/analysis/galaxies.txt" % (dir)
	d = np.loadtxt(data_file, unpack=True, dtype=str)
	galaxy_gals = d[0][1:]
	z_gals, vel_gals, sig_gals = d[1][1:].astype(float), d[2][1:].astype(float), \
		d[3][1:].astype(float),
	x_gals, y_gals = d[4][1:].astype(int), d[5][1:].astype(int)
	SN_gals = {d[i][0]:d[i][1:].astype(float) for i in range(6,len(d))}

	# If galaxy is already in galaxies.txt file
	try:
		i_gal = np.where(galaxy_gals == galaxy)
	except:
		i_gal = -1
		galaxy_gals = [galaxy_gals, galaxy]
		z_gals = [z_gals, z]
		vel_gals = [vel_gals, vel]
		sig_gals = [sig_gals, sig]
	else:
		z_gals[i_gal] = z
		vel_gals[i_gal] = vel
		sig_gals[i_gal] = sig

	temp = "{0:12}{1:11}{2:10}{3:15}{4:4}{5:4}" + ''.join(['{%i:%i}'%(i+6,len(t)+1) 
		for i, t in enumerate(SN_gals.keys())]) + "\n"

	SN_titles = list(SN_gals.keys())

	with open(data_file, 'w') as f:
		f.write(temp.format("Galaxy", "z", "velocity", "sigma", "x", "y", 
			*(s for s in SN_titles)))
		for i in range(len(galaxy_gals)):
			f.write(temp.format(galaxy_gals[i], str(round(z_gals[i],7)), 
				str(round(vel_gals[i],4)), str(round(sig_gals[i],4)), 
				str(int(x_gals[i])), str(int(y_gals[i])), 
				*(str(round(SN_gals[s][i],2)) for s in SN_titles)))
def ppxf_example_population_gas_sdss():

    file_dir = path.dirname(path.realpath(__file__))  # path of this procedure

    # Read SDSS DR8 galaxy spectrum taken from here http://www.sdss3.org/dr8/
    # The spectrum is *already* log rebinned by the SDSS DR8
    # pipeline and log_rebin should not be used in this case.
    #
    file = file_dir + '/spectra/NGC3522_SDSS_DR8.fits'
    hdu = fits.open(file)
    t = hdu[1].data
    z = float(hdu[1].header["Z"]) # SDSS redshift estimate

    # Only use the wavelength range in common between galaxy and stellar library.
    #
    mask = (t['wavelength'] > 3540) & (t['wavelength'] < 7409)
    flux = t['flux'][mask]
    galaxy = flux/np.median(flux)   # Normalize spectrum to avoid numerical issues
    wave = t['wavelength'][mask]

    # The SDSS wavelengths are in vacuum, while the MILES ones are in air.
    # For a rigorous treatment, the SDSS vacuum wavelengths should be
    # converted into air wavelengths and the spectra should be resampled.
    # To avoid resampling, given that the wavelength dependence of the
    # correction is very weak, I approximate it with a constant factor.
    #
    wave *= np.median(util.vac_to_air(wave)/wave)

    # The noise level is chosen to give Chi^2/DOF=1 without regularization (REGUL=0).
    # A constant noise is not a bad approximation in the fitted wavelength
    # range and reduces the noise in the fit.
    #
    noise = np.full_like(galaxy, 0.01635)           # Assume constant noise per pixel here

    # The velocity step was already chosen by the SDSS pipeline
    # and we convert it below to km/s
    #
    c = 299792.458 # speed of light in km/s
    velscale = c*np.log(wave[1]/wave[0])  # eq.(8) of Cappellari (2017)
    FWHM_gal = 2.76 # SDSS has an approximate instrumental resolution FWHM of 2.76A.

    #------------------- Setup templates -----------------------

    pathname = file_dir + '/miles_models/Mun1.30*.fits'
    miles = lib.miles(pathname, velscale, FWHM_gal)

    # The stellar templates are reshaped into a 2-dim array with each spectrum
    # as a column, however we save the original array dimensions, which are
    # needed to specify the regularization dimensions
    #
    reg_dim = miles.templates.shape[1:]
    stars_templates = miles.templates.reshape(miles.templates.shape[0], -1)

    # See the pPXF documentation for the keyword REGUL,
    regul_err = 0.013  # Desired regularization error

    # Construct a set of Gaussian emission line templates.
    # Estimate the wavelength fitted range in the rest frame.
    #
    lam_range_gal = np.array([np.min(wave), np.max(wave)])/(1 + z)
    gas_templates, gas_names, line_wave = \
        util.emission_lines(miles.log_lam_temp, lam_range_gal, FWHM_gal)

    # Combines the stellar and gaseous templates into a single array
    # during the PPXF fit they will be assigned a different kinematic
    # COMPONENT value
    #
    templates = np.column_stack([stars_templates, gas_templates])

    #-----------------------------------------------------------

    # The galaxy and the template spectra do not have the same starting wavelength.
    # For this reason an extra velocity shift DV has to be applied to the template
    # to fit the galaxy spectrum. We remove this artificial shift by using the
    # keyword VSYST in the call to PPXF below, so that all velocities are
    # measured with respect to DV. This assume the redshift is negligible.
    # In the case of a high-redshift galaxy one should de-redshift its
    # wavelength to the rest frame before using the line below as described
    # in PPXF_EXAMPLE_KINEMATICS_SAURON.
    #
    c = 299792.458
    dv = c*(miles.log_lam_temp[0] - np.log(wave[0])) # km/s

    # Here the actual fit starts. The best fit is plotted on the screen.
    #
    # IMPORTANT: Ideally one would like not to use any polynomial in the fit
    # as the continuum shape contains important information on the population.
    # Unfortunately this is often not feasible, due to small calibration
    # uncertainties in the spectral shape. To avoid affecting the line strength of
    # the spectral features, we exclude additive polynomials (DEGREE=-1) and only use
    # multiplicative ones (MDEGREE=10). This is only recommended for population, not
    # for kinematic extraction, where additive polynomials are always recommended.
    #
    vel = c*np.log(1 + z)   # eq.(8) of Cappellari (2017)
    start = [vel, 180.]  # (km/s), starting guess for [V, sigma]

    t = clock()

    n_temps = stars_templates.shape[1]
    n_balmer = 4   # Number of Balmer lines included in the fit
    n_forbidden = 5

    # Assign component=0 to the stellar templates, component=1 to the Balmer
    # gas emission lines templates and component=2 to the forbidden lines.
    component = [0]*n_temps + [1]*n_balmer + [2]*n_forbidden
    gas_component = np.array(component) > 0  # gas_component=True for gas templates

    # Fit (V, sig, h3, h4) moments=4 for the stars
    # and (V, sig) moments=2 for the two gas components
    moments = [4, 2, 2]

    # Adopt the same starting value for the stars and the two gas components
    start = [start, start, start]

    pp = ppxf(templates, galaxy, noise, velscale, start,
              plot=False, moments=moments, degree=-1, mdegree=10, vsyst=dv, lam=wave,
              clean=False, regul=1./regul_err, reg_dim=reg_dim, component=component,
              gas_component=gas_component, gas_names=gas_names)

    # When the two Delta Chi^2 below are the same, the solution
    # is the smoothest consistent with the observed spectrum.
    #
    print('Desired Delta Chi^2: %.4g' % np.sqrt(2*galaxy.size))
    print('Current Delta Chi^2: %.4g' % ((pp.chi2 - 1)*galaxy.size))
    print('Elapsed time in PPXF: %.2f s' % (clock() - t))

    weights = pp.weights[~gas_component]  # Exclude weights of the gas templates
    weights = weights.reshape(reg_dim)/weights.sum()  # Normalized

    miles.mean_age_metal(weights)
    miles.mass_to_light(weights, band="r")

    # Plot fit results for stars and gas.
    # For every kinematic component, specify whether they are
    # gas emission, using the boolean `gas_component` keyword.
    plt.clf()
    plt.subplot(211)
    pp.plot()

    # Plot stellar population mass fraction distribution
    plt.subplot(212)
    miles.plot(weights)
    plt.tight_layout()
    plt.pause(1)
示例#33
0
def fitms(spectrum,error,template_list, out_prefix, cut=0.75, pre=0, mdegree=25, degree=4):

    pd = PDF(out_prefix+'.pdf')

    #make the galaxy wavelength vector
    data_hdu = pyfits.open(spectrum)
    error_hdu = pyfits.open(error)
    ddw = data_hdu[0].header['CDELT1']
    lambda0 = data_hdu[0].header['CRVAL1']
    wave =  lambda0+ \
        np.arange(data_hdu[0].shape[1])*ddw
#    wave = wave[pre:]
    lamRange1 = np.array([wave.min(),wave.max()])#/1.008246
    galaxy = data_hdu[0].data[0]#[pre:]
    loggalaxy, logLam1, velscale = pputil.log_rebin(lamRange1,galaxy)

    masklow = wave.min() - 500.
    maskhigh = wave.max() + 500.

    print data_hdu[0].shape
    print masklow,maskhigh

    c = 299792.458
    FWHM_gal = 0.95/1.008246 #AA
    FWHM_tmp = 0.55 #AA
    FWHM_diff = np.sqrt(FWHM_gal**2 - FWHM_tmp**2)
    sigma = FWHM_diff/2.355/ddw

    temp_hdu = pyfits.open(template_list[0])
    twave = temp_hdu[0].header['CRVAL1'] + \
        np.arange(temp_hdu[0].data.shape[1])*temp_hdu[0].header['CDELT1']
    mask = np.where((twave > masklow) & (twave < maskhigh))
    template = temp_hdu[0].data[0][mask]
    twave = twave[mask]
    lamRange2 = np.array([twave.min(),twave.max()])
    template = ndimage.gaussian_filter1d(template,sigma)
    logtmp, logLam2, velscale_tmp = pputil.log_rebin(lamRange2,
                                                     template,
                                                     velscale=velscale)

    bestfits = np.empty((1,wave.size))
    templates = np.empty((logtmp.size))
    print templates.shape

    for template_file in template_list:
        temp_hdu = pyfits.open(template_file)[0]
        data = temp_hdu.data
        header = temp_hdu.header
        twave = header['CRVAL1'] + np.arange(data.shape[1])*header['CDELT1']
        mask = (twave > masklow) & (twave < maskhigh)
        twave = twave[mask]
        for i in range(data.shape[0]):
            td = data[i][mask]
            # tfit = ADE.polyclip(twave,td,order)
            # td /= tfit(twave)
            td = ndimage.gaussian_filter1d(td,sigma)
            logtd, logLam2, velscale = pputil.log_rebin(lamRange2,td,
                                                        velscale=velscale)
            templates = np.vstack((templates,logtd/np.median(logtd)))
    
    templates = templates[1:].T
    print templates.shape, twave.shape

    for i in range(data_hdu[0].data.shape[0]):

        fig = plt.figure()

        galaxy = data_hdu[0].data[i]#[pre:]
        noise = error_hdu[0].data[i]
#        datafit = ADE.polyclip(wave,galaxy,order)

        # ax = fig.add_subplot(212)
        # ax.plot(wave,galaxy)
#        ax.plot(np.arange(templates[:,0].size),templates[:,0])
#        ax.set_ylim(0,2)
#        ax.plot(wave,datafit(wave))
#        galaxy /= datafit(wave)

        loggalaxy, logLam1, velscale = pputil.log_rebin(lamRange1,galaxy)
        logerror, _, _ = pputil.log_rebin(lamRange1,noise)
#        pyfits.PrimaryHDU(templates).writeto('templates.fits')
        print 'velscale:', velscale

        dv = (logLam2[0] - logLam1[0])*c
        print 'dv:', dv
        vel = c*1.008246
        start = [0.,2.]
        loggalaxy /= np.median(loggalaxy)
        logerror /= np.median(loggalaxy)
        
        goodfit = ADE.polyclip(np.arange(loggalaxy.size),loggalaxy,4)
        tmpgood = np.where(np.abs(loggalaxy - goodfit(np.arange(loggalaxy.size))) < \
                                  cut*np.std(loggalaxy))[0]
        tmpgood = tmpgood[tmpgood > pre]

        goodsmooth = np.zeros(loggalaxy.size)
        goodsmooth[tmpgood] = 1.
        goodsmooth = ndimage.gaussian_filter1d(goodsmooth,11)
        goodpixels = np.where(goodsmooth > 0.9)[0]

        pp = ppxf(templates,loggalaxy, logerror, 
                  velscale,start,bias=None,degree=degree,mdegree=mdegree,
                  vsyst=dv,plot=True, goodpixels=goodpixels)
        print bestfits.shape
        bestfits = np.vstack((bestfits,pp.bestfit))
        
        ###PLOT###
        plot_px = np.exp(logLam1)
        plot_gal = ndimage.gaussian_filter1d(loggalaxy,3)
        collection = collections.BrokenBarHCollection.span_where(
            plot_px,
            ymin=0,ymax=4,
            where=goodsmooth < 0.9,
            facecolor='red',alpha=0.5)
        collection2 = collections.BrokenBarHCollection.span_where(
            plot_px,
            ymin=0,ymax=4,
            where=np.arange(loggalaxy.size) <= pre, 
            facecolor='red',alpha=0.5)


        ax2 = fig.add_subplot(411)
        ax2.plot(np.exp(logLam1),plot_gal)
        ax2.plot(np.exp(logLam1),goodfit(np.arange(plot_px.size)))
        ax2.add_collection(collection)
        ax2.add_collection(collection2)

        ax2.plot(plot_px,pp.bestfit)
        ax2.set_xlim(plot_px[0],plot_px[-1])
        ax5 = ax2.twiny()
        ax5.set_xlim(0,loggalaxy.size)
        ax2.set_ylim(0.6,1.4)
        bbox2 = ax2.get_position().get_points().flatten()

        plt.setp(ax2.get_xticklabels(),visible=False)
        ax3 = fig.add_subplot(412,sharex=ax2)
        bbox3 = ax3.get_position().get_points().flatten()
        newpos = [bbox3[0],
                  bbox2[1] - (bbox3[3] - bbox3[1]),
                  bbox3[2] - bbox3[0],
                  bbox3[3] - bbox3[1]]
        ax3.set_position(newpos)
        ax3.plot(plot_px,loggalaxy - pp.bestfit,'r',lw=0.7)     
        ax3.set_ylim(-0.2,0.2)
        ax3.set_xlim(plot_px[0],plot_px[-1])

        ax = fig.add_subplot(212)
        pidx = np.arange(1525,1725,1)
        ax.plot(plot_px[pidx],plot_gal[pidx])
        ax.plot(plot_px[pidx],pp.bestfit[pidx])
        ax4 = ax.twiny()
        velx = np.arange(pidx.size)*velscale
        velx -= velx[-1]/2.
        ax4.set_xlim(velx[0],velx[-1])

        pd.savefig(fig)
        plt.close(fig)

    bestfits = bestfits[1:]
    pd.close()
    bfh = pyfits.PrimaryHDU(np.array(bestfits,dtype=np.float32))
    bfh.header.update('CDELT1',ddw)
    bfh.header.update('CRVAL1',wave.min())
    bfh.header.update('CRPIX1',1)
    bfh.header.update('CTYPE1','LINEAR')
    bfh.writeto(out_prefix+'.ms.fits',clobber=True)
    iraf.noao.onedspec.dispcor(out_prefix+'.ms.fits',out_prefix+'_lin.ms.fits',
                               linearize=True,log=False,flux=False,
                               dw=ddw,w1=wave.min(),w2=wave.max(),nw='INDEF',
                               samedis=True)
    plt.close('all')

    return
示例#34
0
    def run(self):
        if self.params.start is None:
            if not self.params.narrow_broad:
                start = [[self.vel, self.sig]] * (max(self.component) + 1)
            else:
                start = [[self.vel, self.sig]] * (max(self.component) / 2 + 1)
                # Start narrow line component at half of broad line
                start.extend([[self.vel, self.sig / 2]] *
                             (max(self.component) / 2))
        else:
            start = self.params.start
            for i, e in enumerate(self.element):
                if start[i][0] is None:
                    start[i][0] = self.vel
                if start[i][1] is None:
                    if 'n_' not in e:
                        start[i][1] = self.sig
                    else:
                        start[i][1] = self.sig / 2.

        moments = [self.params.stellar_moments] + [self.params.gas_moments] * \
         max(self.component)
        ## ----------============== The bestfit part ===============---------
        ppxf.__init__(self,
                      self.templates,
                      self.bin_log,
                      self.bin_log_noise,
                      self.velscale,
                      start,
                      goodpixels=self.goodPixels,
                      mdegree=self.params.mdegree,
                      moments=moments,
                      degree=self.params.degree,
                      vsyst=self.dv,
                      component=self.component,
                      lam=self.lambdaq,
                      plot=not self.params.quiet,
                      quiet=self.params.quiet,
                      produce_plot=False)
        if self.params.gas == 0:
            self.sol = [self.sol]
            self.error = [self.error]

    ## ----------===============================================---------
    ## ----------================= The MC part =================---------
    ## ----------===============================================---------
        if self.params.use_all_temp is not None:
            self.MCstellar_kin = np.zeros(
                (self.params.reps, abs(self.params.stellar_moments)))
            self.MCstellar_kin_err = np.zeros(
                (self.params.reps, abs(self.params.stellar_moments)))
        self.MCbestfit_uncert = np.zeros((3, len(self.galaxy)))
        MCbestfit_mean = np.zeros(len(self.galaxy))

        if self.params.gas:
            self.MCgas_kin = np.zeros((max(self.component), self.params.reps,
                                       abs(self.params.gas_moments)))
            self.MCgas_kin_err = np.zeros(
                (max(self.component), self.params.reps,
                 abs(self.params.gas_moments)))

            n_lines = len(self.e_templates.templatesToUse)
            # self.MCgas_weights = np.zeros((n_lines, self.params.reps))

            self.MCgas_uncert_spec = np.zeros((n_lines, 3, len(self.galaxy)))
            MCgas_mean_spec = np.zeros((n_lines, len(self.galaxy)))

            if self.params.reps == 0:
                self.MCgas_uncert_spec = np.zeros((n_lines, len(self.galaxy)))
        else:
            self.MCgas_kin = None
            self.MCgas_kin_err = None
            # self.MCgas_weights = None

        for rep in range(self.params.reps):
            random = np.random.randn(len(self.bin_log_noise))
            if self.params.use_residuals and rep == 0:
                _, residuals, _ = moving_weighted_average(self.lam,
                                                          self.bestfit -
                                                          self.bin_lin,
                                                          step_size=3.,
                                                          interp=True)
                add_noise = random * np.sqrt(self.bin_log_noise**2 +
                                             residuals**2)
            elif self.params.use_residuals:
                _, residuals, _ = moving_weighted_average(self.lam,
                                                          ppMC.bestfit -
                                                          self.bin_lin,
                                                          step_size=3.,
                                                          interp=True)
                add_noise = random * np.sqrt(self.bin_log_noise**2 +
                                             residuals**2)
            else:
                add_noise = random * np.abs(self.bin_log_noise)
            self.bin_log = self.bestfit + add_noise

            ppMC = ppxf(self.templates,
                        self.bin_log,
                        self.bin_log_noise,
                        self.velscale,
                        start,
                        goodpixels=self.goodPixels,
                        moments=moments,
                        degree=self.params.degree,
                        vsyst=self.dv,
                        lam=self.lambdaq,
                        plot=not self.params.quiet,
                        quiet=self.params.quiet,
                        bias=0.1,
                        component=self.component,
                        mdegree=self.params.mdegree)

            if self.params.gas == 0:
                ppMC.sol = [ppMC.sol]
                ppMC.error = [ppMC.error]

            if self.params.use_all_temp is not None:
                self.MCstellar_kin[
                    rep, :] = ppMC.sol[0][0:abs(self.params.stellar_moments)]
                self.MCstellar_kin_err[
                    rep, :] = ppMC.error[0][0:abs(self.params.stellar_moments)]

            # Find uncertainty in bestfit
            new_mean_bestfit_spec = (
                (rep + 1) * MCbestfit_mean + ppMC.bestfit) / (rep + 2)
            if rep < 3:
                # Save spec until 3 reps have been completed
                self.MCbestfit_uncert[rep, :] = ppMC.bestfit
            else:
                # Finding sigma_N from x_N, mean_N, mean_(N-1) and sigma_(N-1)
                # NB: rep = N-1 due to being zero-based
                self.MCbestfit_uncert = np.sqrt(
                    ((ppMC.bestfit - new_mean_bestfit_spec) *
                     (ppMC.bestfit - MCbestfit_mean) +
                     rep * self.MCbestfit_uncert**2) / (rep + 1))
            MCbestfit_mean = np.array(new_mean_bestfit_spec)
            if rep == 2:
                # Calc std at 3rd rep
                self.MCbestfit_uncert = np.std(self.MCbestfit_uncert, axis=0)

            # Gas kinematics from all reps
            for g in range(len(self.element) - 1):
                self.MCgas_kin[g, rep, :] = ppMC.sol[
                    g + 1][0:abs(self.params.gas_moments)]
                self.MCgas_kin_err[g, rep, :] = ppMC.error[
                    g + 1][0:abs(self.params.gas_moments)]

            # Find uncertainty in fitted emission lines
            for i, n in enumerate(self.e_templates.templatesToUse):
                e_line_spec = ppMC.matrix[:, -n_lines +
                                          i] * ppMC.weights[-n_lines + i]
                new_mean_gas_spec = ((rep + 1) * MCgas_mean_spec[i, :] +
                                     e_line_spec) / (rep + 2)

                if rep < 3 or self.params.reps == 0:
                    self.MCgas_uncert_spec[i, rep, :] = e_line_spec
                else:
                    self.MCgas_uncert_spec[i, :] = np.sqrt(
                        ((e_line_spec - new_mean_gas_spec) *
                         (e_line_spec - MCgas_mean_spec[i, :]) +
                         rep * self.MCgas_uncert_spec[i, :]**2) / (rep + 1))
                MCgas_mean_spec[i, :] = np.array(new_mean_gas_spec)

            if rep == 2 and self.params.gas != 0:
                self.MCgas_uncert_spec = np.std(self.MCgas_uncert_spec, axis=1)

        if self.params.produce_plot:
            self.fig, self.ax = create_plot(self).produce
def ppxf_simulation_example():

    hdu = fits.open('miles_models/Mun1.30Zp0.00T12.5893.fits'
                    )  # Solar metallicitly, Age=12.59 Gyr
    ssp = hdu[0].data
    h = hdu[0].header

    lamRange = h['CRVAL1'] + np.array([0., h['CDELT1'] * (h['NAXIS1'] - 1)])
    c = 299792.458  # speed of light in km/s
    velscale = c * h['CDELT1'] / max(
        lamRange)  # Do not degrade original velocity sampling
    star, logLam, velscale = util.log_rebin(lamRange, ssp, velscale=velscale)

    # The finite sampling of the observed spectrum is modeled in detail:
    # the galaxy spectrum is obtained by oversampling the actual observed spectrum
    # to a high resolution. This represent the true spectrum, which is later resampled
    # to lower resolution to simulate the observations on the CCD. Similarly, the
    # convolution with a well-sampled LOSVD is done on the high-resolution spectrum,
    # and later resampled to the observed resolution before fitting with PPXF.

    factor = 10  # Oversampling integer factor for an accurate convolution
    starNew = ndimage.interpolation.zoom(
        star, factor,
        order=3)  # This is the underlying spectrum, known at high resolution
    star = rebin(
        starNew, factor
    )  # Make sure that the observed spectrum is the integral over the pixels

    np.random.seed(133)  # for reproducible results

    h3 = 0.1  # Adopted G-H parameters of the LOSVD
    h4 = -0.1
    sn = 30.  # Adopted S/N of the Monte Carlo simulation
    m = 300  # Number of realizations of the simulation
    moments = 4
    velV = np.random.rand(m)  # velocity in *pixels* [=V(km/s)/velScale]
    sigmaV = np.linspace(
        0.5, 4, m)  # Range of sigma in *pixels* [=sigma(km/s)/velScale]

    result = np.zeros((m, moments))  # This will store the results
    t = clock()

    for j, (vel, sigma) in enumerate(zip(velV, sigmaV)):

        dx = int(
            abs(vel) +
            4.0 * sigma)  # Sample the Gaussian and GH at least to vel+4*sigma
        x = np.linspace(
            -dx, dx, 2 * dx * factor +
            1)  # Evaluate the Gaussian using steps of 1/factor pixels.
        w = (x - vel) / sigma
        w2 = w**2
        gauss = np.exp(-0.5 * w2)
        gauss /= np.sum(gauss)  # Normalized total(gauss)=1
        h3poly = w * (2. * w2 - 3.) / np.sqrt(3.)  # H3(y)
        h4poly = (w2 * (4. * w2 - 12.) + 3.) / np.sqrt(24.)  # H4(y)
        losvd = gauss * (1. + h3 * h3poly + h4 * h4poly)

        galaxy = signal.fftconvolve(
            starNew, losvd, mode="same")  # Convolve the oversampled spectrum
        galaxy = rebin(
            galaxy, factor)  # Integrate spectrum into original spectral pixels
        noise = galaxy / sn  # 1sigma error spectrum
        galaxy = np.random.normal(galaxy,
                                  noise)  # Add noise to the galaxy spectrum
        start = np.array([
            vel + np.random.random(), sigma * np.random.uniform(0.85, 1.15), 0,
            0
        ]) * velscale  # Convert to km/s

        pp = ppxf(star,
                  galaxy,
                  noise,
                  velscale,
                  start,
                  goodpixels=np.arange(dx, galaxy.size - dx),
                  plot=False,
                  moments=moments,
                  bias=0.3,
                  oversample=None)
        result[j, :] = pp.sol

    print('Calculation time: %.2f s' % (clock() - t))

    plt.clf()
    plt.subplot(221)
    plt.plot(sigmaV * velscale, (result[:, 0] / velscale - velV) / sigmaV,
             '+k')
    plt.axhline(0, color='r')
    plt.axvline(velscale, linestyle='dashed')
    plt.axvline(2 * velscale, linestyle='dashed')
    plt.ylim(-0.3, 0.3)
    plt.xlabel(r'$\sigma_{\rm in}\ (km\ s^{-1})$')
    plt.ylabel(r'$(V - V_{\rm in})/\sigma_{\rm in}$')
    plt.text(2.05 * velscale, -0.2, r'2$\times$velscale')

    plt.subplot(222)
    plt.plot(sigmaV * velscale, np.log10(result[:, 1] / (velscale * sigmaV)),
             '+k')
    plt.axhline(0, color='r')
    plt.axvline(velscale, linestyle='dashed')
    plt.axvline(2 * velscale, linestyle='dashed')
    plt.ylim(-0.15, 0.15)
    plt.xlabel(r'$\sigma_{in}\ (km\ s^{-1})$')
    plt.ylabel(r'$\log(\sigma/\sigma_{\rm in})$')
    plt.text(2.05 * velscale, -0.1, r'2$\times$velscale')

    plt.subplot(223)
    plt.plot(sigmaV * velscale, result[:, 2], '+k')
    plt.axhline(h3, color='r')
    plt.axhline(0, linestyle='dotted', color='limegreen')
    plt.axvline(velscale, linestyle='dashed')
    plt.axvline(2 * velscale, linestyle='dashed')
    plt.ylim(-0.15 + h3, 0.15 + h3)
    plt.xlabel(r'$\sigma_{\rm in}\ (km\ s^{-1})$')
    plt.ylabel('$h_3$')
    plt.text(2.05 * velscale, h3 - 0.1, r'2$\times$velscale')

    plt.subplot(224)
    plt.plot(sigmaV * velscale, result[:, 3], '+k')
    plt.axhline(h4, color='r')
    plt.axhline(0, linestyle='dotted', color='limegreen')
    plt.axvline(velscale, linestyle='dashed')
    plt.axvline(2 * velscale, linestyle='dashed')
    plt.ylim(-0.15 + h4, 0.15 + h4)
    plt.xlabel(r'$\sigma_{\rm in}\ (km\ s^{-1})$')
    plt.ylabel('$h_4$')
    plt.text(2.05 * velscale, h4 - 0.1, r'2$\times$velscale')

    plt.tight_layout()
    plt.pause(0.01)
def ppxf_kinematics_example_sdss():

    # Read SDSS DR8 galaxy spectrum taken from here http://www.sdss3.org/dr8/
    # The spectrum is *already* log rebinned by the SDSS DR8
    # pipeline and log_rebin should not be used in this case.
    #
    file = 'spectra/NGC3522_SDSS.fits'
    hdu = pyfits.open(file)
    t = hdu[1].data
    z = float(hdu[1].header["Z"]) # SDSS redshift estimate

    # Only use the wavelength range in common between galaxy and stellar library.
    #
    mask = (t.field('wavelength') > 3540) & (t.field('wavelength') < 7409)
    galaxy = t[mask].field('flux')/np.median(t[mask].field('flux'))  # Normalize spectrum to avoid numerical issues
    wave = t[mask].field('wavelength')
    noise = galaxy*0 + 0.0156           # Assume constant noise per pixel here

    # The velocity step was already chosen by the SDSS pipeline
    # and we convert it below to km/s
    #
    c = 299792.458 # speed of light in km/s
    velscale = np.log(wave[1]/wave[0])*c
    FWHM_gal = 2.76 # SDSS has an instrumental resolution FWHM of 2.76A.

    # If the galaxy is at a significant redshift (z > 0.03), one would need to apply
    # a large velocity shift in PPXF to match the template to the galaxy spectrum.
    # This would require a large initial value for the velocity (V > 1e4 km/s)
    # in the input parameter START = [V,sig]. This can cause PPXF to stop!
    # The solution consists of bringing the galaxy spectrum roughly to the
    # rest-frame wavelength, before calling PPXF. In practice there is no
    # need to modify the spectrum in any way, given that a red shift
    # corresponds to a linear shift of the log-rebinned spectrum.
    # One just needs to compute the wavelength range in the rest-frame
    # and adjust the instrumental resolution of the galaxy observations.
    # This is done with the following three commented lines:
    #
    # z = 1.23 # Initial estimate of the galaxy redshift
    # wave = wave/(1+z) # Compute approximate restframe wavelength
    # FWHM_gal = FWHM_gal/(1+z)   # Adjust resolution in Angstrom

    # Read the list of filenames from the Single Stellar Population library
    # by Vazdekis (2010, MNRAS, 404, 1639) http://miles.iac.es/. A subset
    # of the library is included for this example with permission
    #
    vazdekis = glob.glob('miles_models/Mun1.30Z*.fits')
    FWHM_tem = 2.51 # Vazdekis+10 spectra have a resolution FWHM of 2.51A.

    # Extract the wavelength range and logarithmically rebin one spectrum
    # to the same velocity scale of the SDSS galaxy spectrum, to determine
    # the size needed for the array which will contain the template spectra.
    #
    hdu = pyfits.open(vazdekis[0])
    ssp = hdu[0].data
    h2 = hdu[0].header
    lamRange2 = h2['CRVAL1'] + np.array([0.,h2['CDELT1']*(h2['NAXIS1']-1)])
    sspNew, logLam2, velscale = util.log_rebin(lamRange2, ssp, velscale=velscale)
    templates = np.empty((sspNew.size,len(vazdekis)))

    # Convolve the whole Vazdekis library of spectral templates
    # with the quadratic difference between the SDSS and the
    # Vazdekis instrumental resolution. Logarithmically rebin
    # and store each template as a column in the array TEMPLATES.

    # Quadratic sigma difference in pixels Vazdekis --> SDSS
    # The formula below is rigorously valid if the shapes of the
    # instrumental spectral profiles are well approximated by Gaussians.
    #
    FWHM_dif = np.sqrt(FWHM_gal**2 - FWHM_tem**2)
    sigma = FWHM_dif/2.355/h2['CDELT1'] # Sigma difference in pixels

    for j in range(len(vazdekis)):
        hdu = pyfits.open(vazdekis[j])
        ssp = hdu[0].data
        ssp = ndimage.gaussian_filter1d(ssp,sigma)
        sspNew, logLam2, velscale = util.log_rebin(lamRange2, ssp, velscale=velscale)
        templates[:,j] = sspNew/np.median(sspNew) # Normalizes templates

    # The galaxy and the template spectra do not have the same starting wavelength.
    # For this reason an extra velocity shift DV has to be applied to the template
    # to fit the galaxy spectrum. We remove this artificial shift by using the
    # keyword VSYST in the call to PPXF below, so that all velocities are
    # measured with respect to DV. This assume the redshift is negligible.
    # In the case of a high-redshift galaxy one should de-redshift its
    # wavelength to the rest frame before using the line below (see above).
    #
    c = 299792.458
    dv = (logLam2[0]-np.log(wave[0]))*c # km/s
    vel = c*z # Initial estimate of the galaxy velocity in km/s
    goodpixels = util.determine_goodpixels(np.log(wave),lamRange2,vel)

    # Here the actual fit starts. The best fit is plotted on the screen.
    # Gas emission lines are excluded from the pPXF fit using the GOODPIXELS keyword.
    #
    start = [vel, 180.] # (km/s), starting guess for [V,sigma]
    t = clock()

    pp = ppxf(templates, galaxy, noise, velscale, start,
              goodpixels=goodpixels, plot=True, moments=4,
              degree=10, vsyst=dv, clean=False)

    print("Formal errors:")
    print("     dV    dsigma   dh3      dh4")
    print("".join("%8.2g" % f for f in pp.error*np.sqrt(pp.chi2)))

    print('Elapsed time in PPXF: %.2f s' % (clock() - t))
示例#37
0
def run_ppxf(spectra, velscale, ncomp=2, has_emission=True, mdegree=-1,
             degree=20, pkls=None, plot=False, data_sky=None):
    """ Run pPXF in a list of spectra"""
    if isinstance(spectra, str):
        spectra = [spectra]
    if isinstance(pkls, str):
        pkls = [pkls]
    if pkls == None:
        pkls = [x.replace(".fits", ".pkl") for x in spectra]
    ##########################################################################
    # Load templates for both stars and gas
    star_templates, logLam2, delta, miles= stellar_templates(velscale)
    gas_templates,logLam_gas, delta_gas, gas_files=emission_templates(velscale)
    ##########################################################################
    # Join templates in case emission lines are used.
    if has_emission:
        templates = np.column_stack((star_templates, gas_templates))
    else:
        templates = star_templates
    ##########################################################################
    if ncomp == 1:
        components = 0
        moments = [4]
        templates_names = miles
    elif ncomp == 2:
        components = np.hstack((np.zeros(len(star_templates[0])),
                                np.ones(len(gas_templates[0]))))
        moments = [4,2]
        templates_names = np.hstack((miles, gas_files))

    else:
        raise Exception("ncomp has to be 1 or 2.")
    for i, spec in enumerate(spectra):
        print "pPXF run of spectrum {0} ({1} of {2})".format(spec, i+1,
              len(spectra))
        pkl = pkls[i]
        ######################################################################
        # Read one galaxy spectrum and define the wavelength range
        specfile = os.path.join(data_dir, spec)
        hdu = pf.open(specfile)
        spec_lin = hdu[0].data
        h1 = pf.getheader(specfile)
        lamRange1 = h1['CRVAL1'] + np.array([0.,h1['CDELT1']*(h1['NAXIS1']-1)])
        ######################################################################
        # Degrade observed spectra to match template resolution
        FWHM_dif = np.sqrt(FWHM_tem**2 - FWHM_spec**2)
        sigma = FWHM_dif/2.355/delta # Sigma difference in pixels
        spec_lin = ndimage.gaussian_filter1d(spec_lin,sigma)
        ######################################################################
        # Rebin to log scale
        galaxy, logLam1, velscale = util.log_rebin(lamRange1, spec_lin, 
                                                   velscale=velscale)
        ######################################################################
        # First guess for the noise
        noise = np.ones_like(galaxy) * np.std(galaxy - medfilt(galaxy, 5))
        ######################################################################
        # Calculate difference of velocity between spectrum and templates
        # due to different initial wavelength
        dv = (logLam2[0]-logLam1[0])*c
        ######################################################################
        # Set first guess from setup files
        start, goodPixels = read_setup_file(spec, logLam1, mask_emline=False)
        ######################################################################
        # Expand start variable to include multiple components
        if ncomp > 1:
            start = [start, [start[0], 30]]
        ######################################################################
        # Read sky in needed
        if data_sky == None:
            sky = None
        else:
            sky_lin = pf.getdata(data_sky[i])
            sky_lin = ndimage.gaussian_filter1d(sky_lin,sigma)
            sky, logLam1, velscale = util.log_rebin(lamRange1, sky_lin,
                                                    velscale=velscale)
            sky = sky.reshape(-1,1)
        ######################################################################
        # First pPXF interaction
        if os.path.exists(spec.replace(".fits", ".pkl")):
            pp0 = pPXF(spec, velscale, pklfile=spec.replace(".fits", ".pkl"))
            noise0 = pp0.noise
        else:
            pp0 = ppxf(templates, galaxy, noise, velscale, start,
                       goodpixels=goodPixels, plot=False, moments=moments,
                       degree=12, mdegree=-1, vsyst=dv, component=components,
                       sky=sky)
            rms0 = galaxy[goodPixels] - pp0.bestfit[goodPixels]
            noise0 = 1.4826 * np.median(np.abs(rms0 - np.median(rms0)))
            noise0 = np.zeros_like(galaxy) + noise0
        # Second pPXF interaction, realistic noise estimation
        pp = ppxf(templates, galaxy, noise0, velscale, start,
                  goodpixels=goodPixels, plot=plot, moments=moments,
                  degree=degree, mdegree=mdegree, vsyst=dv,
                  component=components, sky=sky)
        # pp.template_files = templates_names
        # pp.has_emission = has_emission
        ######################################################################
        # Save to output file to keep session
        with open(pkl, "w") as f:
            pickle.dump(pp, f)
        ######################################################################
    return
示例#38
0
def ppxf_run_MaNGA_galaxy(ifu,
                          fname_tem,
                          first_few=None,
                          Tsample=4,
                          over=True):

    plt.close('all')
    plate = ifu[0]
    ifudsgn = ifu[1]
    fname_ifu = 'manga-{}-{}-LOGCUBE.fits.gz'.format(plate, ifudsgn)

    # now read in drpall
    drpall = table.Table.read(m.drpall_loc + 'drpall-v1_3_3.fits',
                              format='fits')
    objconds = drpall['plateifu'] == '{}-{}'.format(plate, ifudsgn)
    obj = drpall[objconds]

    c = 299792.458
    z = obj['nsa_redshift']
    if z == -9999.:
        z = 0.1

    # now read in IFU
    ifu = fits.open(fname_ifu)
    ifu_flux = ifu['FLUX'].data
    ifu_ivar = ifu['ivar'].data
    ifu_mask = ifu['MASK'].data
    L_ifu = ifu['WAVE'].data
    red_scattered_light = L_ifu > 9500.
    logL_ifu = np.log(L_ifu) - z
    L_ifu = np.exp(logL_ifu)

    res_path = '{}-{}/'.format(plate, ifudsgn)
    try:
        os.makedirs(res_path)
    except OSError:
        pass

    # read in stellar templates
    # templates have already been convolved to the proper resolution
    tems = fits.open(fname_tem)
    htems = tems[0].header
    dtems = tems[0].data[::Tsample, :, :]

    dtems_red = kin_models(tems)
    print dtems_red.shape

    nT, nZ, nL = dtems.shape

    Zs = htems['CRVAL2'] + np.linspace(0., htems['CDELT2'] *
                                       (htems['NAXIS2'] - 1), htems['NAXIS2'])
    Ts = htems['CRVAL3'] + np.linspace(0., htems['CDELT3'] *
                                       (htems['NAXIS3'] - 1), htems['NAXIS3'])

    logL_tem = np.linspace(htems['CRVAL1'],
                           htems['CRVAL1'] + (nL - 1) * htems['CDELT1'],
                           nL)  # base e

    L_tem = np.exp(logL_tem)

    dtems_med = np.median(dtems)
    dtems /= dtems_med

    ssps = np.swapaxes(dtems, 0, 2)
    ssps = np.reshape(ssps, (nL, -1))

    ssps_red = np.swapaxes(dtems_red, 0, 2)
    ssps_red = np.reshape(ssps_red, (nL, -1))

    vel = z * c
    veldisp = obj['nsa_vdisp']
    if veldisp < 0.:  # deal with non-measured veldisps
        veldisp = 300.

    velscale = (logL_ifu[1] - logL_ifu[0]) * c
    dl = L_tem[0] - L_ifu[0]
    dv = c * (logL_tem[0] - logL_ifu[0])  # km/s
    moments = 4
    regul_err = .01
    ebvgal = obj['ebvgal']
    start = [0., veldisp]
    reg_dim = [nZ, nT]

    spaxels = which_spaxels(fname_ifu)

    n_stellar_tems = nZ * nT
    moments0 = [
        2,
    ]
    moments1 = [
        -2,
    ]
    start = [0., veldisp]

    i = 0

    for spaxel in spaxels:

        gridx, gridy = spaxel['gridx'], spaxel['gridy']
        print 'Spaxel row {}; col {}'.format(gridx, gridy)
        figpath = res_path + '{}-{}_pp_fit.png'.format(gridx, gridy)
        fig_ex = os.path.exists(figpath)

        if (spaxel['good'] == True) and ((fig_ex == False) or (over == True)):
            goodpixels = 1 - (ifu_mask[:, gridy, gridx] & 10)
            goodpixels *= (1 - (ifu_mask[:, gridy, gridx] & 8))
            goodpixels *= (np.isfinite(ifu_ivar[:, gridy, gridx]))
            # red end has some scattered light, so throw it out
            goodpixels *= (1 - red_scattered_light)
            emlines = line_mask(logL_ifu, 800.)
            goodpixels *= emlines
            goodpixels_i = np.where(goodpixels)[0]

            galaxy = ifu_flux[:, gridy, gridx]
            ivar = ifu_ivar[:, gridy, gridx]
            noise = 1. / np.sqrt(ivar)
            noise = np.where(np.isfinite(noise), noise, 9999.)
            med = np.median(galaxy)

            try:

                pp0 = ppxf(templates=ssps_red,
                           galaxy=galaxy / med,
                           noise=noise / med,
                           goodpixels=goodpixels_i,
                           start=start,
                           vsyst=dv,
                           velScale=velscale,
                           moments=moments0,
                           degree=-1,
                           mdegree=-1,
                           clean=False,
                           lam=L_ifu,
                           regul=None)

                pp = ppxf(templates=ssps,
                          galaxy=galaxy / med,
                          noise=noise / med,
                          goodpixels=goodpixels_i,
                          start=pp0.sol,
                          vsyst=dv,
                          velScale=velscale,
                          moments=moments1,
                          degree=-1,
                          mdegree=-1,
                          reddening=ebvgal,
                          clean=False,
                          lam=L_ifu,
                          regul=1. / regul_err,
                          reg_dim=reg_dim)

                #print('Desired Delta Chi^2: %.4g' % np.sqrt(2*galaxy.size))
                #print('Current Delta Chi^2: %.4g' % ((pp.chi2 - 1)*\
                #    galaxy.size))

            except:
                print '\tFITTING PROBLEM'
                fit_success = False

            else:
                ppxf_fig(pp, (gridx, gridy), tems, (plate, ifudsgn), reg_dim)
                plt.savefig(figpath, dpi=300)

                start = pp.sol[:2]

                pp_res = {
                    'bestfit': pp.bestfit,
                    'chi2': pp.chi2,
                    'error': pp0.error,
                    'galaxy': pp.galaxy,
                    'lam': pp.lam,
                    'sol': pp0.sol,
                    'vsyst': pp.vsyst,
                    'noise': pp.noise,
                    'specres': ifu['SPECRES']
                }

                pk.dump(
                    pp_res,
                    file(res_path + '{}-{}-pp.pickle'.format(gridx, gridy),
                         'w'))

            finally:
                i += 1
                if i >= first_few:
                    break
 #
 #
 logLam2, fl_temp = numpy.genfromtxt(directory + 'wise_template.dat', unpack = True, skiprows = 1)
 wave_temp = numpy.exp(logLam2)
 lamRange2 = numpy.array([logLam2[0], logLam2[-1]])
 #
 templates = fl_temp
 #
 c = 299792.458
 dv = (logLam2[0]-logLam1[0])*c # km/s
 #
 vel = 1500. 
 #
 start = [vel, 200.] # (km/s), starting guess for [V,sigma]
 #
 pp = ppxf(templates, galaxy, noise, velscale, start, plot=False, moments=2, 
       degree=4, vsyst=dv)
 #
 if moments == 4:
   pp = ppxf(templates, galaxy, noise, velscale, [pp.sol[0], pp.sol[1]], plot=False, moments=2, 
       degree=4, vsyst=dv)
 #
 #
 rec.append(pp.sol[0])
 sig.append(pp.sol[1])
 chi2.append(pp.chi2)
 er.append(pp.error*np.sqrt(pp.chi2))
 #
 # RUN MONTECARLO SIMULATION
 #
 lenMC = N_iter_MC
 #
示例#40
0
	hdu = fits.open('temp_final_%s.fits' % Z)
	h_template=hdu[0].header
	temp_lin=hdu[0].data
	naxis=h_template['NAXIS1']
	
	lamRange2=h_template['CRVAL1'] + np.array([0.,h_template['CDELT1']*(h_template['NAXIS1']-1.)])
	templates, logLam2, velscale= util.log_rebin(lamRange2, temp_lin, velscale=velscale)
	
# Calculate the offset between the template and the galaxy
c = 299792.458
dv = (logLam2[0]-logLam1[0])*c
noise = galaxy*0 + 1  


# Do the ppxf
pp = ppxf(templates, galaxy, noise, velscale, start,
              goodpixels=goodPixels, plot=True, degree=4, moments=2, lam = np.exp(logLam1), vsyst=dv)

# Save the template and the weights
if make_temp:
	find_weights = pp.weights > 0
	nw = len(find_weights)
	weights = pp.weights[find_weights]
	temp_final = np.average(templates_lin[:,find_weights], weights=weights,axis=1)
	
	
	fits.writeto('temp_final_'+Z+'.fits',temp_final, header=h_template, clobber=True)
	ascii.write(Table([temp_files,pp.weights]), name+'_tempWeights.txt')
	

# Plot
fig=plt.figure(figsize=(9,5))
def ppxf_two_components_example():

    hdu = fits.open('miles_models/Mun1.30Zp0.00T12.5893.fits')  # Solar metallicitly, Age=12.59 Gyr
    gal_lin = hdu[0].data
    h1 = hdu[0].header
    lamRange1 = h1['CRVAL1'] + np.array([0., h1['CDELT1']*(h1['NAXIS1']-1)])
    c = 299792.458 # speed of light in km/s
    velscale = c*h1['CDELT1']/max(lamRange1)   # Do not degrade original velocity sampling
    model1, logLam1, velscale = util.log_rebin(lamRange1, gal_lin, velscale=velscale)
    model1 /= np.median(model1)

    hdu = fits.open('miles_models/Mun1.30Zp0.00T01.0000.fits')  # Solar metallicitly, Age=1.00 Gyr
    gal_lin = hdu[0].data
    model2, logLam1, velscale = util.log_rebin(lamRange1, gal_lin, velscale=velscale)
    model2 /= np.median(model2)

    model = np.column_stack([model1, model2])
    galaxy = np.empty_like(model)

    # These are the input values in spectral pixels
    # for the (V,sigma) of the two kinematic components
    #
    vel = np.array([0., 250.])/velscale
    sigma = np.array([200., 100.])/velscale

    # The synthetic galaxy model consists of the sum of two
    # SSP spectra with age of 1Gyr and 13Gyr respectively
    # with different velocity and dispersion
    #
    for j in range(len(vel)):
        dx = int(abs(vel[j]) + 4.*sigma[j])   # Sample the Gaussian at least to vel+4*sigma
        v = np.linspace(-dx, dx, 2*dx + 1)
        losvd = np.exp(-0.5*((v - vel[j])/sigma[j])**2) # Gaussian LOSVD
        losvd /= np.sum(losvd) # normaize LOSVD
        galaxy[:, j] = signal.fftconvolve(model[:, j], losvd, mode="same")
        galaxy[:, j] /= np.median(model[:, j])
    galaxy = np.sum(galaxy, axis=1)
    sn = 100.
    np.random.seed(2) # Ensure reproducible results
    noise = galaxy/sn
    galaxy = np.random.normal(galaxy, noise) # add noise to galaxy

    # Adopts two templates per kinematic component
    #
    templates = np.column_stack([model1, model2, model1, model2])

    # Start both kinematic components from the same guess.
    # With multiple stellar kinematic components
    # a good starting guess is essential
    #
    start = [np.mean(vel)*velscale, np.mean(sigma)*velscale]
    start = [start, start]
    goodPixels = np.arange(20, 6000)

    t = clock()

    plt.clf()
    plt.subplot(211)
    plt.title("Two components pPXF fit")
    print("+++++++++++++++++++++++++++++++++++++++++++++")

    pp = ppxf(templates, galaxy, noise, velscale, start,
              goodpixels=goodPixels, plot=True, degree=4,
              moments=[2, 2], component=[0, 0, 1, 1])

    plt.subplot(212)
    plt.title("Single component pPXF fit")
    print("---------------------------------------------")

    start = start[0]
    pp = ppxf(templates, galaxy, noise, velscale, start,
              goodpixels=goodPixels, plot=True, degree=4, moments=2)

    plt.tight_layout()
    plt.pause(0.01)

    print("=============================================")
    print("Total elapsed time %.2f s" % (clock() - t))
示例#42
0
def run_stellar_templates(velscale):
    """ Run over stellar templates. """
    temp_dir = os.path.join(home, "stellar_templates/MILES_FWHM_3.6")
    standards_dir = os.path.join(home, "data/standards")
    table = os.path.join(tables_dir, "lick_standards.txt")
    ids = np.loadtxt(table, usecols=(0,), dtype=str).tolist()
    star_pars = np.loadtxt(table, usecols=(26,27,28,))
    for night in nights:
        cdir = os.path.join(standards_dir, night)
        os.chdir(cdir)
        standards = sorted([x for x in os.listdir(".") if x.endswith(".fits")])
        for standard in standards:
            name = standard.split(".")[0].upper()
            if name not in ids:
                continue
            print standard
            idx = ids.index(name)
            T, logg, FeH = star_pars[idx]
            tempfile= "MILES_Teff{0:.2f}_Logg{1:.2f}_MH{2:.2f}" \
                       "_linear_FWHM_3.6.fits".format(T, logg, FeH )
            os.chdir(temp_dir)
            template = pf.getdata(tempfile)
            htemp = pf.getheader(tempfile)
            wtemp = htemp["CRVAL1"] + htemp["CDELT1"] * \
                            (np.arange(htemp["NAXIS1"]) + 1 - htemp["CRPIX1"])
            os.chdir(cdir)
            dsigma = np.sqrt((3.7**2 - 3.6**2))/2.335/(wtemp[1]-wtemp[0])
            template = broad2hydra(wtemp, template, 3.6)
            data = pf.getdata(standard)
            w = wavelength_array(standard)
            lamRange1 = np.array([w[0], w[-1]])
            lamRange2 = np.array([wtemp[0], wtemp[-1]])
            # Rebin to log scale
            star, logLam1, velscale = util.log_rebin(lamRange1, data,
                                                       velscale=velscale)
            temp, logLam2, velscale = util.log_rebin(lamRange2, template,
                                                       velscale=velscale)
            w = np.exp(logLam1)
            goodpixels = np.argwhere((w>4700) & (w<6100)).T[0]
            noise = np.ones_like(star)
            dv = (logLam2[0]-logLam1[0])*c
            pp0 = ppxf(temp, star, noise, velscale, [300.,5], plot=False,
                       moments=2, degree=20, mdegree=-1, vsyst=dv, quiet=True,
                       goodpixels=goodpixels)
            noise = np.ones_like(noise) * np.nanstd(star - pp0.bestfit)
            pp0 = ppxf(temp, star, noise, velscale, [0.,5], plot=False,
                       moments=2, degree=20, mdegree=-1, vsyst=dv,
                       goodpixels=goodpixels)
            pp0.w = np.exp(logLam1)
            pp0.wtemp = np.exp(logLam2)
            pp0.template_linear = [wtemp, template]
            pp0.temp = temp
            pp0.ntemplates = 1
            pp0.ngas = 0
            pp0.has_emission = False
            pp0.dv = dv
            pp0.velscale = velscale
            pp0.ngas = 0
            pp0.nsky = 0
            if not os.path.exists("logs"):
                os.mkdir("logs")
            ppsave(pp0, "logs/{0}".format(standard.replace(".fits", "")))
            pp = ppload("logs/{0}".format(standard.replace(".fits", "")))
            pp = pPXF(standard, velscale, pp)
            pp.plot("logs/{0}".format(standard.replace(".fits", ".png")))
            # plt.show()
    return
def ppxf_kinematics_example_sdss():

    # Read SDSS DR12 galaxy spectrum taken from here http://dr12.sdss3.org/
    # The spectrum is *already* log rebinned by the SDSS DR12
    # pipeline and log_rebin should not be used in this case.
    file = 'spectra/NGC4636_SDSS_DR12.fits'
    hdu = pyfits.open(file)
    t = hdu['COADD'].data
    z =  0.003129   # SDSS redshift estimate

    # Only use the wavelength range in common between galaxy and stellar library.
    mask = (t['loglam'] > np.log10(3540)) & (t['loglam'] < np.log10(7409))
    flux = t['flux'][mask]
    galaxy = flux/np.median(flux)   # Normalize spectrum to avoid numerical issues
    loglam_gal = t['loglam'][mask]
    lam_gal = 10**loglam_gal
    noise = galaxy*0 + 0.0166       # Assume constant noise per pixel here

    c = 299792.458                  # speed of light in km/s
    frac = lam_gal[1]/lam_gal[0]    # Constant lambda fraction per pixel
    dlam_gal = (frac - 1)*lam_gal   # Size of every pixel in Angstrom
    wdisp = t['wdisp'][mask]        # Intrinsic dispersion of every pixel, in pixels units
    fwhm_gal = 2.355*wdisp*dlam_gal # Resolution FWHM of every pixel, in Angstroms
    velscale = np.log(frac)*c       # Constant velocity scale in km/s per pixel

    # If the galaxy is at a significant redshift (z > 0.03), one would need to apply
    # a large velocity shift in PPXF to match the template to the galaxy spectrum.
    # This would require a large initial value for the velocity (V > 1e4 km/s)
    # in the input parameter START = [V,sig]. This can cause PPXF to stop!
    # The solution consists of bringing the galaxy spectrum roughly to the
    # rest-frame wavelength, before calling PPXF. In practice there is no
    # need to modify the spectrum in any way, given that a red shift
    # corresponds to a linear shift of the log-rebinned spectrum.
    # One just needs to compute the wavelength range in the rest-frame
    # and adjust the instrumental resolution of the galaxy observations.
    # This is done with the following three commented lines:
    #
    # lam_gal = lam_gal/(1+z)  # Compute approximate restframe wavelength
    # fwhm_gal = fwhm_gal/(1+z)   # Adjust resolution in Angstrom

    # Read the list of filenames from the Single Stellar Population library
    # by Vazdekis (2010, MNRAS, 404, 1639) http://miles.iac.es/. A subset
    # of the library is included for this example with permission
    vazdekis = glob.glob('miles_models/Mun1.30Z*.fits')
    vazdekis.sort()
    fwhm_tem = 2.51 # Vazdekis+10 spectra have a constant resolution FWHM of 2.51A.

    # Extract the wavelength range and logarithmically rebin one spectrum
    # to the same velocity scale of the SDSS galaxy spectrum, to determine
    # the size needed for the array which will contain the template spectra.
    #
    hdu = pyfits.open(vazdekis[0])
    ssp = hdu[0].data
    h2 = hdu[0].header
    lam_temp = h2['CRVAL1'] + h2['CDELT1']*np.arange(h2['NAXIS1'])
    lamRange_temp = [np.min(lam_temp), np.max(lam_temp)]
    sspNew, logLam2, velscale = util.log_rebin(lamRange_temp, ssp, velscale=velscale)
    templates = np.empty((sspNew.size, len(vazdekis)))

    # Interpolates the galaxy spectral resolution at the location of every pixel
    # of the templates. Outside the range of the galaxy spectrum the resolution
    # will be extrapolated, but this is irrelevant as those pixels cannot be
    # used in the fit anyway.
    fwhm_gal = np.interp(lam_temp, lam_gal, fwhm_gal)

    # Convolve the whole Vazdekis library of spectral templates
    # with the quadratic difference between the SDSS and the
    # Vazdekis instrumental resolution. Logarithmically rebin
    # and store each template as a column in the array TEMPLATES.

    # Quadratic sigma difference in pixels Vazdekis --> SDSS
    # The formula below is rigorously valid if the shapes of the
    # instrumental spectral profiles are well approximated by Gaussians.
    #
    # In the line below, the fwhm_dif is set to zero when fwhm_gal < fwhm_tem.
    # In principle it should never happen and a higher resolution template should be used.
    #
    fwhm_dif = np.sqrt((fwhm_gal**2 - fwhm_tem**2).clip(0))
    sigma = fwhm_dif/2.355/h2['CDELT1'] # Sigma difference in pixels

    for j, fname in enumerate(vazdekis):
        hdu = pyfits.open(fname)
        ssp = hdu[0].data
        ssp = util.gaussian_filter1d(ssp, sigma)  # perform convolution with variable sigma
        sspNew, logLam2, velscale = util.log_rebin(lamRange_temp, ssp, velscale=velscale)
        templates[:, j] = sspNew/np.median(sspNew) # Normalizes templates

    # The galaxy and the template spectra do not have the same starting wavelength.
    # For this reason an extra velocity shift DV has to be applied to the template
    # to fit the galaxy spectrum. We remove this artificial shift by using the
    # keyword VSYST in the call to PPXF below, so that all velocities are
    # measured with respect to DV. This assume the redshift is negligible.
    # In the case of a high-redshift galaxy one should de-redshift its
    # wavelength to the rest frame before using the line below (see above).
    #
    c = 299792.458
    dv = np.log(lam_temp[0]/lam_gal[0])*c    # km/s
    goodpixels = util.determine_goodpixels(np.log(lam_gal), lamRange_temp, z)

    # Here the actual fit starts. The best fit is plotted on the screen.
    # Gas emission lines are excluded from the pPXF fit using the GOODPIXELS keyword.
    #
    vel = c*np.log(1 + z)   # Initial estimate of the galaxy velocity in km/s
    start = [vel, 200.] # (km/s), starting guess for [V,sigma]
    t = clock()

    pp = ppxf(templates, galaxy, noise, velscale, start,
              goodpixels=goodpixels, plot=True, moments=4,
              degree=12, vsyst=dv, clean=False)

    print("Formal errors:")
    print("     dV    dsigma   dh3      dh4")
    print("".join("%8.2g" % f for f in pp.error*np.sqrt(pp.chi2)))

    print('Elapsed time in PPXF: %.2f s' % (clock() - t))
示例#44
0
        goodpixels = util.determine_goodpixels(log_wave, lamRange_temp, z)

        start = [c * np.log(1 + z), 0.01]  #3*velscale]
        fixed = [True, True]  #, False, False]
        fixed = [False, False]
        print start

        pp = ppxf(templates,
                  galaxy,
                  noise,
                  velscale,
                  start,
                  goodpixels=goodpixels,
                  plot=False,
                  moments=2,
                  degree=-1,
                  vsyst=dv,
                  clean=False,
                  regul=1,
                  mdegree=0,
                  reddening=0.1,
                  lam=wave,
                  fixed=fixed)

        # noise *= np.sqrt(pp.chi2)
        # pp = ppxf(templates, galaxy, noise, velscale, start,
        #           goodpixels=goodpixels, plot=False, moments=2, degree=-1,
        #           vsyst=dv, clean=False, regul=0.,
        #           mdegree=0, reddening=None,lam=wave
        #           )
示例#45
0
def ppxf_kinematics_example_sauron():

    # Read a galaxy spectrum and define the wavelength range
    #
    file = 'spectra/NGC4550_SAURON.fits'
    hdu = fits.open(file)
    gal_lin = hdu[0].data
    h1 = hdu[0].header

    lamRange1 = h1['CRVAL1'] + np.array(
        [0., h1['CDELT1'] * (h1['NAXIS1'] - 1)])
    FWHM_gal = 4.2  # SAURON has an instrumental resolution FWHM of 4.2A.

    # If the galaxy is at a significant redshift (z > 0.03), one would need to apply
    # a large velocity shift in PPXF to match the template to the galaxy spectrum.
    # This would require a large initial value for the velocity (V > 1e4 km/s)
    # in the input parameter START = [V,sig]. This can cause PPXF to stop!
    # The solution consists of bringing the galaxy spectrum roughly to the
    # rest-frame wavelength, before calling PPXF. In practice there is no
    # need to modify the spectrum before the usual LOG_REBIN, given that a
    # red shift corresponds to a linear shift of the log-rebinned spectrum.
    # One just needs to compute the wavelength range in the rest-frame
    # and adjust the instrumental resolution of the galaxy observations.
    # This is done with the following three commented lines:
    #
    # z = 1.23 # Initial estimate of the galaxy redshift
    # lamRange1 = lamRange1/(1+z) # Compute approximate restframe wavelength range
    # FWHM_gal = FWHM_gal/(1+z)   # Adjust resolution in Angstrom

    galaxy, logLam1, velscale = util.log_rebin(lamRange1, gal_lin)
    galaxy = galaxy / np.median(
        galaxy)  # Normalize spectrum to avoid numerical issues
    noise = galaxy * 0 + 0.0047  # Assume constant noise per pixel here

    # Read the list of filenames from the Single Stellar Population library
    # by Vazdekis (2010, MNRAS, 404, 1639) http://miles.iac.es/. A subset
    # of the library is included for this example with permission
    vazdekis = glob.glob('miles_models/Mun1.30Z*.fits')
    FWHM_tem = 2.51  # Vazdekis+10 spectra have a constant resolution FWHM of 2.51A.

    # Extract the wavelength range and logarithmically rebin one spectrum
    # to the same velocity scale of the SAURON galaxy spectrum, to determine
    # the size needed for the array which will contain the template spectra.
    #
    hdu = fits.open(vazdekis[0])
    ssp = hdu[0].data
    h2 = hdu[0].header
    lamRange2 = h2['CRVAL1'] + np.array(
        [0., h2['CDELT1'] * (h2['NAXIS1'] - 1)])
    sspNew, logLam2, velscale = util.log_rebin(lamRange2,
                                               ssp,
                                               velscale=velscale)
    templates = np.empty((sspNew.size, len(vazdekis)))

    # Convolve the whole Vazdekis library of spectral templates
    # with the quadratic difference between the SAURON and the
    # Vazdekis instrumental resolution. Logarithmically rebin
    # and store each template as a column in the array TEMPLATES.

    # Quadratic sigma difference in pixels Vazdekis --> SAURON
    # The formula below is rigorously valid if the shapes of the
    # instrumental spectral profiles are well approximated by Gaussians.
    #
    FWHM_dif = np.sqrt(FWHM_gal**2 - FWHM_tem**2)
    sigma = FWHM_dif / 2.355 / h2['CDELT1']  # Sigma difference in pixels

    for j, file in enumerate(vazdekis):
        hdu = fits.open(file)
        ssp = hdu[0].data
        ssp = ndimage.gaussian_filter1d(ssp, sigma)
        sspNew, logLam2, velscale = util.log_rebin(lamRange2,
                                                   ssp,
                                                   velscale=velscale)
        templates[:, j] = sspNew / np.median(sspNew)  # Normalizes templates

    # The galaxy and the template spectra do not have the same starting wavelength.
    # For this reason an extra velocity shift DV has to be applied to the template
    # to fit the galaxy spectrum. We remove this artificial shift by using the
    # keyword VSYST in the call to PPXF below, so that all velocities are
    # measured with respect to DV. This assume the redshift is negligible.
    # In the case of a high-redshift galaxy one should de-redshift its
    # wavelength to the rest frame before using the line below (see above).
    #
    c = 299792.458
    dv = (logLam2[0] - logLam1[0]) * c  # km/s

    vel = 450.  # Initial estimate of the galaxy velocity in km/s
    z = np.exp(vel / c) - 1  # Relation between velocity and redshift in pPXF
    goodPixels = util.determine_goodpixels(logLam1, lamRange2, z)

    # Here the actual fit starts. The best fit is plotted on the screen.
    # Gas emission lines are excluded from the pPXF fit using the GOODPIXELS keyword.
    #
    start = [vel, 180., 0, 0]  # (km/s), starting guess for [V,sigma]
    t = clock()

    pp = ppxf(templates,
              galaxy,
              noise,
              velscale,
              start,
              goodpixels=goodPixels,
              plot=True,
              moments=4,
              degree=4,
              vsyst=dv)

    print("Formal errors:")
    print("     dV    dsigma   dh3      dh4")
    print("".join("%8.2g" % f for f in pp.error * np.sqrt(pp.chi2)))

    print('Elapsed time in PPXF: %.2f s' % (clock() - t))
def ppxf_population_gas_example_sdss(quiet=True):
    galaxy='ic1459'
    z = 0.005
    wav_range='4200-'
    out_dir = '%s/Data/vimos/analysis' % (cc.base_dir)
    output = "%s/%s/results/%s" % (out_dir, galaxy, wav_range)
    out_plots = "%splots" % (output)
    out_pickle = '%s/pickled' % (output)
    pickleFile = open("%s/dataObj_%s_pop.pkl" % (out_pickle, wav_range), 'rb')
    #pickleFile = open("%s/dataObj_%s.pkl" % (cc.home_dir, wav_range), 'rb')

    D = pickle.load(pickleFile)
    pickleFile.close()



    #------------------- Setup templates -----------------------
    c = 299792.458 # speed of light in km/s
    velscale = np.log(D.bin[100].lam[1]/D.bin[100].lam[0])*c
    FWHM_gal = 4*0.71 # SDSS has an instrumental resolution FWHM of 2.76A.

    stars_templates, lamRange_temp, logLam_temp = \
        setup_spectral_library(velscale, FWHM_gal)

    # The stellar templates are reshaped into a 2-dim array with each spectrum
    # as a column, however we save the original array dimensions, which are
    # needed to specify the regularization dimensions
    #
    reg_dim = stars_templates.shape[1:]
    stars_templates = stars_templates.reshape(stars_templates.shape[0],-1)

    # See the pPXF documentation for the keyword REGUL,
    # for an explanation of the following two lines
    #
    stars_templates /= np.median(stars_templates) # Normalizes stellar templates by a scalar
    regul_err = 0.004 # Desired regularization error







    w=[]
    for bin_number in range(D.number_of_bins):
        bin_number=32
        print(bin_number,'/',D.number_of_bins)

        # Read SDSS DR8 galaxy spectrum taken from here http://www.sdss3.org/dr8/
        # The spectrum is *already* log rebinned by the SDSS DR8
        # pipeline and log_rebin should not be used in this case.
        #

        b=D.bin[bin_number]


        # file = 'spectra/NGC3522_SDSS.fits'
        # hdu = pyfits.open(file)
        # t = hdu[1].data
        # z = float(hdu[1].header["Z"]) # SDSS redshift estimate


        # # Only use the wavelength range in common between galaxy and stellar library.
        # #
        # mask = (t.field('wavelength') > 3540) & (t.field('wavelength') < 7409)
        # galaxy = t[mask].field('flux')/np.median(t[mask].field('flux'))  # Normalize spectrum to avoid numerical issues
        # wave = t[mask].field('wavelength')
        mask = (b.lam > 3540) & (b.lam < 7409)
        galaxy = b.spectrum/np.median(b.spectrum)  # Normalize spectrum to avoid numerical issues
        wave = b.lam

        # The noise level is chosen to give Chi^2/DOF=1 without regularization (REGUL=0).
        # A constant error is not a bad approximation in the fitted wavelength
        # range and reduces the noise in the fit.
        #
        noise = galaxy*0 + 0.01528           # Assume constant noise per pixel here

       
        
        # Construct a set of Gaussian emission line templates.
        # Estimate the wavelength fitted range in the rest frame.
        #
        lamRange_gal = np.array([np.min(wave), np.max(wave)])/(1 + z)
        gas_templates, line_names, line_wave = \
            util.emission_lines(logLam_temp, lamRange_gal, FWHM_gal, quiet=quiet)

        # Combines the stellar and gaseous templates into a single array
        # during the PPXF fit they will be assigned a different kinematic
        # COMPONENT value
        #
        templates = np.hstack([stars_templates, gas_templates])

        #-----------------------------------------------------------

        # The galaxy and the template spectra do not have the same starting wavelength.
        # For this reason an extra velocity shift DV has to be applied to the template
        # to fit the galaxy spectrum. We remove this artificial shift by using the
        # keyword VSYST in the call to PPXF below, so that all velocities are
        # measured with respect to DV. This assume the redshift is negligible.
        # In the case of a high-redshift galaxy one should de-redshift its
        # wavelength to the rest frame before using the line below as described
        # in PPXF_KINEMATICS_EXAMPLE_SAURON.
        #
        dv = (np.log(lamRange_temp[0])-np.log(wave[0]))*c # km/s
        vel = c*z # Initial estimate of the galaxy velocity in km/s

        # Here the actual fit starts. The best fit is plotted on the screen.
        #
        # IMPORTANT: Ideally one would like not to use any polynomial in the fit
        # as the continuum shape contains important information on the population.
        # Unfortunately this is often not feasible, due to small calibration
        # uncertainties in the spectral shape. To avoid affecting the line strength of
        # the spectral features, we exclude additive polynomials (DEGREE=-1) and only use
        # multiplicative ones (MDEGREE=10). This is only recommended for population, not
        # for kinematic extraction, where additive polynomials are always recommended.
        #
        start = [vel, 180.] # (km/s), starting guess for [V,sigma]


        # Assign component=0 to the stellar templates and
        # component=1 to the gas emission lines templates.
        # One can easily assign different kinematic components to different gas species
        # e.g. component=1 for the Balmer series, component=2 for the [OIII] doublet, ...)
        # Input a negative MOMENTS value to keep fixed the LOSVD of a component.
        #
        nTemps = stars_templates.shape[1]
        nLines = gas_templates.shape[1]
        component = [0]*nTemps + [1]*nLines
        moments = [4, 2] # fit (V,sig,h3,h4) for the stars and (V,sig) for the gas
        start = [start, start] # adopt the same starting value for both gas and stars

        pp = ppxf(templates, galaxy, noise, velscale, start,
                  plot=True, moments=moments, degree=-1, mdegree=10,
                  vsyst=dv, clean=False, regul=1./regul_err,
                  reg_dim=reg_dim, component=component, quiet=quiet)

        plt.subplot(212)
        weights = pp.weights[:np.prod(reg_dim)].reshape(reg_dim)/pp.weights.sum()
        plt.imshow(np.rot90(weights), interpolation='nearest', 
                   cmap='gist_heat', aspect='auto', origin='upper',
                   extent=[np.log10(1), np.log10(17.7828), -1.9, 0.45])
        plt.colorbar()
        plt.title("Mass Fraction")
        plt.xlabel("log$_{10}$ Age (Gyr)")
        plt.ylabel("[M/H]")
        plt.tight_layout()

        plt.show()
        # Plot fit results for stars and gas
        weights = pp.weights[:np.prod(reg_dim)].reshape(reg_dim)/pp.weights.sum()

        
        w.append(weights)
    w = np.array(w)

    pickleFile = open("pop.pkl", 'wb')
    pickle.dump(w,pickleFile)
    pickleFile.close()
示例#47
0
def run_ppxf(spectra, velscale, ncomp=None, has_emission=True, mdegree=-1,
             degree=20, plot=False, sky=None, start=None, moments=None,
             log_dir=None, w1=4000., w2=7000.):
    """ Run pPXF in a list of spectra"""
    if isinstance(spectra, str):
        spectra = [spectra]
    ##########################################################################
    # Load templates for both stars and gas
    star_templates = pf.getdata(os.path.join(templates_dir,
                                             'miles_FWHM_3.7.fits'), 0)
    logLam2 = pf.getdata(os.path.join(templates_dir, 'miles_FWHM_3.7.fits'), 1)
    miles = np.loadtxt(os.path.join(templates_dir, 'miles_FWHM_3.7.txt'),
                       dtype=str).tolist()
    gas_templates = pf.getdata(os.path.join(templates_dir,
                                             'emission_FWHM_3.7.fits'), 0)
    logLam_gas = pf.getdata(os.path.join(templates_dir, 'emission_FWHM_3.7.fits'),
                            1)
    gas_files = np.loadtxt(os.path.join(templates_dir, 'emission_FWHM_3.7.txt'),
                       dtype=str).tolist()

    ngas = len(gas_files)
    ntemplates = len(miles)
    ##########################################################################
    # Join templates in case emission lines are used.
    if has_emission:
        templates = np.column_stack((star_templates, gas_templates))
        templates_names = np.hstack((miles, gas_files))
    else:
        templates = star_templates
        templates_names = miles
        ngas = 0
    ##########################################################################
    if sky == None:
        nsky = 0
    else:
        nsky = sky.shape[1]
    if ncomp == 1:
        components = 0
    elif ncomp == 2:
        components = np.hstack((np.zeros(len(star_templates[0])),
                                np.ones(len(gas_templates[0]))))
    if moments == None:
        moments = [4] if ncomp == 1 else [4,2]
    for i, spec in enumerate(spectra):
        print "pPXF run of spectrum {0} ({1} of {2})".format(spec, i+1,
              len(spectra))
        plt.clf()
        ######################################################################
        # Read galaxy spectrum and define the wavelength range
        hdu = pf.open(spec)
        spec_lin = hdu[0].data
        h1 = pf.getheader(spec)
        lamRange1 = h1['CRVAL1'] + np.array([0.,h1['CDELT1']*(h1['NAXIS1']-1)])
        ######################################################################
        # Rebin to log scale
        galaxy, logLam1, velscale = util.log_rebin(lamRange1, spec_lin, 
                                                   velscale=velscale)
        ######################################################################
        # First guess for the noise
        noise = np.ones_like(galaxy) * np.std(galaxy - medfilt(galaxy, 5))
        ######################################################################
        # Calculate difference of velocity between spectrum and templates
        # due to different initial wavelength
        dv = (logLam2[0]-logLam1[0])*c
        ######################################################################
        # Set first guess from setup files
        if start is None:
            start = [v0s[spec.split("_")[0]], 50]
            goodPixels = None
        ######################################################################
        # Expand start variable to include multiple components
        if ncomp > 1:
            start = [start, [start[0], 30]]
        ######################################################################
        # Select goodpixels
        w = np.exp(logLam1)
        goodpixels = np.argwhere((w>w1) & (w<w2)).T[0]
        # First pPXF interaction
        pp0 = ppxf(templates, galaxy, noise, velscale, start,
                   goodpixels=goodpixels, plot=False, moments=moments,
                   degree=degree, mdegree=mdegree, vsyst=dv, component=components,
                   sky=sky)
        rms0 = galaxy[goodpixels] - pp0.bestfit[goodpixels]
        noise0 = 1.4826 * np.median(np.abs(rms0 - np.median(rms0)))
        noise0 = np.zeros_like(galaxy) + noise0
        # Second pPXF interaction, realistic noise estimation
        pp = ppxf(templates, galaxy, noise0, velscale, start,
                  goodpixels=goodpixels, plot=False, moments=moments,
                  degree=degree, mdegree=mdegree, vsyst=dv,
                  component=components, sky=sky)
        plt.title(spec.replace("_", "-"))
        plt.show(block=False)
        plt.savefig("{1}/{0}".format(spec.replace(".fits", ".png"), log_dir))
        ######################################################################
        # Adding other things to the pp object
        pp.template_files = templates_names
        pp.has_emission = has_emission
        pp.dv = dv
        pp.w = w
        pp.velscale = velscale
        pp.spec = spec
        pp.ngas = ngas
        pp.ntemplates = ntemplates
        pp.nsky = nsky
        pp.templates = 0
        ######################################################################
        # Save fit to pickles file to keep session
        ppsave(pp, "{1}/{0}".format(spec.replace(".fits", ""), log_dir))
        pp = ppload("{1}/{0}".format(spec.replace(".fits", ""), log_dir))
        ######################################################################
        ppf = pPXF(spec, velscale, pp)
        ppf.plot("{1}/{0}".format(spec.replace(".fits", ".png"), log_dir))
        ######################################################################
        # # Save to output text file
        # if ncomp > 1:
        #     pp.sol = pp.sol[0]
        #     pp.error = pp.error[0]
        # sol = [val for pair in zip(pp.sol, pp.error) for val in pair]
        # sol = ["{0:12s}".format("{0:.3g}".format(x)) for x in sol]
        # sol.append("{0:12s}".format("{0:.3g}".format(pp0.chi2)))
        # sol = ["{0:30s}".format(spec)] + sol
    return
def ppxf_population_example_sdss():

    # Read SDSS DR8 galaxy spectrum taken from here http://www.sdss3.org/dr8/
    # The spectrum is *already* log rebinned by the SDSS DR8
    # pipeline and log_rebin should not be used in this case.
    #
    file = 'spectra/NGC3522_SDSS_DR8.fits'
    hdu = fits.open(file)
    t = hdu[1].data
    z = float(hdu[1].header["Z"])  # SDSS redshift estimate

    # Only use the wavelength range in common between galaxy and stellar library.
    #
    mask = (t['wavelength'] > 3540) & (t['wavelength'] < 7409)
    flux = t['flux'][mask]
    galaxy = flux / np.median(
        flux)  # Normalize spectrum to avoid numerical issues
    wave = t['wavelength'][mask]

    # The noise level is chosen to give Chi^2/DOF=1 without regularization (REGUL=0)
    #
    noise = galaxy * 0 + 0.01528  # Assume constant noise per pixel here

    # The velocity step was already chosen by the SDSS pipeline
    # and we convert it below to km/s
    #
    c = 299792.458  # speed of light in km/s
    velscale = np.log(wave[1] / wave[0]) * c
    FWHM_gal = 2.76  # SDSS has an instrumental resolution FWHM of 2.76A.

    templates, lamRange_temp, logAge_grid, metal_grid = \
        setup_spectral_library(velscale, FWHM_gal)

    # The galaxy and the template spectra do not have the same starting wavelength.
    # For this reason an extra velocity shift DV has to be applied to the template
    # to fit the galaxy spectrum. We remove this artificial shift by using the
    # keyword VSYST in the call to PPXF below, so that all velocities are
    # measured with respect to DV. This assume the redshift is negligible.
    # In the case of a high-redshift galaxy one should de-redshift its
    # wavelength to the rest frame before using the line below as described
    # in PPXF_KINEMATICS_EXAMPLE_SAURON.
    #
    c = 299792.458
    dv = c * np.log(lamRange_temp[0] / wave[0])  # km/s
    goodpixels = util.determine_goodpixels(np.log(wave), lamRange_temp, z)

    # Here the actual fit starts. The best fit is plotted on the screen.
    #
    # IMPORTANT: Ideally one would like not to use any polynomial in the fit
    # as the continuum shape contains important information on the population.
    # Unfortunately this is often not feasible, due to small calibration
    # uncertainties in the spectral shape. To avoid affecting the line strength of
    # the spectral features, we exclude additive polynomials (DEGREE=-1) and only use
    # multiplicative ones (MDEGREE=10). This is only recommended for population, not
    # for kinematic extraction, where additive polynomials are always recommended.
    #
    vel = c * np.log(1 + z)  # Initial estimate of the galaxy velocity in km/s
    start = [vel, 180., 0, 0]  # (km/s), starting guess for [V,sigma]

    # See the pPXF documentation for the keyword REGUL,
    # for an explanation of the following two lines
    #
    templates /= np.median(templates)  # Normalizes templates by a scalar
    regul_err = 0.004  # Desired regularization error

    t = clock()

    plt.clf()
    plt.subplot(211)

    pp = ppxf(templates,
              galaxy,
              noise,
              velscale,
              start,
              goodpixels=goodpixels,
              plot=True,
              moments=4,
              degree=-1,
              vsyst=dv,
              clean=False,
              mdegree=10,
              regul=1. / regul_err)

    # When the two numbers below are the same, the solution is the smoothest
    # consistent with the observed spectrum.
    #
    print('Desired Delta Chi^2: %.4g' % np.sqrt(2 * goodpixels.size))
    print('Current Delta Chi^2: %.4g' % ((pp.chi2 - 1) * goodpixels.size))
    print('Elapsed time in PPXF: %.2f s' % (clock() - t))

    print('Mass-weighted <logAge> [Gyr]: %.3g' %
          (np.sum(pp.weights * logAge_grid.ravel()) / np.sum(pp.weights)))
    print('Mass-weighted <[M/H]>: %.3g' %
          (np.sum(pp.weights * metal_grid.ravel()) / np.sum(pp.weights)))

    plt.subplot(212)
    s = templates.shape
    weights = pp.weights.reshape(s[1:]) / pp.weights.sum()
    plt.imshow(np.rot90(weights),
               interpolation='nearest',
               cmap='gist_heat',
               aspect='auto',
               origin='upper',
               extent=[np.log10(1), np.log10(17.7828), -1.9, 0.45])
    plt.colorbar()
    plt.title("Mass Fraction")
    plt.xlabel("log$_{10}$ Age (Gyr)")
    plt.ylabel("[M/H]")
    plt.tight_layout()
    plt.show()
def ppxf_CaT(wavelengths, templates, fluxes, sigmas, vel_scale, delta_v, start_rv, start_sigma, goodpixels, quiet):
    '''
    Run pPXF on a given spectrum
    
    This function runs pPXF on a spectrum using the given templates and returns the
    fitted spectrum, radial velocity, velocity dispersion and CaT index strength.  
    
    Parameters
    ----------
    
    wavelengths : numpy array
        Linearly dispersed input wavelengths
        
    templates : numpy array
        Logarithmically dispersed fluxes of templates. The shape of the array is
        (M, N) where M is number of pixels in a template and N is the number of
        templates 
    
    fluxes : numpy array
        Logarithmically dispersed input fluxes
        
    sigmas : numpy array
        Logarithmically dispersed input sigmas
        
    vel_scale : float
        Natural logarithmic dispersion converted into velocity
        
    delta_v : float
        Difference in natural logarithmic starting wavelengths of the input spectrum
        and the templates in velocity
    
    start_rv : float
        Starting radial velocity for fit
        
    start_sigma : float
        Starting velocity dispersion for fit
        
    goodpixels : numpy array
        Indices of pixels to be fit 
        
    quite : bool
        Whether pPXF should be verbose or not
        
    
    
    Returns
    -------
        
    log_output_fluxes : numpy array
        Logarithmically dispersed fitted fluxes
    
    output_wavelengths : numpy array
        Linearly dispersed fitted wavelengths
        
    output_fluxes : numpy array
        Linear dispersed fitted fluxes
        
    normed_fluxes : numpy array
        Continuum normalised fluxes
        
    rv : float
        Fitted radial velocity
        
    sigma : float
        Fitted velocity dispersion
        
    CaT : float
        Measured CaT strength
    
    '''
    output = ppxf.ppxf(templates, fluxes, sigmas, vel_scale, moments=2, mdegree=7, degree=-1, clean=True, vsyst=delta_v, start=[start_rv, start_sigma], quiet=quiet, goodpixels=goodpixels)

    #convert fitted spectra to linear dispersion and de-redshift
    log_output_fluxes = output.bestfit
    output_wavelengths, output_fluxes = interp.logtolinear(wavelengths, log_output_fluxes, function='nearest', ratio=True)
    rv = output.sol[0]
    sigma = output.sol[1]
    zp1 = 1 + rv * 1e3 / constants.c
    output_wavelengths = output_wavelengths / zp1

    #normalise fitted spectrum and measure CaT strength
    normed_fluxes = normalisation.normalise(output_wavelengths, output_fluxes, 8, normalisation.newmask, True, .004, .010)
    CaT = indices.CaT_gc(output_wavelengths, normed_fluxes, normalized=True)[0]

    return log_output_fluxes, output_wavelengths, output_fluxes, normed_fluxes, rv, sigma, CaT
示例#50
0
def ppxf_example_simulation():

    file_dir = path.dirname(path.realpath(__file__))  # path of this procedure

    hdu = fits.open(file_dir + '/veltemps/bd-013097_M2III_rebinflux_rest.fits'
                    )  # BUCKET: which star should I choose???
    # '/miles_models/Mun1.30Zp0.00T12.5893_iPp0.00_baseFe_linear_FWHM_2.51.fits')  # Solar metallicitly, Age=12.59 Gyr
    ssp = hdu[
        1].data  # hdu[1] is science header for us; hdu[0] is generic header
    h = hdu[1].header

    lamRange = h['CRVAL1'] + np.array([0., h['CD1_1'] * (h['NAXIS1'] - 1)])
    c = 299792.458  # speed of light in km/s
    velscale = c * h['CD1_1'] / max(
        lamRange)  # Do not degrade original velocity sampling
    star, logLam, velscale = util.log_rebin(lamRange, ssp, velscale=velscale)

    # The finite sampling of the observed spectrum is modeled in detail:
    # the galaxy spectrum is obtained by oversampling the actual observed spectrum
    # to a high resolution. This represent the true spectrum, which is later resampled
    # to lower resolution to simulate the observations on the CCD. Similarly, the
    # convolution with a well-sampled LOSVD is done on the high-resolution spectrum,
    # and later resampled to the observed resolution before fitting with PPXF.

    factor = 10  # Oversampling integer factor for an accurate convolution
    starNew = ndimage.interpolation.zoom(
        star, factor,
        order=3)  # This is the underlying spectrum, known at high resolution
    star = rebin(
        starNew, factor
    )  # Make sure that the observed spectrum is the integral over the pixels

    # find worst SNR, and worst h3, h4 (highest)
    h3 = 0.1  # Adopted G-H parameters of the LOSVD
    h4 = 0.04  # 0.1
    sn = 30.  # Adopted S/N of the Monte Carlo simulation
    m = 300  # Number of realizations of the simulation
    moments = 4
    velV = np.random.rand(
        m
    )  # velocity in *pixels* [=V(km/s)/velScale]  # (len m array of random on [0, 1)
    sigmaV = np.linspace(
        0.5, 4, m
    )  # Range of sigma in *pixels* [=sigma(km/s)/velScale]  # m evenly spaced [0.5,...,4]

    result = np.zeros((m, moments))  # This will store the results
    t = clock()

    for j, (vel, sigma) in enumerate(zip(velV, sigmaV)):

        dx = int(
            abs(vel) +
            5 * sigma)  # Sample the Gaussian and GH at least to vel+5*sigma
        x = np.linspace(
            -dx, dx, 2 * dx * factor +
            1)  # Evaluate the Gaussian using steps of 1/factor pixels.
        w = (x - vel) / sigma
        w2 = w**2
        gauss = np.exp(-0.5 * w2)
        gauss /= np.sum(gauss)  # Normalized total(gauss)=1
        h3poly = w * (2. * w2 - 3.) / np.sqrt(3.)  # H3(y)
        h4poly = (w2 * (4. * w2 - 12.) + 3.) / np.sqrt(24.)  # H4(y)
        losvd = gauss * (1. + h3 * h3poly + h4 * h4poly)

        galaxy = signal.fftconvolve(
            starNew, losvd, mode="same")  # Convolve the oversampled spectrum
        # FFT convolution: multiplication in the frequency domain corresponds to convolution in the time domain
        galaxy = rebin(
            galaxy, factor)  # Integrate spectrum into original spectral pixels
        noise = galaxy / sn  # 1sigma error spectrum
        galaxy = np.random.normal(galaxy,
                                  noise)  # Add noise to the galaxy spectrum
        start = np.array([
            vel + np.random.uniform(-1, 1),
            sigma * np.random.uniform(0.8, 1.2)
        ]) * velscale  # Convert to km/s

        pp = ppxf(star,
                  galaxy,
                  noise,
                  velscale,
                  start,
                  goodpixels=np.arange(dx, galaxy.size - dx),
                  plot=False,
                  moments=moments,
                  bias=0.4)  # 0.2
        result[j, :] = pp.sol

    print('Calculation time: %.2f s' % (clock() - t))

    # large scale kinematics to estimate dark matter fraction within effective radii, and stellar mass-light ratio
    # mass distribution discribed by GME and dark matter dstribution different
    # work with sarah to use JAM; figure out why we ever use schwarzschild model if JAM is faster (what assumptions are
    # we making/what regimes do we have to be in to use JAM?

    plt.clf()
    plt.subplot(221)
    plt.plot(sigmaV * velscale, result[:, 0] - velV * velscale,
             '+k')  # BUCKET: why is V multiplied by velscale?
    plt.axhline(0, color='r')
    plt.axvline(velscale, linestyle='dashed')
    plt.axvline(2 * velscale, linestyle='dashed')
    plt.ylim(-20, 20)
    plt.xlabel(r'$\sigma_{\rm in}\ (km\ s^{-1})$')
    plt.ylabel(r'$V - V_{\rm in}\ (km\ s^{-1})$')
    plt.text(2.05 * velscale, -15, r'2$\times$velscale')

    plt.subplot(222)
    plt.plot(sigmaV * velscale, result[:, 1] - sigmaV * velscale,
             '+k')  # BUCKET: why is sigma multiplied by velscale?
    plt.axhline(0, color='r')
    plt.axvline(velscale, linestyle='dashed')
    plt.axvline(2 * velscale, linestyle='dashed')
    plt.ylim(-20, 20)
    plt.xlabel(r'$\sigma_{in}\ (km\ s^{-1})$')
    plt.ylabel(r'$\sigma - \sigma_{\rm in}\ (km\ s^{-1})$')
    plt.text(2.05 * velscale, -15, r'2$\times$velscale')

    plt.subplot(223)
    plt.plot(sigmaV * velscale, result[:, 2], '+k')
    plt.axhline(h3, color='r')
    plt.axhline(0, linestyle='dotted', color='limegreen')
    plt.axvline(velscale, linestyle='dashed')
    plt.axvline(2 * velscale, linestyle='dashed')
    plt.ylim(-0.2 + h3, 0.2 + h3)
    plt.xlabel(r'$\sigma_{\rm in}\ (km\ s^{-1})$')
    plt.ylabel('$h_3$')
    plt.text(2.05 * velscale, h3 - 0.15, r'2$\times$velscale')

    plt.subplot(224)
    plt.plot(sigmaV * velscale, result[:, 3], '+k')
    plt.axhline(h4, color='r')
    plt.axhline(0, linestyle='dotted', color='limegreen')
    plt.axvline(velscale, linestyle='dashed')
    plt.axvline(2 * velscale, linestyle='dashed')
    plt.ylim(-0.2 + h4, 0.2 + h4)
    plt.xlabel(r'$\sigma_{\rm in}\ (km\ s^{-1})$')
    plt.ylabel('$h_4$')
    plt.text(2.05 * velscale, h4 - 0.15, r'2$\times$velscale')

    plt.tight_layout()
    plt.show()  # pause(1)
示例#51
0
def ppxf_nifs_kinematics(newgal=None, resid=None, centers=None, coords=[0, 0]):

    file_dir = path.dirname(path.realpath(__file__))  # path of this procedure

    # Read a galaxy spectrum and define the wavelength range
    file = file_dir + '/spectra/pgc12557_combined.fits'  # '/spectra/NGC4550_SAURON.fits'  # my current file location
    hdu = fits.open(file)
    gal_lin = hdu[1].data  # gal_lin = hdu[0].data
    h1 = hdu[
        1].header  # I need to use 1st extension header (0=general, 1=science, 2=variance, 3=data quality flags)

    lamRange1 = h1['CRVAL3'] + np.array(
        [0., h1['CD3_3'] *
         (h1['NAXIS3'] - 1)])  # [ 19971.86914062  24319.31070422]
    print(lamRange1, 'l1')

    # print(gal_lin[0][20]) all 0s  # print((gal_lin[300][35])) NOT all 0s!
    # len(gal_lin) = 2040, len(gal_lin[0]) = 69, len(gal_lin[0][1]) = 71
    # 2040 --> NAXIS 3, 69 --> NAXIS2, 71 --> NAXIS1
    # There's a len 2040 spectrum at each gal_lin[:,x,y] --> gal_lin[:, x, y] is an array len(2040) starting at
    # lamRange1[0] and finishing at lamRange1[1], with each pixel in between separated by h1['CD3_3'].

    # CUT SPECTRUM TO WAVELENGTH RANGE 2.26 - 2.42
    low_lim = 22600.
    up_lim = 24200.
    cut1 = int(
        (low_lim - lamRange1[0]) / h1['CD3_3']
    )  # num pixels between pix 1 & pix corresponding to 2.26 microns
    cut2 = int(
        (up_lim - lamRange1[0]) / h1['CD3_3']
    )  # num pixels between pix 1 & pix corresponding to 2.42 microns
    # print(cut1, cut2, 'cuts')  # 1232.62354281, 1983.04191009 --> int(cut1) = 1232, int(cut2) = 1983
    gal_lin = gal_lin[cut1:
                      cut2]  # cut gal_lin spectrum to new wavelength range
    start = h1['CRVAL3'] + h1['CD3_3'] * cut1
    stop = h1['CRVAL3'] + h1['CD3_3'] * cut2
    lamRange1 = [start, stop
                 ]  # redefine lamRange1 to correspond to new wavelength range
    print(lamRange1, 'l1, cut')
    # len(gal_lin) is now NOT 2040 but 1983 - 1233 = 750

    FWHM_gal = 4.2  # SAURON has an instrumental resolution FWHM of 4.2A.  # BUCKET: do I need this? If so what for?

    # If the galaxy is at significant redshift, one should bring the galaxy
    # spectrum roughly to the rest-frame wavelength, before calling pPXF
    # (See Sec2.4 of Cappellari 2017). In practice there is no
    # need to modify the spectrum in any way, given that a red shift
    # corresponds to a linear shift of the log-rebinned spectrum.
    # One just needs to compute the wavelength range in the rest-frame
    # and adjust the instrumental resolution of the galaxy observations.
    # This is done with the following three commented lines:
    #
    # z = 1.23 # Initial estimate of the galaxy redshift
    # lamRange1 = lamRange1/(1+z) # Compute approximate restframe wavelength range
    # FWHM_gal = FWHM_gal/(1+z)   # Adjust resolution in Angstrom

    # There's a len 2040 spectrum at each gal_lin[:,x,y]
    x = coords[0]
    y = coords[1]
    # if newgal is None:
    galaxy, logLam1, velscale = util.log_rebin(
        lamRange1, gal_lin[:, x, y])  # no input velscale --> fcn returns it
    print(len(galaxy), 'len gal')  # 750 now because of cut to gal_lin!
    print(np.median(galaxy))
    galaxy = galaxy / np.median(
        galaxy)  # Normalize spectrum to avoid numerical issues
    # else:
    #     galaxy, logLam1, velscale = util.log_rebin(lamRange1, newgal)  # newgal is the spectrum at coords x, y

    # basically bootstrap the noise!
    # Do one fit with flat noise, then save the best fit spectrum and the residuals.
    # Then, iterate ~200 times. For these iterations, set bias = 0.0. Each iteration, for each pixel, use the spectrum
    # value as the center of a gaussian and use the residuals at that pixel value as the width of the gaussian. Draw
    # from the resultant distribution to make the new noise. For each iteration, save the output V, sigma, h3, h4, and
    # print each spectrum so that we can see it evolving (it should look more like a real spectrum, rather than smooth
    # curve without lines)
    noise = np.full_like(galaxy,
                         0.0047)  # Assume constant noise per pixel here

    # MEANTIME: why is output velocity close to systemic insted of close to 0, if I'm dealing with redshift already?
    # SET bias = 0
    #
    print('shape', noise.shape)  # 751,
    # print(galaxy.shape)  # 751,

    # Read the list of filenames from the Single Stellar Population library
    # by Vazdekis (2010, MNRAS, 404, 1639) http://miles.iac.es/. A subset
    # of the library is included for this example with permission
    vazdekis = glob.glob(
        file_dir +
        '/veltemps/*.fits')  # '/miles_models/Mun1.30Z*.fits')  # my new
    # BUCKET: what are FWHM of spectra in the veltemps library??
    FWHM_tem = 2.51  # Vazdekis+10 spectra have a constant resolution FWHM of 2.51A.
    # BUCKET: what spectral sampling compared to galaxy do we want for templates??

    # velscale_ratio = 2  # 1.23  # 2  # adopts 2x higher spectral sampling for templates than for galaxy
    # PROBLEM!! If velscale_ratio is not integer, we get issue later because we slice a list with velscale_ratio
    # so need to change velscale? But it's set by util.log_rebin originally! Only need this if oversampling the
    # templates, which I'm not doing

    # Extract the wavelength range and logarithmically rebin one spectrum
    # to a velocity scale 2x smaller than the SAURON galaxy spectrum, to determine
    # the size needed for the array which will contain the template spectra.
    #
    print(vazdekis[0], 'template name')
    hdu = fits.open(
        vazdekis[0]
    )  # do for just one template to determine size needed for array containing all templates
    ssp = hdu[
        1].data  # was hdu[0], but that's generic header rather than science header
    h2 = hdu[1].header  # was hdu[0]

    lamRange2 = h2['CRVAL1'] + np.array([0., h2['CD1_1'] * (h2['NAXIS1'] - 1)
                                         ])  # BUCKET want NAXIS - 1?
    print(lamRange2, 'l2')  # [ 20628.29  24291.89]
    # print((lamRange2[1] - lamRange2[0])/h2['CD1_1'], 'num of steps in lam2')  # 1720.
    # print(len(ssp), 'len ssp')  # 1721
    sspNew, logLam2, velscale_temp = util.log_rebin(
        lamRange2, ssp, velscale=velscale)  # /velscale_ratio)
    # print(len(sspNew), 'len sspnew')  # 622 hmmmm NEED THIS TO BE >= (len(galaxy)=750)  # FIXED, now 1791
    templates = np.empty((sspNew.size, len(vazdekis)))

    # Convolve the whole Vazdekis library of spectral templates
    # with the quadratic difference between the SAURON and the
    # Vazdekis instrumental resolution. Logarithmically rebin
    # and store each template as a column in the array TEMPLATES.

    # Quadratic sigma difference in pixels Vazdekis --> SAURON
    # The formula below is rigorously valid if the shapes of the
    # instrumental spectral profiles are well approximated by Gaussians.
    #
    FWHM_dif = np.sqrt(FWHM_gal**2 - FWHM_tem**2)
    # sigma = FWHM_dif/2.355/h2['CDELT1']  # Sigma difference in pixels
    sigma = FWHM_dif / 2.355 / h2['CD1_1']  # Sigma difference in pixels

    for j, file in enumerate(
            vazdekis
    ):  # now for each template file; so why do the thing above with just 1??
        hdu = fits.open(file)
        # ssp = hdu[0].data
        ssp = hdu[1].data
        ssp = ndimage.gaussian_filter1d(ssp, sigma)
        # ndimage.gaussian_filter takes input array (ssp) and filters it. Sigma = standard deviation of Gaussian kernel
        # used to filter the array
        # note: discrete convolution is defined (for any 2 arrays a, v): (a*v)[n] == sum m(-inf to inf) of a[m]*v[n-m]
        # where a is the original curve and v is the gaussian
        # print(len(ssp))  # 1721 --> currently by default being resampled to be 116, want it to be resampled to be 71
        sspNew, logLam2, velscale_temp = util.log_rebin(
            lamRange2, ssp, velscale=velscale)  # /velscale_ratio)
        # print(velscale_temp, 'vt')  # 78.82967746
        # print(velscale, 'v')  # 78.82967746
        # print(len(sspNew))  # need this to be >= len(galaxy)  # now 1791
        templates[:, j] = sspNew / np.median(sspNew)  # Normalizes templates

    # print(len(templates[0]))  # len(templates)=29, len(templates[0] = 19)
    # The galaxy and the template spectra do not have the same starting wavelength.
    # For this reason an extra velocity shift DV has to be applied to the template
    # to fit the galaxy spectrum. We remove this artificial shift by using the
    # keyword VSYST in the call to PPXF below, so that all velocities are
    # measured with respect to DV. This assume the redshift is negligible.
    # In the case of a high-redshift galaxy one should de-redshift its
    # wavelength to the rest frame before using the line below (see above).
    #
    c = 299792.458
    dv = (logLam2[0] - logLam1[0]) * c  # km/s
    '''
    if velscale_ratio > 1:
        dv = (np.mean(logLam2[:velscale_ratio]) - logLam1[0])*c  # km/s
    else:
        dv = (logLam2[0] - logLam1[0])*c  # km/s
    '''

    z = 0.016561  # z = 0.0015  # Initial redshift estimate of the galaxy
    goodPixels = util.determine_goodpixels(logLam1, lamRange2, z)

    # Here the actual fit starts. The best fit is plotted on the screen.
    # Gas emission lines are excluded from the pPXF fit using the GOODPIXELS keyword.
    #
    vel = c * np.log(1 + z)  # eq.(8) of Cappellari (2017)
    start = [vel, 200.]  # [vel, 200.]  # (km/s), starting guess for [V, sigma]
    t = clock()
    #  print(galaxy.shape[0])  # = len(galaxy) = 750
    # print(goodPixels)

    # print(max(noise[:, x, y]), min(noise[:, x, y]), np.median(noise[:, x, y]), np.mean(noise[:, x, y]))
    # 0.00106575 -2.77079e-05 4.62628e-05 5.89877e-05
    if newgal is None:
        pp = ppxf(templates,
                  galaxy,
                  noise,
                  velscale,
                  start,
                  goodpixels=goodPixels,
                  plot=True,
                  moments=4,
                  degree=4,
                  vsyst=dv,
                  velscale_ratio=1,
                  bias=0.)  # velscale_ratio=velscale_ratio)

        stuff = pp.bestfit, pp.galaxy, pp.sol, goodPixels
    else:
        pp = ppxf(templates,
                  galaxy,
                  noise,
                  velscale,
                  start,
                  goodpixels=goodPixels,
                  plot=True,
                  moments=4,
                  degree=4,
                  vsyst=dv,
                  velscale_ratio=1,
                  bias=0.)  # velscale_ratio=velscale_ratio)
        pp_new = ppxf(templates,
                      newgal,
                      noise,
                      velscale,
                      start,
                      goodpixels=goodPixels,
                      plot=False,
                      moments=4,
                      degree=4,
                      vsyst=dv,
                      velscale_ratio=1,
                      bias=0.)  # velscale_ratio=velscale_ratio)
        #   pp.plot()      # Plot best fit and gas lines
        x_ax = np.arange(galaxy.size)
        plt.plot(x_ax, pp.galaxy, 'k')
        plt.plot(x_ax, newgal, 'b')

        stuff = pp_new.bestfit, pp_new.galaxy, pp_new.sol

    print("Formal errors:")
    print("     dV    dsigma   dh3      dh4")
    print("".join("%8.2g" % f for f in pp.error * np.sqrt(pp.chi2)))

    print('Elapsed time in pPXF: %.2f s' % (clock() - t))

    # If the galaxy is at significant redshift z and the wavelength has been
    # de-redshifted with the three lines "z = 1.23..." near the beginning of
    # this procedure, the best-fitting redshift is now given by the following
    # commented line (equation 2 of Cappellari et al. 2009, ApJ, 704, L34;
    # http://adsabs.harvard.edu/abs/2009ApJ...704L..34C)
    #
    # print('Best-fitting redshift z:', (z + 1)*(1 + pp.sol[0]/c) - 1)

    return stuff  # output_spectrum, output_noise  # for use in noise_in
示例#52
0
def run_ppxf(spectra,
             velscale,
             ncomp=2,
             has_emission=True,
             mdegree=-1,
             degree=20,
             pkls=None,
             plot=False,
             data_sky=None):
    """ Run pPXF in a list of spectra"""
    if isinstance(spectra, str):
        spectra = [spectra]
    if isinstance(pkls, str):
        pkls = [pkls]
    if pkls == None:
        pkls = [x.replace(".fits", ".pkl") for x in spectra]
    ##########################################################################
    # Load templates for both stars and gas
    star_templates, logLam2, delta, miles = stellar_templates(velscale)
    gas_templates, logLam_gas, delta_gas, gas_files = emission_templates(
        velscale)
    ##########################################################################
    # Join templates in case emission lines are used.
    if has_emission:
        templates = np.column_stack((star_templates, gas_templates))
    else:
        templates = star_templates
    ##########################################################################
    if ncomp == 1:
        components = 0
        moments = [4]
        templates_names = miles
    elif ncomp == 2:
        components = np.hstack(
            (np.zeros(len(star_templates[0])), np.ones(len(gas_templates[0]))))
        moments = [4, 2]
        templates_names = np.hstack((miles, gas_files))

    else:
        raise Exception("ncomp has to be 1 or 2.")
    for i, spec in enumerate(spectra):
        print "pPXF run of spectrum {0} ({1} of {2})".format(
            spec, i + 1, len(spectra))
        pkl = pkls[i]
        ######################################################################
        # Read one galaxy spectrum and define the wavelength range
        specfile = os.path.join(data_dir, spec)
        hdu = pf.open(specfile)
        spec_lin = hdu[0].data
        h1 = pf.getheader(specfile)
        lamRange1 = h1['CRVAL1'] + np.array(
            [0., h1['CDELT1'] * (h1['NAXIS1'] - 1)])
        ######################################################################
        # Degrade observed spectra to match template resolution
        FWHM_dif = np.sqrt(FWHM_tem**2 - FWHM_spec**2)
        sigma = FWHM_dif / 2.355 / delta  # Sigma difference in pixels
        spec_lin = ndimage.gaussian_filter1d(spec_lin, sigma)
        ######################################################################
        # Rebin to log scale
        galaxy, logLam1, velscale = util.log_rebin(lamRange1,
                                                   spec_lin,
                                                   velscale=velscale)
        ######################################################################
        # First guess for the noise
        noise = np.ones_like(galaxy) * np.std(galaxy - medfilt(galaxy, 5))
        ######################################################################
        # Calculate difference of velocity between spectrum and templates
        # due to different initial wavelength
        dv = (logLam2[0] - logLam1[0]) * c
        ######################################################################
        # Set first guess from setup files
        start, goodPixels = read_setup_file(spec, logLam1, mask_emline=False)
        ######################################################################
        # Expand start variable to include multiple components
        if ncomp > 1:
            start = [start, [start[0], 30]]
        ######################################################################
        # Read sky in needed
        if data_sky == None:
            sky = None
        else:
            sky_lin = pf.getdata(data_sky[i])
            sky_lin = ndimage.gaussian_filter1d(sky_lin, sigma)
            sky, logLam1, velscale = util.log_rebin(lamRange1,
                                                    sky_lin,
                                                    velscale=velscale)
            sky = sky.reshape(-1, 1)
        ######################################################################
        # First pPXF interaction
        if os.path.exists(spec.replace(".fits", ".pkl")):
            pp0 = pPXF(spec, velscale, pklfile=spec.replace(".fits", ".pkl"))
            noise0 = pp0.noise
        else:
            pp0 = ppxf(templates,
                       galaxy,
                       noise,
                       velscale,
                       start,
                       goodpixels=goodPixels,
                       plot=False,
                       moments=moments,
                       degree=12,
                       mdegree=-1,
                       vsyst=dv,
                       component=components,
                       sky=sky)
            rms0 = galaxy[goodPixels] - pp0.bestfit[goodPixels]
            noise0 = 1.4826 * np.median(np.abs(rms0 - np.median(rms0)))
            noise0 = np.zeros_like(galaxy) + noise0
        # Second pPXF interaction, realistic noise estimation
        pp = ppxf(templates,
                  galaxy,
                  noise0,
                  velscale,
                  start,
                  goodpixels=goodPixels,
                  plot=plot,
                  moments=moments,
                  degree=degree,
                  mdegree=mdegree,
                  vsyst=dv,
                  component=components,
                  sky=sky)
        # pp.template_files = templates_names
        # pp.has_emission = has_emission
        ######################################################################
        # Save to output file to keep session
        with open(pkl, "w") as f:
            pickle.dump(pp, f)
        ######################################################################
    return
def ppxf_population_example_sdss():

    # Read SDSS DR8 galaxy spectrum taken from here http://www.sdss3.org/dr8/
    # The spectrum is *already* log rebinned by the SDSS DR8
    # pipeline and log_rebin should not be used in this case.
    #
    file = 'spectra/NGC3522_SDSS.fits'
    hdu = pyfits.open(file)
    t = hdu[1].data
    z = float(hdu[1].header["Z"]) # SDSS redshift estimate

    # Only use the wavelength range in common between galaxy and stellar library.
    #
    mask = (t.field('wavelength') > 3540) & (t.field('wavelength') < 7409)
    galaxy = t[mask].field('flux')/np.median(t[mask].field('flux'))  # Normalize spectrum to avoid numerical issues
    wave = t[mask].field('wavelength')

    # The noise level is chosen to give Chi^2/DOF=1 without regularization (REGUL=0)
    #
    noise = galaxy*0 + 0.01528           # Assume constant noise per pixel here

    # The velocity step was already chosen by the SDSS pipeline
    # and we convert it below to km/s
    #
    c = 299792.458 # speed of light in km/s
    velscale = np.log(wave[1]/wave[0])*c
    FWHM_gal = 2.76 # SDSS has an instrumental resolution FWHM of 2.76A.

    templates, lamRange_temp = setup_spectral_library(velscale, FWHM_gal)

    # The galaxy and the template spectra do not have the same starting wavelength.
    # For this reason an extra velocity shift DV has to be applied to the template
    # to fit the galaxy spectrum. We remove this artificial shift by using the
    # keyword VSYST in the call to PPXF below, so that all velocities are
    # measured with respect to DV. This assume the redshift is negligible.
    # In the case of a high-redshift galaxy one should de-redshift its
    # wavelength to the rest frame before using the line below as described
    # in PPXF_KINEMATICS_EXAMPLE_SAURON.
    #
    c = 299792.458
    dv = (np.log(lamRange_temp[0])-np.log(wave[0]))*c # km/s
    vel = c*z # Initial estimate of the galaxy velocity in km/s
    goodpixels = util.determine_goodpixels(np.log(wave),lamRange_temp,vel)

    # Here the actual fit starts. The best fit is plotted on the screen.
    #
    # IMPORTANT: Ideally one would like not to use any polynomial in the fit
    # as the continuum shape contains important information on the population.
    # Unfortunately this is often not feasible, due to small calibration
    # uncertainties in the spectral shape. To avoid affecting the line strength of
    # the spectral features, we exclude additive polynomials (DEGREE=-1) and only use
    # multiplicative ones (MDEGREE=10). This is only recommended for population, not
    # for kinematic extraction, where additive polynomials are always recommended.
    #
    start = [vel, 180.] # (km/s), starting guess for [V,sigma]

    # See the pPXF documentation for the keyword REGUL,
    # for an explanation of the following two lines
    #
    templates /= np.median(templates) # Normalizes templates by a scalar
    regul_err = 0.004 # Desired regularization error

    t = clock()

    plt.clf()
    plt.subplot(211)

    pp = ppxf(templates, galaxy, noise, velscale, start,
              goodpixels=goodpixels, plot=True, moments=4, degree=-1,
              vsyst=dv, clean=False, mdegree=10, regul=1./regul_err)

    # When the two numbers below are the same, the solution is the smoothest
    # consistent with the observed spectrum.
    #
    print('Desired Delta Chi^2: %.4g' % np.sqrt(2*goodpixels.size))
    print('Current Delta Chi^2: %.4g' % ((pp.chi2 - 1)*goodpixels.size))
    print('Elapsed time in PPXF: %.2f s' % (clock() - t))

    plt.subplot(212)
    s = templates.shape
    print(s)
    weights = pp.weights.reshape(s[1],s[2])/pp.weights.sum()
    plt.imshow(np.rot90(weights), interpolation='nearest', cmap='gist_heat',
               aspect='auto', extent=(np.log10(1.0), np.log10(17.7828), -1.9, 0.45))
    plt.colorbar()
    plt.title("Mass Fraction")
    plt.xlabel("log$_{10}$ Age (Gyr)")
    plt.ylabel("[M/H]")
    plt.tight_layout()
    plt.show()

    vazdekis = glob.glob('miles_models/Mun1.30*.fits')
    '''
示例#54
0
def find_best_template(wl_obs, flux, err, hdr, spectral_library):

    t2 = clock()

    # Read a spectrum and define the wavelength range
    obs_spectrum = flux
    obs_spectrum_header = hdr
    obs_error_spectrum = abs(err)

    obs_lambda_range = np.array([min(wl), max(wl)])
    z = 0.0  # Initial estimate of the galaxy redshift
    obs_lambda_range = obs_lambda_range / (
        1 + z)  # Compute approximate restframe wavelength range

    #Get median of positive values
    m = np.median(obs_error_spectrum[obs_error_spectrum > 0])
    # Assign the median to the negative elements
    obs_error_spectrum[obs_error_spectrum <= 0] = m

    #logarithmically rebin while conserving flux
    tell_obs, obs_lambda, velscale = util.log_rebin(obs_lambda_range,
                                                    obs_spectrum)
    tell_obs_err, obs_lambda, velscale = util.log_rebin(
        obs_lambda_range, obs_error_spectrum)

    # Normalize to avoid numerical issues
    norm = tell_obs[int(len(tell_obs) / 2)]
    tell_obs = tell_obs / norm
    tell_obs_err = tell_obs_err / norm

    # Load and prepare Model stellar library
    # Extract the wavelength range and logarithmically rebin one spectrum
    # to the same velocity scale of the target spectrum, to determine
    # the size needed for the array which will contain the template spectra.

    hdu = fits.open(spectral_library[0])
    library_spectrum = hdu[0].data
    library_spectrum_header = hdu[0].header
    try:
        wl_lib = np.e**(np.arange((library_spectrum_header['NAXIS1'])) *
                        library_spectrum_header['CDELT1'] +
                        library_spectrum_header['CRVAL1'])
    except:
        wl_lib = fits.open(
            "/Users/jselsing/Work/spectral_libraries/phoenix_spectral_library/WAVE_PHOENIX-ACES-AGSS-COND-2011.fits"
        )[0].data

    #Make empty template holder
    f = splrep(wl_lib, library_spectrum, k=1)
    hdu_wave_short = splev(wl_obs, f)
    lib_lambda_range = np.array([min(wl_obs), max(wl_obs)])
    tell_lib, lib_lambda, velscale = util.log_rebin(lib_lambda_range,
                                                    hdu_wave_short,
                                                    velscale=velscale)
    templates = np.empty((tell_lib.size, len(spectral_library)))

    # Convolve the whole library of spectral templates
    # with the quadratic difference between the target and the
    # library instrumental resolution. Logarithmically rebin
    # and store each template as a column in the array TEMPLATES.

    for j in range(len(spectral_library)):
        t = clock()
        hdu = fits.open(spectral_library[j])
        library_spectrum = hdu[0].data

        # Interpolate template spectrum to match input spectrum
        f = splrep(wl_lib, library_spectrum, k=1)
        interpolated_library_spectrum = splev(wl_obs, f)

        # Logarithmically rebin template spectra
        tell_lib, lib_lambda, velscale = util.log_rebin(
            lib_lambda_range, interpolated_library_spectrum, velscale=velscale)
        norm = tell_lib[int(len(tell_obs) / 2)]
        tell_lib = tell_lib / norm

        templates[:,
                  j] = tell_lib / np.median(tell_lib)  # Normalizes templates
        if j % 60 == 0:
            print 'Approximated remaining time (s) for setup of template spectra: ' + str(
                len(spectral_library) * (clock() - t) - j *
                (clock() - t)) + 's'

    #Excluding areas of strong telluric absorbtion from fitting
    if obs_spectrum_header['HIERARCH ESO SEQ ARM'] == "UVB":
        mask = (obs_lambda < np.log(5500)) & (obs_lambda > np.log(3100))
        goodPixels = np.where(mask == True)[0]
    elif obs_spectrum_header['HIERARCH ESO SEQ ARM'] == "VIS":
        # mask = (obs_lambda > np.log(5500)) & (obs_lambda < np.log(6350)) | (obs_lambda > np.log(6380)) & (obs_lambda < np.log(6860)) | (obs_lambda > np.log(7045)) & (obs_lambda < np.log(7140)) | (obs_lambda > np.log(7355)) & (obs_lambda < np.log(7570)) | (obs_lambda > np.log(7710)) & (obs_lambda < np.log(8090)) | (obs_lambda > np.log(8400)) & (obs_lambda < np.log(8900)) | (obs_lambda > np.log(9900)) & (obs_lambda < np.log(10100))
        mask = (obs_lambda > np.log(5550)) & (obs_lambda < np.log(5950)) | (
            obs_lambda > np.log(6400)
        ) & (obs_lambda < np.log(6800)) | (obs_lambda > np.log(7355)) & (
            obs_lambda < np.log(7570)) | (obs_lambda > np.log(8400)) & (
                obs_lambda < np.log(8900)) | (obs_lambda > np.log(9900)) & (
                    obs_lambda < np.log(10100))
        goodPixels = np.where(mask == True)[0]
    elif obs_spectrum_header['HIERARCH ESO SEQ ARM'] == "NIR":
        mask = (obs_lambda > np.log(10000)) & (obs_lambda < np.log(10950)) | (
            obs_lambda > np.log(12240)
        ) & (obs_lambda < np.log(12500)) | (obs_lambda > np.log(12800)) & (
            obs_lambda < np.log(12950)) | (obs_lambda > np.log(15300)) & (
                obs_lambda < np.log(17100)) | (obs_lambda > np.log(21500)) & (
                    obs_lambda < np.log(22000)) & (tell_obs > 0)
        goodPixels = np.where(mask == True)[0]

    # Initial parameters for LOSVD
    c = 299792.458
    dv = 15  #(logLam2[0]-logLam1[0])*c # km/s
    vel = -100  # Initial estimate of the LOSVD km/s
    start = [vel, dv]  # (km/s), starting guess for [V,sigma]

    # Here the actual fit starts.
    print("Fitting ...")
    pp = ppxf.ppxf(templates,
                   tell_obs,
                   tell_obs_err,
                   velscale,
                   start,
                   goodpixels=goodPixels,
                   plot=False,
                   moments=4,
                   degree=3,
                   mdegree=1)
    # pl.show()
    print "Formal errors:"
    print "     dV    dsigma   dh3      dh4"
    print "".join("%8.2g" % f for f in pp.error * np.sqrt(pp.chi2))

    print 'elapsed time (s) for ppxf: ', clock() - t2

    print 'Best-fitting R:', c / pp.sol[1]
    print 'Best-fitting error on R:', (
        c / pp.sol[1] - c / (pp.sol[1] + pp.error[1] * np.sqrt(pp.chi2)))

    print(np.array(spectral_library)[np.ceil(pp.weights).astype("bool")])
    # print(spectral_library[(np.array(pp.weights).astype("int")).astype("bool")])

    print 'Rebinning to linear axes'
    f = interp1d(np.e**obs_lambda,
                 pp.galaxy,
                 bounds_error=False,
                 fill_value=np.nan)
    obj_spec = f(wl_obs)
    f = interp1d(np.e**obs_lambda,
                 pp.bestfit,
                 bounds_error=False,
                 fill_value=np.nan)
    template_fit = f(wl_obs)

    return obj_spec, template_fit, obs_spectrum_header