Esempio n. 1
0
    def get_emission_lines(self, vac_or_air='air'):

        if vac_or_air == 'vac':
            vacuum = True
        else:
            vacuum = False

        self.emission_lines, self.line_names, self.line_wave = P.emission_lines(
            self.log_lam_template,
            self.lam_range_gal,
            self.FWHM_gal,
            vacuum=vacuum)
Esempio n. 2
0
def make_paintbox_model(wave,
                        store,
                        name="test",
                        porder=45,
                        nssps=1,
                        sigma=100):
    """ Prepare a model with paintbox. """
    ssp = pb.CvD18(sigma=sigma, store=store, libpath=context.cvd_dir)
    twave = ssp.wave
    limits = ssp.limits
    if nssps > 1:
        for i in range(nssps):
            p0 = pb.Polynomial(twave, 0, pname="w")
            p0.parnames = [f"w_{i+1}"]
            s = copy.deepcopy(ssp)
            s.parnames = ["{}_{}".format(_, i + 1) for _ in s.parnames]
            if i == 0:
                pop = p0 * s
            else:
                pop += (p0 * s)
    else:
        pop = ssp
    vname = "vsyst_{}".format(name)
    stars = pb.Resample(wave, pb.LOSVDConv(pop, losvdpars=[vname, "sigma"]))
    # Adding a polynomial
    poly = pb.Polynomial(wave, porder, zeroth=True, pname=f"p{name}")
    # Including emission lines
    target_fwhm = lambda w: sigma / const.c.to("km/s").value * w * 2.355
    gas_templates, gas_names, line_wave = ppxf_util.emission_lines(
        np.log(twave), [wave[0], wave[-1]],
        target_fwhm,
        tie_balmer=True,
        vacuum=True)
    gas_templates /= np.max(gas_templates, axis=0)  # Normalize line amplitudes
    gas_names = [_.replace("_", "") for _ in gas_names]
    # for em in gas_templates.T:
    #     plt.plot(twave, em)
    # plt.show()
    if len(gas_names) > 0:
        emission = pb.NonParametricModel(twave,
                                         gas_templates.T,
                                         names=gas_names)
        emkin = pb.Resample(
            wave, pb.LOSVDConv(emission, losvdpars=[vname, "sigma_gas"]))
        sed = (stars + emkin) * poly
    else:
        sed = stars * poly
    return sed, limits, gas_names
Esempio n. 3
0
def ppxf_example_population_gas_sdss(tie_balmer, limit_doublets):

    ppxf_dir = path.dirname(path.realpath(ppxf_package.__file__))

    cube_id = 468

    # reading cube_data
    cube_file = "data/cubes/cube_" + str(cube_id) + ".fits"
    hdu = fits.open(cube_file)
    t = hdu[0].data

    # using our redshift estimate from lmfit
    cube_result_file = ("results/cube_" + str(cube_id) + "/cube_" +
                        str(cube_id) + "_lmfit.txt")
    cube_result_file = open(cube_result_file)

    line_count = 0
    for crf_line in cube_result_file:
        if (line_count == 20):
            curr_line = crf_line.split()
            z = float(curr_line[1])
        line_count += 1

    cube_x_data = np.load("results/cube_" + str(int(cube_id)) + "/cube_" +
                          str(int(cube_id)) + "_cbd_x.npy")
    cube_y_data = np.load("results/cube_" + str(int(cube_id)) + "/cube_" +
                          str(int(cube_id)) + "_cbs_y.npy")

    # Only use the wavelength range in common between galaxy and stellar library.
    #
    mask = (cube_x_data > 3540) & (cube_x_data < 7409)
    flux = cube_y_data[mask]
    galaxy = flux / np.median(
        flux)  # Normalize spectrum to avoid numerical issues
    wave = cube_x_data[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 = ppxf_dir + '/miles_models/Mun1.30*.fits'
    miles = lib.miles(pathname, velscale, FWHM_gal)

    # The stellar templates are reshaped below 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,
        tie_balmer=tie_balmer,
        limit_doublets=limit_doublets)

    # 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 and Sec.2.4 of Cappellari (2017)
    #
    c = 299792.458
    dv = c * (miles.log_lam_temp[0] - np.log(wave[0])
              )  # eq.(8) of Cappellari (2017)
    vel = c * np.log(1 + z)  # eq.(8) of Cappellari (2017)
    start = [vel, 180.]  # (km/s), starting guess for [V, sigma]

    n_temps = stars_templates.shape[1]
    n_forbidden = np.sum(["[" in a
                          for a in gas_names])  # forbidden lines contain "[*]"
    n_balmer = len(gas_names) - n_forbidden

    # 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 kinematic components
    moments = [4, 2, 2]

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

    # If the Balmer lines are tied one should allow for gas reddeining.
    # The gas_reddening can be different from the stellar one, if both are fitted.
    gas_reddening = 0 if tie_balmer else None

    # Here the actual fit starts.
    #
    # 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.
    #
    t = clock()
    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,
              gas_reddening=gas_reddening)

    # 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.
    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)
    plt.show()
Esempio n. 4
0
def emission(dirfile, w1, f1, redshift, plateifu, tie_balmer, limit_doublets):
    ppxf_dir = path.dirname(path.realpath(ppxf_package.__file__))

    z = redshift
    flux = f1
    galaxy = flux
    wave = w1

    wave *= np.median(util.vac_to_air(wave) / wave)

    noise = np.full_like(galaxy,
                         0.01635)  # Assume constant noise per pixel here

    c = 299792.458  # speed of light in km/s
    velscale = c * np.log(wave[1] / wave[0])  # eq.(8) of Cappellari (2017)
    # SDSS has an approximate instrumental resolution FWHM of 2.76A.
    FWHM_gal = 2.76

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

    pathname = ppxf_dir + '/miles_models/Mun1.30*.fits'

    # The templates are normalized to mean=1 within the FWHM of the V-band.
    # In this way the weights and mean values are light-weighted quantities
    miles = lib.miles(pathname, velscale, FWHM_gal)

    reg_dim = miles.templates.shape[1:]

    regul_err = 0.013  # Desired regularization error

    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,
        tie_balmer=tie_balmer,
        limit_doublets=limit_doublets)

    templates = gas_templates

    c = 299792.458
    dv = c * (miles.log_lam_temp[0] - np.log(wave[0])
              )  # eq.(8) of Cappellari (2017)
    vel = c * np.log(1 + z)  # eq.(8) of Cappellari (2017)
    start = [vel, 180.]  # (km/s), starting guess for [V, sigma]

    #     n_temps = stars_templates.shape[1]
    n_forbidden = np.sum(["[" in a
                          for a in gas_names])  # forbidden lines contain "[*]"
    n_balmer = len(gas_names) - n_forbidden

    component = [0] * n_balmer + [1] * n_forbidden
    gas_component = np.array(
        component) >= 0  # gas_component=True for gas templates

    moments = [2, 2]

    start = [start, start]

    gas_reddening = 0 if tie_balmer else None

    t = clock()
    pp = gas.ppxf(dirfile,
                  templates,
                  galaxy,
                  noise,
                  velscale,
                  start,
                  z,
                  plot=True,
                  moments=moments,
                  degree=-1,
                  mdegree=10,
                  vsyst=dv,
                  lam=wave,
                  clean=False,
                  component=component,
                  gas_component=gas_component,
                  gas_names=gas_names,
                  gas_reddening=gas_reddening)

    pp.plot()
    return pp.bestfit, pp.lam
Esempio n. 5
0
File: ppxf.py Progetto: FRBs/FRB
def dump_ppxf_results(ppfit, miles, z, outfile):
    """
    Write the stnadard results and the
    gas_component measurements to a simple ASCII file

    Parameters
    ----------
    ppfit: ppxf
    outfile: str

    Returns
    -------

    """
    # Get the lines (air)
    emission_lines, line_names, line_wave = util.emission_lines(
        np.array([0.1, 0.2]), [1000., 1e5],
        0,
        limit_doublets=False,
        vacuum=True)
    # Construct a simple Table
    gas_tbl = Table()

    # Standard pPXF fit results
    meta = {}
    meta['EBV'] = ppfit.reddening

    star_weights = ppfit.weights[~ppfit.gas_component]
    star_weights = star_weights.reshape(ppfit.reg_dim)
    age, metals = miles.mean_age_metal(star_weights)
    meta['AGE'] = age
    meta['METALS'] = metals

    # Mass -- Approximate
    # Mass -- This is a bit approximate as Dwv is a guess for now
    actualflux = ppfit.bestfit * constants.L_sun.cgs / units.angstrom / (
        4 * np.pi * (cosmo.luminosity_distance(z).to(units.cm))**2 / (1 + z))
    # When fitting, the routine thought our data and model spectra had same units...
    Dwv = 1700.  # Ang, width of the band pass
    scfactor = np.median(ppfit.bestfit *
                         (units.erg / units.s / units.cm**2 / units.angstrom) /
                         actualflux) * Dwv
    # To get the actual model mass required to fit spectrum, scale by this ratio
    massmodels = scfactor * miles.total_mass(star_weights)
    meta['LOGMSTAR'] = np.log10(massmodels.value)

    gas_tbl.meta = meta

    gas = ppfit.gas_component
    comp = ppfit.component[gas]
    gas_tbl['comp'] = comp
    gas_tbl['name'] = ppfit.gas_names
    gas_tbl['flux'] = ppfit.gas_flux
    gas_tbl['err'] = ppfit.gas_flux_error

    # Wavelengths
    waves = []
    for name in ppfit.gas_names:
        idx = np.where(line_names == name)[0][0]
        waves.append(line_wave[idx])
    gas_tbl['wave'] = waves

    vs = [ppfit.sol[icomp][0] for icomp in comp]
    sigs = [ppfit.sol[icomp][1] for icomp in comp]
    gas_tbl['v'] = vs
    gas_tbl['sig'] = sigs
    # Write
    gas_tbl.write(outfile, format='ascii.ecsv', overwrite=True)
    print("Wrote: {:s}".format(outfile))
Esempio n. 6
0
def ppxf_single(object_id,
                z_init,
                lambda_spec,
                galaxy_lin,
                error_lin,
                cars_model,
                emsub_specfile=None,
                plotfile=None,
                outfile=None,
                outfile_spec=None,
                mpoly=None,
                apoly=None,
                reflines=None):

    # Speed of light
    c = 299792.458

    #h_spec = spec_hdu[0].header
    #Ang_air = h_spec['CRVAL3'] + np.arange(0,h_spec['CDELT3']*(h_spec['NAXIS3']),h_spec['CDELT3'])

    #s = 10**4/Ang_air
    #n = 1 + 0.00008336624212083 + 0.02408926869968 / (130.1065924522 - s**2) + 0.000159740894897 / (38.92568793293 - s**2)
    #lambda_spec = Ang_air*n

    #wave = h_spec['CRVAL3'] + np.arange(0,h_spec['CDELT3']*(h_spec['NAXIS3']),h_spec['CDELT3'])

    #lambda_spec = vactoair(wave)
    #lambda_spec = h_spec['CRVAL3'] + np.arange(0,h_spec['CDELT3']*(h_spec['NAXIS3']),h_spec['CDELT3'])
    # Crop to finite
    use = (np.isfinite(galaxy_lin) & (galaxy_lin > 0.0))

    # Making a "use" vector
    use_indices = np.arange(galaxy_lin.shape[0])[use]
    galaxy_lin = (galaxy_lin[use_indices.min():(use_indices.max() + 1)])[2:-3]
    error_lin = (error_lin[use_indices.min():(use_indices.max() + 1)])[2:-3]
    lambda_spec = (lambda_spec[use_indices.min():(use_indices.max() +
                                                  1)])[2:-3]

    lamRange_gal = np.array([np.min(lambda_spec), np.max(lambda_spec)])

    # New resolution estimate = 0.9 \AA (sigma), converting from sigma to FWHM
    FWHM_gal = 2.355 * (np.max(lambda_spec) -
                        np.min(lambda_spec)) / len(lambda_spec)
    print('FWHM', FWHM_gal)
    lamRange_gal = lamRange_gal / (
        1 + float(z_init))  # Compute approximate restframe wavelength range
    FWHM_gal = FWHM_gal / (1 + float(z_init))  # Adjust resolution in Angstrom

    sigma_gal = FWHM_gal / (2.3548 * 4500.0) * c  # at ~4500 \AA

    # log rebinning for the fits
    galaxy, logLam_gal, velscale = util.log_rebin(np.around(lamRange_gal,
                                                            decimals=3),
                                                  galaxy_lin,
                                                  flux=True)

    noise, logLam_noise, velscale = util.log_rebin(np.around(lamRange_gal,decimals=3), error_lin, \
                                              velscale=velscale, flux=True)

    # correcting for infinite or zero noise
    noise[np.logical_or((noise == 0.0), np.isnan(noise))] = 1.0
    galaxy[np.logical_or((galaxy < 0.0), np.isnan(galaxy))] = 0.0

    if galaxy.shape != noise.shape:
        galaxy = galaxy[:-1]
        logLam_gal = logLam_gal[:-1]

    # Define lamRange_temp and logLam_temp

    #lamRange_temp, logLam_temp = setup_spectral_library_conroy(velscale[0], FWHM_gal)

    # Construct a set of Gaussian emission line templates.
    # Estimate the wavelength fitted range in the rest frame.
    #

    gas_templates, line_names, line_wave = util.emission_lines(
        logLam_gal, lamRange_gal, FWHM_gal)

    goodpixels = np.arange(galaxy.shape[0])
    wave = np.exp(logLam_gal)

    # crop very red end (only affects a very small subsample)
    # goodpixels = goodpixels[wave <= 5300]

    # exclude lines at the edges (where things can go very very wrong :))
    include_lines = np.where((line_wave > (wave.min() + 10.0))
                             & (line_wave < (wave.max() - 10.0)))
    if line_wave[include_lines[0]].shape[0] < line_wave.shape[0]:
        line_wave = line_wave[include_lines[0]]
        line_names = line_names[include_lines[0]]
        gas_templates = gas_templates[:, include_lines[0]]

    #reg_dim = stars_templates.shape[1:]
    reg_dim = gas_templates.shape[1:]
    templates = gas_templates
    #np.hstack([gas_templates, gas_templates])

    dv = 0  #(logLam_temp[0]-logLam_gal[0])*c # km/s

    vel = 0  #np.log(z_init + 1)*c # Initial estimate of the galaxy velocity in km/s
    #z = np.exp(vel/c) - 1   # Relation between velocity and redshift in pPXF

    # 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.
    #
    t = clock()

    nNLines = gas_templates.shape[1]
    component = [0] * nNLines
    start_gas = [vel, 100.]
    start = start_gas  # adopt the same starting value for both gas (BLs and NLs) and stars
    moments = [
        2
    ]  # fit (V,sig,h3,h4) for the stars and (V,sig) for the gas' broad and narrow components

    fixed = None

    ## Additive polynomial degree

    if apoly is None:
        degree = int(
            np.ceil((lamRange_gal[1] - lamRange_gal[0]) /
                    (100.0 * (1. + float(z_init)))))
    else:
        degree = int(apoly)

    if mpoly is None: mdegree = 3
    else: mdegree = int(mpoly)

    # Trying: sigmas must be kinematically decoupled

    # #Trying: velocities must be kinematically decoupled
    #A_ineq = [[0,0,2,0,-1,0]]
    #b_ineq = [0]

    bounds_gas = [[-1000, 1000], [0, 1000]]
    bounds = bounds_gas

    pp = ppxf.ppxf(templates,
                   galaxy,
                   noise,
                   velscale,
                   start,
                   fixed=fixed,
                   plot=False,
                   moments=moments,
                   mdegree=mdegree,
                   degree=degree,
                   vsyst=dv,
                   reg_dim=reg_dim,
                   goodpixels=goodpixels,
                   bounds=bounds)
    #component=component,

    # redshift_to_newtonian:
    # return (v-astropy.constants.c.to('km/s').value*redshift)/(1.0+redshift)

    # "v" from above is actually the converted velocity which is
    # (np.exp(v_out/c) - 1)*c

    v_gas = pp.sol[0]
    ev_gas = pp.error[0]

    conv_vel_gas = (np.exp(pp.sol[0] / c) - 1) * c

    vel_gas = (1 + z_init) * (conv_vel_gas - c * z_init)

    sigma_gas = pp.sol[1]  #first # is template, second # is the moment
    esigma_gas = pp.error[1]  #*np.sqrt(pp.chi2)

    zfit_gas = (z_init + 1) * (1 + pp.sol[0] / c) - 1

    zfit_stars = z_init

    ezfit_gas = (z_init + 1) * pp.error[0] * np.sqrt(pp.chi2) / c

    if plotfile is not None:
        #
        ### All of the rest of this plots and outputs the results of the fit
        ### Feel free to comment anything out at will
        #

        maskedgalaxy = np.copy(galaxy)
        lowSN = np.where(noise > (0.9 * np.max(noise)))
        maskedgalaxy[lowSN] = np.nan

        wave = np.exp(logLam_gal) * (1. + z_init) / (1. + zfit_stars)

        fig = plt.figure(figsize=(12, 7))
        ax1 = fig.add_subplot(211)

        # plotting smoothed spectrum
        smoothing_fact = 3

        ax1.plot(wave,
                 convolve(maskedgalaxy, Box1DKernel(smoothing_fact)),
                 color='Gray',
                 linewidth=0.5)
        ax1.plot(wave[goodpixels],
                 convolve(maskedgalaxy[goodpixels],
                          Box1DKernel(smoothing_fact)),
                 'k',
                 linewidth=1.)

        label = "Best fit template from high res Conroy SSPs + emission lines at z={0:.3f}".format(
            zfit_stars)

        # overplot stellar templates alone
        ax1.plot(wave, pp.bestfit, 'r', linewidth=1.0, alpha=0.75, label=label)

        ax1.set_ylabel('Flux')
        ax1.legend(loc='upper right', fontsize=10)
        ax1.set_title(object_id)

        xmin, xmax = ax1.get_xlim()

        ax2 = fig.add_subplot(413, sharex=ax1, sharey=ax1)

        # plotting emission lines if included in the fit

        gas = pp.matrix[:, -nNLines:].dot(pp.weights[-nNLines:])

        ax2.plot(wave, gas, 'b', linewidth=2,\
                 label = '$\sigma_{gas}$'+'={0:.0f}$\pm${1:.0f} km/s'.format(sigma_gas, esigma_gas)+', $V_{gas}$'+'={0:.0f}$\pm${1:.0f} km/s'.format(v_gas, ev_gas)) # overplot emission lines alone
        cars_model = (cars_model[use_indices.min():(use_indices.max() +
                                                    1)])[2:-3]
        ax2.plot(np.array(lambda_spec)/(1+z_init), cars_model, color='orange', linewidth=1,\
            label = 'CARS Model')

        #(lambda_spec[use_indices.min():(use_indices.max()+1)])[2:-3]
        stars = pp.bestfit - gas

        #if (ymax > 3.0*np.median(stars)): ymax = 3.0*np.median(stars)
        #if (ymin < -0.5): ymin = -0.5

        ax2.set_ylabel('Best Fits')

        ax2.legend(loc='upper left', fontsize=10)

        # Plotting the residuals
        ax3 = fig.add_subplot(817, sharex=ax1)
        ax3.plot(wave[goodpixels],
                 (convolve(maskedgalaxy, Box1DKernel(smoothing_fact)) -
                  pp.bestfit)[goodpixels],
                 'k',
                 label='Fit Residuals')
        #ax3.set_yticks([-0.5,0,0.5])
        ax3.set_ylabel('Residuals')

        ax4 = fig.add_subplot(818, sharex=ax1)

        ax4.plot(wave, noise, 'k', label='Flux Error')

        ax4.set_ylabel('Noise')
        ax4.set_xlabel('Rest Frame Wavelength [$\AA$]')
        #ax4.set_yticks(np.arange(0,0.5,0.1))
        '''if reflines is not None:
            for i,w,label in zip(range(len(reflines)),reflines['wave'],reflines['label']):
                if ((w > xmin) and (w < xmax)):
#                     ax1.text(w,ymin+(ymax-ymin)*(0.03+0.08*(i % 2)),'$\mathrm{'+label+'}$',fontsize=10,\
#                              horizontalalignment='center',\
#                              bbox=dict(boxstyle='round', facecolor='white', alpha=0.5))
                    print(label.decode("utf-8"))
                    ax1.text(w,ymin+(ymax-ymin)*(0.03+0.08*(i % 2)),'$\mathrm{'+label.decode("utf-8")+'}$',fontsize=10,\
                             horizontalalignment='center',\
                             bbox=dict(boxstyle='round', facecolor='white', alpha=0.5))
                    ax1.plot([w,w],[ymin,ymax],':k',alpha=0.5)'''

        fig.subplots_adjust(hspace=0.05)
        plt.setp([a.get_xticklabels() for a in fig.axes[:-1]], visible=False)

        #print('Saving figure to {0}'.format(plotfile))

        ax1.set_xlim([6450, 6750])

        ax2.set_xlim([6450, 6750])

        #ymin, ymax = ax1.get_ylim([])

        plt.savefig(plotfile, dpi=150)
        plt.close()
    return v_gas, vel_gas, sigma_gas, pp.chi2
    #    print('# id z_stars ez_stars sigma_stars esigma_stars z_gas ez_gas sigma_gas esigma_gas SN_median SN_rf_4000 SN_obs_8030 chi2dof')
    print('{0:d} {1:.6f} {2:.6f} {3:.1f} {4:.1f} {5:.6f} {6:.6f} {7:.1f} {8:.1f} {9:.1f} {10:.1f} {11:.1f} {12:.2f}\n'.format(\
                      object_id,zfit_stars,ezfit_stars, sigma_stars,esigma_stars,\
                      zfit_gas, ezfit_gas, sigma_gas, esigma_gas, SN_median, SN_rf_4000, SN_obs_8030, pp.chi2))

    ## This prints the fit parameters to an open file object called outfile
    if outfile is not None:
        outfile.write('{0:d} {1:d} {2:.6f} {3:.6f} {4:.1f} {5:.1f} {6:.6f} {7:.6f} {8:.1f} {9:.1f} {10:.1f} {11:.1f} {12:.1f} {13:.2f}\n'.format(\
                      object_id, row2D, zfit_stars, ezfit_stars, sigma_stars,esigma_stars,\
                      zfit_gas, ezfit_gas, sigma_gas, esigma_gas, SN_median, SN_rf_4000, SN_obs_8030, pp.chi2))

    ## This outputs the spectrum and best-fit model to an open file object called outfile_spec
    if outfile_spec is not None:
        outfile_spec.write(
            '# l f ef f_stars f_gas f_model_tot used_in_fit add_poly mult_poly\n'
        )
        for i in np.arange(wave.shape[0]):
            isgood = 0
            if goodpixels[goodpixels == i].shape[0] == 1: isgood = 1
            outfile_spec.write(
                '{0:0.4f} {1:0.4f} {2:0.4f} {3:0.4f} {4:0.4f} {5:0.4f} {6} {7:0.4f} {8:0.4f}\n'
                .format(wave[i], galaxy[i], noise[i], stars[i], gas[i],
                        pp.bestfit[i], isgood, add_polynomial[i],
                        mult_polynomial[i]))

#     ## This outputs the best-fit emission-subtracted spectrum to a fits file
#     ## but I've commented it out since you are unlikely to need this!
#     if emsub_specfile is not None:
#         wave = np.exp(logLam_gal)*(1.+z_init)
#         if include_gas:
#             emsub = galaxy - gas
#         else:
#             emsub = galaxy

#         col1 = fits.Column(name='wavelength', format='E', array=wave)
#         col2 = fits.Column(name='flux',format='E',array=galaxy)
#         col3 = fits.Column(name='error',format='E',array=noise)
#         col4 = fits.Column(name='flux_emsub',format='E',array=emsub)

#         cols = fits.ColDefs([col1,col2,col3,col4])

#         tbhdu = fits.BinTableHDU.from_columns(cols)

#         # delete old file if it exists
#         if os.path.isfile(emsub_specfile): os.remove(emsub_specfile)
#         tbhdu.writeto(emsub_specfile)

# return zfit_stars, ezfit_stars, sigma_stars, esigma_stars, zfit_gas, ezfit_gas, \
#     sigma_gas, esigma_gas, SN_median, SN_rf_4000, SN_obs_8030, pp.chi2
    return zfit_stars, sigma_stars, sigma_blr, wave, pp.bestfit