def fit_spectra(bin_sci_j, ppxf_bestfit_j, plot=False):
    """
    """

    with fits.open(bin_sci_j) as hdu:
        odata = hdu[0].data
        ohdr = hdu[0].header

    bestfit = fits.getdata(ppxf_bestfit_j)

    lamRange = ohdr['CRVAL1'] + np.array([1. - ohdr['CRPIX1'], ohdr['NAXIS1'] - ohdr['CRPIX1']]) * ohdr['CD1_1']
    x = np.linspace(lamRange[0], lamRange[1], ohdr['NAXIS1'])

    # Log bin the spectra to match the best fit absorption template
    galaxy, logLam1, velscale = util.log_rebin(lamRange, odata)
    log_bins = np.exp(logLam1)
    emlns, lnames, lwave = util.emission_lines(logLam1, lamRange, 2.3)

    # Hgamma 4340.47, Hbeta 4861.33, OIII [4958.92, 5006.84]
    # lwave = [4340.47, 4861.33, 4958.92, 5006.84]

    # find the index of the emission lines
    iHg = (np.abs(log_bins - lwave[0])).argmin()
    iHb = (np.abs(log_bins - lwave[1])).argmin()
    iOIII = (np.abs(log_bins - lwave[2])).argmin()
    iOIIIb = (np.abs(log_bins - (lwave[2] - 47.92))).argmin()

    # There are BOTH absorption and emission features about the wavelength of Hgamma and Hbeta, so we need
    # to use a specialized fitting function (convolved Guassian and Lorentzian -> pVoight) to remove the
    # emission lines
    popt_Hg, pcov_Hg = fit_ems_pvoightcont(log_bins, galaxy, x, odata, iHg, bestfit)
    popt_Hb, pcov_Hb = fit_ems_pvoightcont(log_bins, galaxy, x, odata, iHb, bestfit)

    # There are only emission features about the OIII doublet so we only fit the emission line with a Gaussian
    popt_OIII, pcov_OIII = fit_ems_lincont(x, odata, iOIII, bestfit, x[954], [x[952], x[956]])
    popt_OIIIb, pcov_OIIIb = fit_ems_lincont(x, odata, iOIIIb, bestfit)

    em_fit = gauss_lorentz(x, popt_Hg[0], popt_Hg[1], popt_Hg[2], popt_Hg[3], popt_Hg[4], popt_Hg[5]) + \
             gauss_lorentz(x, popt_Hb[0], popt_Hb[1], popt_Hb[2], popt_Hb[3], popt_Hb[4], popt_Hb[5]) + \
             gauss_lorentz(x, popt_OIII[0], popt_OIII[1], popt_OIII[2], popt_OIII[3], popt_OIII[4], popt_OIII[5]) + \
             gauss_lorentz(x, popt_OIIIb[0], popt_OIIIb[1], popt_OIIIb[2], popt_OIIIb[3], popt_OIIIb[4], popt_OIIIb[5])

    abs_fit = odata - em_fit

    if plot:
        plt.plot(x, odata, '-k', label="spectra")
        # plt.plot(x, bestfit, '--r', label="bestfit absorption line")
        plt.plot(x, abs_fit, '-b', label="absorption spectra - gauss")
        plt.legend()
        plt.show()

    return abs_fit, em_fit
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()
def remove_emission_lines(bin_sci, ppxf_bestfit, abs_bin_sci, plot=True, bad_lines=[]):
    """
    Fit the absorption spectra with a pVoight function by parameterizing the bestfit template as output from pPXF,
    fit the emission lines over the absorption features with a sum of a gaussian and lorentz and subtract from the
    original spectra. Where the emission lines are isolated, just interpolate the continuum values from the best fit
    template and replace.
    This requires a very good fit between the galaxy spectra and the bestfit spectra. I recommend running this with
    plot=True to check for any residuals.
    """

    for j in range(len(glob.glob(bin_sci.format('*')))):

        bin_sci_j = bin_sci.format(j)
        ppxf_bestfit_j = ppxf_bestfit.format(j)

        if not os.path.exists(abs_bin_sci.format(j)):

            if plot:
                print('>>>>> Removing emission lines from spectra {}'.format(j))

            with fits.open(bin_sci_j) as hdu:
                odata = hdu[0].data
                ohdr = hdu[0].header

            bestfit = fits.getdata(ppxf_bestfit_j)

            lamRange = ohdr['CRVAL1'] + np.array([1. - ohdr['CRPIX1'], ohdr['NAXIS1'] - ohdr['CRPIX1']]) * ohdr['CD1_1']
            lin_bins = np.linspace(lamRange[0], lamRange[1], ohdr['NAXIS1'])

            # Log bin the spectra to match the best fit absorption template
            galaxy, logLam1, velscale = util.log_rebin(lamRange, odata)
            log_bins = np.exp(logLam1)
            emlns, lnames, lwave = util.emission_lines(logLam1, lamRange, 2.3)

            # Hgamma 4340.47, Hbeta 4861.33, OIII [4958.92, 5006.84]
            # lwave = [4340.47, 4861.33, 4958.92, 5006.84]

            # find the index of the emission lines
            iHg = (np.abs(log_bins - lwave[0])).argmin()
            iHb = (np.abs(log_bins - lwave[1])).argmin()
            iOIII = (np.abs(log_bins - lwave[2])).argmin()
            iOIIIb = (np.abs(log_bins - (lwave[2] - 47.92))).argmin()

            # There are BOTH absorption and emission features about the wavelength of Hgamma and Hbeta, so we need
            # to use a specialized fitting function (convolved Guassian and Lorentzian -> pVoight) to remove the
            # emission lines
            popt_Hg, pcov_Hg = remove_lines.fit_ems_pvoightcont(log_bins, galaxy, lin_bins, odata, iHg, bestfit)
            popt_Hb, pcov_Hb = remove_lines.fit_ems_pvoightcont(log_bins, galaxy, lin_bins, odata, iHb, bestfit)

            em_fit = remove_lines.gauss_lorentz(lin_bins, popt_Hg[0], popt_Hg[1], popt_Hg[2], popt_Hg[3], popt_Hg[4],
                                                popt_Hg[5]) + \
                     remove_lines.gauss_lorentz(lin_bins, popt_Hb[0], popt_Hb[1], popt_Hb[2], popt_Hb[3], popt_Hb[4],
                                                popt_Hb[5])

            abs_feat_fit = odata - em_fit

            abs_fit = remove_lines.em_chop(lin_bins, abs_feat_fit, iOIII, log_bins, bestfit)
            abs_fit = remove_lines.em_chop(lin_bins, abs_fit, iOIIIb, log_bins, bestfit)

            for lin in bad_lines:
                ibad = (np.abs(lin_bins - lin)).argmin()
                abs_fit = remove_lines.em_chop(lin_bins, abs_fit, ibad, log_bins, bestfit)

            if plot:
                plt.plot(lin_bins, odata, '-k', label="spectra")
                # plt.plot(lin_bins, bestfit, '--r', label="bestfit absorption line")
                plt.plot(lin_bins, abs_fit, '-b', label="absorption spectra")
                plt.legend()
                plt.show()

            abs_hdu = fits.PrimaryHDU()
            abs_hdu.data = abs_fit
            abs_hdu.header = fits.getheader(bin_sci.format(j), 0)
            abs_hdu.writeto(abs_bin_sci.format(j))
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()
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)
Example #6
0
	def __init__(self, gas, lamRange, logLam_template, FWHM_gal, quiet=True,
		lines='all', narrow_broad=False, goodWav=None):#, NaD=False):
		if narrow_broad:
			raise ValueError('Two components (narrow and broad line componets' +
				' are not currently set up on the version (See git' +
				' errors2_muse.py)')

		if lines == 'all' or lines =='All' or lines =='ALL':
			lines = ['Hdelta', 'Hgamma', 'Hbeta', 'Halpha', '[OII]3726', 
				'[OII]3729', '[SII]6716', '[SII]6731', '[OIII]5007d', '[NI]d', 
				'[OI]6300d', '[NII]6583d']#, 'NaD']
		self.element = []
		self.component = []
		self.templatesToUse = []
		self.templates = []

		# This is not particularly robust code
		if goodWav is not None:
			w = np.where(np.diff(goodWav) > goodWav[-1]-goodWav[-2])[0]
			mask = np.array(zip(goodWav[w], goodWav[w+1]))
		else: 
			mask = np.array([[lamRange[0],lamRange[0]]])
		if len(mask) == 0:
			mask = np.array([[lamRange[0],lamRange[0]]])
		## ----------============ All lines together ===============---------
		if gas == 1:
			emission_lines, line_name, line_wav = util.emission_lines(
				logLam_template, lamRange, FWHM_gal, quiet=quiet)

			nTemp = 0
			for i in range(len(line_name)):
				if np.sum(mask[:,0] - line_wav[i] > 0) == np.sum(
					mask[:,1] - line_wav[i] > 0) and line_name[i] in lines:
					self.templatesToUse = np.append(self.templatesToUse, 
						line_name[i])
					self.templates.append(emission_lines[:,i])
					nTemp += 1

			# if NaD and np.sum(mask[:,0] - 588.995 > 0) == np.sum(
			# 	mask[:,1] -589.5924 > 0) and 'NaD' in lines:
			# if NaD and 'NaD' in lines:
			# 	print 'here'
			# 	self.templatesToUse = np.append(self.templatesToUse, 'NaD')
			# 	# taken from ppxf_util.py
			# 	lam = np.exp(logLam_template)
			# 	doublet = np.exp(-0.5*((lam - 588.995)/(FWHM_gal/2.355))**2) + \
			# 		0.5*np.exp(-0.5*((lam - 589.5924)/(FWHM_gal/2.355))**2)
			# 	self.templates.append(-doublet) # -ve to give absorption
			# 	nTemp += 1

			self.component = self.component + [1]*nTemp
			self.element.append('gas')
		## ----------=============== SF and shocks lines ==============---------
		if gas == 2:
			emission_lines, line_name, line_wav = util.emission_lines(
				logLam_template, lamRange, FWHM_gal, quiet=quiet)

			for i in range(len(line_name)):
				if np.sum(mask[:,0] - line_wav[i] > 0) == np.sum(
					mask[:,1] - line_wav[i] > 0) and line_name[i] in lines:
					if 'H' in line_name[i]:
						self.templatesToUse = np.append(self.templatesToUse, 
							line_name[i])
						self.templates.append(emission_lines[:,i])
						self.component = self.component + [1]
						self.element.append['SF']
					else:
						self.templatesToUse = np.append(self.templatesToUse, 
							line_name[i])
						self.templates.append(emission_lines[:,i])
						self.component = self.component + [2] 
						self.element.append['Shocks']
		## ----------=========== All lines inderpendantly ==============---------
		if gas == 3:
			emission_lines, line_name, line_wav = util.emission_lines(
				logLam_template, lamRange, FWHM_gal, quiet=quiet)

			aph_lin = np.sort(line_name)

			for i in range(len(line_name)):
				if np.sum(mask[:,0] - line_wav[i] > 0) == np.sum(
					mask[:,1] - line_wav[i] > 0) and line_name[i] in lines:
					self.templatesToUse = np.append(self.templatesToUse,
						line_name[i])

					# line listed alphabetically
					self.component = self.component + \
						[np.where(line_name[i] == aph_lin)[0][0]+1]
					self.templates.append(emission_lines[:,i])
					self.element.append(aph_lin[i])

		self.templates = np.array(self.templates).T
		if gas:	self.ntemp = self.templates.shape[1]
		else: self.ntemp = 0
Example #7
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()
Example #8
0
def ppxf_kinematics_gas_parallel(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)])

    pp = Parallel(n_jobs=len(bin_sci_list))(
        delayed(ppxf_gas_kinematics_parallel_loop)(bin_sci.format(j), ppxf_bestfit.format(j), gal_hdr, templates,
                                                   velscale, sigma, lamRange1, logLam2, start, plot, moments, regul_err,
                                                   reg_dim, component, bias, quiet) for j in range(len(bin_sci_list)))

    with open(ppxf_file, 'a') as outfile:
        outfile.write(pp)
    '''
Example #9
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')
Example #10
0
def ppxf_population_gas_sdss(file, z, name):

    # 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.

    hdulist = pyfits.open(file)
    VAC = (10**hdulist[1].data.loglam)
    wave = []
    for i in range(0, len(VAC)):
        wave.append(VAC[i] / (1.0 + 2.735182E-4 + 131.4182 / VAC[i]**2 + 2.76249E8 / VAC[i]**4) / (1+z))
    flux = hdulist[1].data.flux*10**-17
    err = hdulist[1].data.ivar*10**-17
    #bunit = hdulist[0].header['bunit']
    #c0 = hdulist[0].header['coeff0']
    #c1 = hdulist[0].header['coeff1']
    #units = 'erg/s/cm^2/Ang'

    xarr = pyspeckit.units.SpectroscopicAxis(wave, units='angstroms')
    spec = pyspeckit.OpticalSpectrum(header=hdulist[0].header, xarr=xarr, data=flux*1e17, error=err)
    #spec.units = 'erg s^{-1} cm^{-2} \\AA^{-1}'
    #spec.xarr.units='angstroms'

    #Galactic extinction correction
    #Take the ebv of the galaxy from IrsaDust
    table = IrsaDust.get_query_table(name, section='ebv')
    ebv = table['ext SFD mean'][0]

    spec.deredden(ebv=ebv)  # deredden in place
    t = hdulist[1].data
    #z = float(hdu[1].header["Z"]) # SDSS redshift estimate

    # Create the mask
    # Only use the wavelength range in common between galaxy and stellar library.
    mask = [True]*(len(wave))
    for i in range(0, len(wave)):
        #mask[i]=(wave[i] > 3540) & (wave[i] < 7409)
        mask[i] = (wave[i] > 3750) & (wave[i] < 7400)  # take a smaller the wavelength range

    #mask for the galaxy
    galaxy = t.field('flux')/np.median(t.field('flux'))  # Normalize spectrum to avoid numerical issues

    galaxymask = []
    for i in range(0, len(mask)):
        if mask[i]:
            galaxymask.append(galaxy[i])

    galaxy = np.array(galaxymask)

    #mask for the wavelength
    #create an array with only the allowed values of the wavelenght
    wavemask = []
    for i in range(0, len(mask)):
        if mask[i]:
            wavemask.append(wave[i])

    wave = np.array(wavemask)

    #create a mask for the emission lines
    NeIIIa = 3869.9
    NeIIIb = 3971.1
    Heps = 3890.2
    Hdelta = 4102.9
    Hgamma = 4341.7
    OIIIc = 4364.4
    HeIIa = 4687.0
    HeIIb = 5413.0
    SIII = 6313.8
    OIa = 5578.9
    OIb = 6365.5

    Hbeta = 4861.33
    OIIIa = 4958.92
    OIIIb = 5006.84
    OI = 6300.30
    NIIa = 6549.86
    NIIb = 6585.27
    Halpha = 6564.614
    SIIa = 6718.2
    SIIb = 6732.68
    ArIII = 7137.8

    delta = 10
    delta2 = 20
    maskHa = [True]*(len(wave))
    for i in range(0, len(wave)):
        maskHa[i] = (((wave[i] < (Halpha - delta2)) or (wave[i] > (Halpha + delta2))) &
                    ((wave[i] < (Hbeta - delta2)) or (wave[i] > (Hbeta + delta2))) &
                    ((wave[i] < (OIIIa - delta)) or (wave[i] > (OIIIa + delta))) &
                    ((wave[i] < (OIIIb - delta)) or (wave[i] > (OIIIb + delta))) &
                    ((wave[i] < (OI - delta)) or (wave[i] > (OI + delta))) &
                    ((wave[i] < (NIIa - delta)) or (wave[i] > (NIIa + delta))) &
                    ((wave[i] < (NIIb - delta)) or (wave[i] > (NIIb + delta))) &
                    ((wave[i] < (SIIa - delta)) or (wave[i] > (SIIa + delta))) &
                    ((wave[i] < (SIIb - delta)) or (wave[i] > (SIIb + delta))) &
                    ((wave[i] < (NeIIIa - delta)) or (wave[i] > (NeIIIa + delta))) &
                    ((wave[i] < (NeIIIb - delta)) or (wave[i] > (NeIIIb + delta))) &
                    ((wave[i] < (Heps - delta)) or (wave[i] > (Heps + delta))) &
                    ((wave[i] < (Hdelta - delta)) or (wave[i] > (Hdelta + delta))) &
                    ((wave[i] < (Hgamma - delta)) or (wave[i] > (Hgamma + delta))) &
                    ((wave[i] < (OIIIc - delta)) or (wave[i] > (OIIIc + delta))) &
                    ((wave[i] < (HeIIa - delta)) or (wave[i] > (HeIIa + delta))) &
                    ((wave[i] < (HeIIb - delta)) or (wave[i] > (HeIIb + delta))) &
                    ((wave[i] < (SIII - delta)) or (wave[i] > (SIII + delta))) &
                    ((wave[i] < (OIa - delta)) or (wave[i] > (OIa + delta))) &
                    ((wave[i] < (OIb - delta)) or (wave[i] > (OIb + delta))) &
                    ((wave[i] < (ArIII - delta)) or (wave[i] > (ArIII + delta))))

    # mask for the wavelength for the emission lines
    # create an array with only the allowed values of the wavelenght
    wavemask = []
    for i in range(0, len(maskHa)):
        if maskHa[i]:
            wavemask.append(wave[i])

    wave = np.array(wavemask)

    #Use this mask for the galaxy
    galaxymask = []
    for i in range(0, len(maskHa)):
        if maskHa[i]:
            galaxymask.append(galaxy[i])
        
    galaxy = np.array(galaxymask)
    
    # 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.

    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
    #
    gas_templates = util.emission_lines(logLam_temp, 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.
    #
    z = 0  # redshift already corrected
    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()

    plt.clf()
    plt.subplot(211)

    # Assign component=0 to the stellar templates and
    # component=1 to the gas emission lines templates.
    # One can easily assign different 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.
    #
    component = [0]*stars_templates.shape[1] + [1]*gas_templates.shape[1]
    moments = [4, 4]  # fit (V,sig,h3,h4) for both the stars and the gas
    start = [start, start]  # adopt the same starting value for both gas and stars

    pp = ppxf(file, templates, wave, 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)

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

    plt.subplot(212)
    #plt.set_cmap('gist_heat') # = IDL's loadct, 3
    plt.imshow(np.rot90(pp.weights[:np.prod(reg_dim)].reshape(reg_dim)/pp.weights.sum()),
               interpolation='nearest', aspect='auto', extent=(np.log(1.0),
               np.log(17.7828), -1.9, 0.45))
    plt.set_cmap('gist_heat')  # = IDL's loadct, 3
    plt.colorbar()
    plt.title("Mass Fraction")
    plt.xlabel("log Age (Gyr)")
    plt.ylabel("[M/H]")
    plt.tight_layout()

    # Save the figure
    name = splitext(basename(file))[0]
    plt.savefig(name)

    return
def ppxf_population_gas(bin_sci, FWHM_gal, template_fits, FWHM_tem, ppxf_file, ppxf_bestfit, vel_init=1500, bias=0.6, plot=False):
    """

    """

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



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


    # 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'
    # with fits.open(file) as hdu:
    #     t = hdu[1].data
    #     z = float(hdu[1].header["Z"])  # SDSS redshift estimate
    #
    with fits.open(bin_sci.format(0)) as hdu:
        gal_lin = hdu[0].data
        hdr = hdu[0].header

    # 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')
    #
    lam_range = hdr['CRVAL1'] + np.array([1. - hdr['CRPIX1'], hdr['NAXIS1'] - hdr['CRPIX1']]) * hdr['CD1_1']
    # log rebin the bins, not done here because SDSS alrady log binned, which is what this example uses
    galaxy, wave, velscale = util.log_rebin(lam_range, gal_lin)
    galaxy = galaxy/np.median(galaxy)
    # our data set does not exceed the range of the template so dont 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
    z = np.exp(vel_init / c) - 1
    # FWHM_gal = 2.76  # SDSS has an instrumental resolution FWHM of 2.76A.

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

    stars_templates, lamRange_temp, logLam_temp, logAge_grid, metal_grid = setup_spectral_library(velscale, FWHM_gal, template_fits, FWHM_tem)

    # 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)
    gas_templates, line_names, line_wave = util.emission_lines(logLam_temp, lam_range, 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
    dv = (np.log(lamRange_temp[0]) - wave[0]) * c  # km/s
    # vel = c * z  # Initial estimate of the galaxy velocity in km/s
    vel = vel_init

    # 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_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)
        galaxy, logLam1, velscale = util.log_rebin(lam_range, gal_data, velscale=velscale)

        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)

        # 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, 5])
            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

            weight_arr = pp.weights[:stars_templates.shape[1]]

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

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

        np.savetxt(ppxf_bestfit.format(j), pp.bestfit)

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