Beispiel #1
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()
Beispiel #2
0
Datei: ppxf.py Projekt: FRBs/FRB
def fit_spectrum(spec,
                 zgal,
                 specresolution,
                 tie_balmer=False,
                 miles_dir=None,
                 rebin=True,
                 limit_doublets=False,
                 degree_add=None,
                 degree_mult=5,
                 **kwargs):
    """This is a wrapper for pPXF to fit stellar population models as well as
    emission lines to galaxy spectra.  Although pPXF allows otherwise, the
    emission lines are kinematically fixed to one another as are the stellar
    models, and the stars and gas independently vary with one another.

    Please see the pPXF documentation for more details on the vast array of
    parameters and options afforded by the software.

    The pPXF software may be downloaded at
    http://www-astro.physics.ox.ac.uk/~mxc/software/

    Parameters
    ----------
    spec : XSpectrum1D
        Spectrum to be fitted
    zgal : float
        Redshift of galaxy
    specresolution : float
        Spectral resolution (R) of the data
    tie_balmer : bool, optional
        Assume intrinsic Balmer decrement.  See documentation in ppxf_util.py,
        as this has implications for the derived reddening.
    limit_doublets : bool, optional
        Limit the ratios of [OII] and [SII] lines to ranges allowed by atomic
        physics.  See documentation in ppxf_util.py, as this has implications
        for getting the true flux values from those reported.
    degree_add : int, optional
        Degree of the additive Legendre polynomial used to modify the template
        continuum in the fit.
    degree_mult : int,optional
    miles_dir: str, optional
      Location of MILES models

    Returns
    -------
    ppfit : ppxf
        Object returned by pPXF; attributes are data pertaining to the fit
    miles : miles
        Contains information about the stellar templates used in the fit.
        See the documentation in miles_util.py for full details
    weights : 1d numpy vector
        Weights of the *stellar* template components.  Equivalent to the
        first N elements of ppfit.weights where N is the number of stellar
        templates used in the fit.
    """

    if spec is None:
        print('Galaxy has no spectrum associated')
        return None

    ### Spectra must be rebinned to a log scale (in wavelength).
    ### pPXF provides a routine to do this, but the input spectra
    ### must be sampled uniformly linear. linetools to the rescue!
    if rebin:
        meddiff = np.median(spec.wavelength.value[1:] -
                            spec.wavelength.value[0:-1])
        newwave = np.arange(spec.wavelength.value[0],
                            spec.wavelength.value[-1], meddiff)
        spec = spec.rebin(newwave * units.AA, do_sig=True, grow_bad_sig=True)

    # get data and transform wavelength to rest frame for template fitting
    wave = spec.wavelength.to(units.Angstrom).value
    flux = spec.flux.value
    flux = flux * (1. + zgal)
    noise = spec.sig.value
    noise = noise * (1. + zgal)
    wave = wave / (1. + zgal)
    # transform to air wavelengths for MILES templates and get approx. FWHM
    wave *= np.median(util.vac_to_air(wave) / wave)

    # use only wavelength range covered by templates
    mask = (wave > 3540) & (wave < 7409)
    #mask = (wave > 1682) & (wave < 10000.)
    maskidx = np.where(mask)[0]
    # also deal with declared good regions of the spectrum
    if 'goodpixels' in kwargs:
        goodpix = kwargs['goodpixels']
        pixmask = np.in1d(maskidx, goodpix)
        newgoodpix = np.where(pixmask)[0]
        kwargs['goodpixels'] = newgoodpix

    wave = wave[mask]
    flux = flux[mask]
    noise = noise[mask]

    # Nonnegative noise values are not allowed
    noise[noise <= 0] = np.max(noise)

    # pPXF requires the spectra to be log rebinned, so do it
    flux, logLam, velscale = util.log_rebin(np.array([wave[0], wave[-1]]),
                                            flux)
    noise, junk1, junk2 = util.log_rebin(np.array([wave[0], wave[-1]]), noise)

    ### The following lines unnecessary for DEIMOS/Hecto spectra due to their
    ### units but rescaling may be necessary for some
    # galaxy = flux / np.median(flux)  # Normalize spectrum to avoid numerical issues
    # print 'Scale flux by', round(np.median(flux),2)
    galaxy = flux  # use the native units

    # pPXF wants the spectral resolution in units of wavelength
    FWHM_gal = wave / specresolution

    ### Set up stellar templates
    #miles_dir = resource_filename('ppxf', '/miles_models/')
    #miles_dir = resource_filename('ppxf', '/emiles_padova_chabrier/')
    if miles_dir is None:
        miles_dir = resource_filename('ppxf', '/miles_padova_chabrier/')
    #path4libcall = miles_dir + 'Mun1.30*.fits'
    #path4libcall = miles_dir + 'Ech1.30*.fits'
    path4libcall = miles_dir + 'Mch1.30*.fits'
    miles = lib.miles(path4libcall, velscale, FWHM_gal, wave_gal=wave)

    ### Stuff for 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.01  # Desired regularization error

    ### Now the emission lines!  Only include lines in fit region.
    if 'goodpixels' in kwargs:
        gal_lam = wave[newgoodpix]
        # Also, log rebinning the spectrum change which pixels are 'goodpixels'
        newnewgoodpix = np.searchsorted(np.exp(logLam), gal_lam, side='left')
        uqnewnewgoodpix = np.unique(newnewgoodpix)
        if uqnewnewgoodpix[-1] == len(wave):
            uqnewnewgoodpix = uqnewnewgoodpix[:-1]
        kwargs['goodpixels'] = uqnewnewgoodpix

    else:
        gal_lam = wave

    def FWHM_func(wave):  # passed to generate emission line templates
        return wave / specresolution

    gas_templates, gas_names, line_wave = util.emission_lines_mask(
        miles.log_lam_temp,
        gal_lam,
        FWHM_func,
        tie_balmer=tie_balmer,
        limit_doublets=limit_doublets)

    # How many gas components do we have?
    balmerlines = [ll for ll in gas_names if ll[0] == 'H']
    numbalm = len(balmerlines)
    numforbid = len(gas_names) - numbalm

    # Stack all templates
    templates = np.column_stack([stars_templates, gas_templates])

    # other needed quantities
    dv = c * (miles.log_lam_temp[0] - logLam[0])
    ### use the following line if not transforming to z=0 first
    # vel = c * np.log(1 + zgal)  # eq.(8) of Cappellari (2017)
    vel = 0.  # We already transformed to the restframe!
    start = [vel, 25.]  # (km/s), starting guess for [V, sigma]

    ### Set up combination of templates
    n_temps = stars_templates.shape[1]
    n_balmer = 1 if tie_balmer else numbalm  # Number of Balmer lines included in the fit
    n_forbidden = numforbid  # Number of other lines included in the fit

    # Assign component=0 to the stellar templates, component=1 to the Balmer
    # emission lines templates, and component=2 to the forbidden lines.
    # component = [0]*n_temps + [1]*n_balmer + [2]*n_forbidden
    component = [0] * n_temps + [1] * (n_balmer + n_forbidden
                                       )  # tie the gas lines together
    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
    if len(gas_names) > 0:
        moments = [2, 2]  # fix the gas kinematic components to one another
        start = [[vel, 50.],
                 start]  # Adopt different gas/stars starting values
    else:
        moments = [2]  # only stars to be fit
        start = [vel, 50.]

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

    if degree_add is None:
        degree_add = -1
    t = time.time()
    ppfit = ppxf.ppxf(templates,
                      galaxy,
                      noise,
                      velscale,
                      start,
                      plot=False,
                      moments=moments,
                      degree=degree_add,
                      vsyst=dv,
                      lam=np.exp(logLam),
                      clean=False,
                      regul=1. / regul_err,
                      reg_dim=reg_dim,
                      component=component,
                      gas_component=gas_component,
                      gas_names=gas_names,
                      gas_reddening=gas_reddening,
                      mdegree=degree_mult,
                      **kwargs)

    print('Desired Delta Chi^2: %.4g' % np.sqrt(2 * galaxy.size))
    print('Current Delta Chi^2: %.4g' % ((ppfit.chi2 - 1) * galaxy.size))
    print('Elapsed time in PPXF: %.2f s' % (time.time() - t))

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

    return ppfit, miles, weights
Beispiel #3
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
Beispiel #4
0
def ppxf_L_tot(int_spec, header, redshift, vel, dist_mod, dM_err=[0.1, 0.1]):
    """Take in collapsed galaxy spectra, with vel and distance modulus, to calculate L_bol based on various filter bands. \
        Also returns Vega mangitudes for g,r and Johnson V bands, along with sigma (LOSVD).

    Parameters
    ----------
    int_spec : float, array
        Collapsed spectra of the galaxy.
    header : Fits header object
        galaxy's Fits file header, containing information for the function.
    redshift : float
        Redshift of the galaxy, from recession velocity
    vel : float
        recession velocity of the galaxy, in km/s
    dist_mod : float
        Distance modulus for use in the calculation of Lbol and magnitudes
    dM_err : list, optional
        Error in distance modulus, by default [0.1,0.1]

    Returns
    -------
    dict
        lum_bol_g, lum_bol_g_u, lum_bol_g_l, mag_g, mag_r, mag_v, fit_sigma
    """

    # Read a galaxy spectrum and define the wavelength range

    lamRange1 = header['CRVAL3'] + np.array([
        0., header['CD3_3'] * (header['NAXIS3'] - 1)
    ])  #IMPORTANTE: EL RANGO DE LAMBDAS ESTA EN ESCALA LOGARITMICA
    #Transformamos los pixeles en lambdas:
    #lam=np.linspace(lamRange1[0],lamRange[1],len(gal_lin[0,:]))
    FWHM_gal = 2.81  # SAURON has an instrumental resolution FWHM of 4.2A.

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

    galaxy, logLam1, velscale = util.log_rebin(lamRange1, int_spec)
    cond = np.exp(logLam1) <= 6900
    # Getting the apparent magnitude of the galaxy in the g, r and V bands
    mag_g, Flux_g = library(np.exp(logLam1[cond]),
                            galaxy[cond] * 1.0e-20,
                            filt="SDSS",
                            band="g")
    mag_r, Flux_r = library(np.exp(logLam1[cond]),
                            galaxy[cond] * 1.0e-20,
                            filt="SDSS",
                            band="r")
    mag_v, Flux_v = library(np.exp(logLam1[cond]),
                            galaxy[cond] * 1.0e-20,
                            filt="GROUND_JOHNSON",
                            band="V")

    # Converting to absolute magnitude
    M_g = mag_g - dist_mod  #
    M_g_u = mag_g - (dist_mod + dM_err[0])  # upper distance M_g
    M_g_l = mag_g - (dist_mod - dM_err[1])  # lower distance M_g

    # M_r = mag_r - dist_mod
    # M_r_u = mag_r - (dist_mod + dM_err[0]) # upper distance M_g
    # M_r_l = mag_r - (dist_mod - dM_err[1])

    # M_v = mag_v - dist_mod
    # M_v_u = mag_v - (dist_mod + dM_err[0]) # upper distance M_g
    # M_v_l = mag_v - (dist_mod - dM_err[1])

    galaxy = galaxy / np.median(
        galaxy)  # Normalize spectrum to avoid numerical issues
    noise = np.full_like(galaxy,
                         redshift)  # Assume constant noise per pixel here

    # Read the list of filenames from the Single Stellar Population library
    # by Vazdekis (2010, MNRAS, 404, 1639) http://miles.iac.es/. A subset
    # of the library is included for this example with permission
    vazdekis = glob.glob(
        'emiles/Ekb1.30*')  # PUT HERE THE DIRECTORY TO EMILES_STARS
    FWHM_tem = 2.51  # Vazdekis+10 spectra have a constant resolution FWHM of 2.51A.
    velscale_ratio = 2  # adopts 2x higher spectral sampling for templates than for galaxy
    miles_path = path.expanduser("emiles/Ekb1.30Z*.fits")
    miles = lib.miles(miles_path, velscale, FWHM_tem)
    stars_templates = miles.templates.reshape(miles.templates.shape[0], -1)
    reg_dim = miles.templates.shape[1:]
    # Extract the wavelength range and logarithmically rebin one spectrum
    # to a velocity scale 2x smaller than the SAURON galaxy spectrum, to determine
    # the size needed for the array which will contain the template spectra.
    #
    hdu = fits.open(vazdekis[0])
    ssp = hdu[0].data
    h2 = hdu[0].header
    lamRange2 = h2['CRVAL1'] + np.array(
        [0., h2['CDELT1'] * (h2['NAXIS1'] - 1)])
    sspNew, logLam2, velscale_temp = util.log_rebin(lamRange2,
                                                    ssp,
                                                    velscale=velscale /
                                                    velscale_ratio)
    templates = np.empty(
        (sspNew.size, len(vazdekis)))  # PUT HERE THE DIRECTORY TO MILES_STARS

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

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

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

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

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

    cond = np.exp(logLam1) <= 7810  #6900
    logLam1 = logLam1[cond]
    galaxy = galaxy[cond]
    noise = noise[cond]
    goodPixels = util.determine_goodpixels(logLam1, lamRange2, z)

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

    galaxy[np.where(np.isfinite(galaxy) == False)] = 0.0
    noise[np.where(np.isfinite(noise) == False)] = 0.0
    templates[np.where(np.isfinite(templates) == False)] = 0.0

    pp = ppxf(templates,
              galaxy,
              noise * 0.0 + 1.0,
              velscale,
              start,
              goodpixels=goodPixels,
              plot=True,
              moments=4,
              mdegree=15,
              vsyst=dv,
              velscale_ratio=velscale_ratio)

    weights = pp.weights
    normalized_weights = weights / np.sum(weights)

    # Use miles_utils to get metallicity
    weights = pp.weights
    weights = weights.reshape(reg_dim) / weights.sum()  # Normalized
    miles.mean_age_metal(weights)

    fit_sigma = pp.sol

    optimal_template = np.zeros(templates.shape[0])
    for j in range(0, templates.shape[1]):
        optimal_template = optimal_template + templates[:,
                                                        j] * normalized_weights[
                                                            j]

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

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

    # Pass the optimal template to get the bolometric correction for the g-band
    BC_g = transmission(np.exp(logLam2), optimal_template, band="g")
    # BC_r = transmission(np.exp(logLam2), optimal_template, band="r")
    # BC_v = transmission(np.exp(logLam2), optimal_template, band="V")

    # Obtaining the bolometric correction of the Sun
    BC_sun_g, M_sun_g = library(0, 0, filt="SDSS", band="g", get_sun='Y')
    # BC_sun_r, M_sun_r = library(0,0,filt="SDSS", band="r",get_sun='Y')
    # BC_sun_v, M_sun_v = library(0,0,filt="GROUND_JOHNSON", band="V",get_sun='Y')

    # Getting the bolometric luminosity (in solar luminosity) for the g-band
    lum_bol_g = 10.0**(-0.4 * (M_g - M_sun_g)) * 10.0**(-0.4 *
                                                        (BC_g - BC_sun_g))
    lum_bol_g_u = 10.0**(-0.4 * (M_g_u - M_sun_g)) * 10.0**(
        -0.4 * (BC_g - BC_sun_g))  # upper dM L
    lum_bol_g_l = 10.0**(-0.4 * (M_g_l - M_sun_g)) * 10.0**(
        -0.4 * (BC_g - BC_sun_g))  # lower dM L

    # lum_bol_r = 10.0**(-0.4*(M_r-M_sun_r)) * 10.0**(-0.4*(BC_r-BC_sun_r))
    # lum_bol_r_u = 10.0**(-0.4*(M_r_u-M_sun_r)) * 10.0**(-0.4*(BC_r-BC_sun_r)) # upper dM L
    # lum_bol_r_l = 10.0**(-0.4*(M_r_l-M_sun_r)) * 10.0**(-0.4*(BC_r-BC_sun_r)) # lower dM L

    # lum_bol_v = 10.0**(-0.4*(M_v-M_sun_v)) * 10.0**(-0.4*(BC_v-BC_sun_v))
    # lum_bol_v_u = 10.0**(-0.4*(M_v_u-M_sun_v)) * 10.0**(-0.4*(BC_v-BC_sun_v)) # upper dM L
    # lum_bol_v_l = 10.0**(-0.4*(M_v_l-M_sun_v)) * 10.0**(-0.4*(BC_v-BC_sun_v)) # lower dM L

    #lum_bol_r, [lum_bol_r_u, lum_bol_r_l], lum_bol_v, [lum_bol_v_u, lum_bol_v_l],
    result_dict = {
        "Lbol": lum_bol_g,
        "Lbol_err_up": lum_bol_g_u,
        "Lbol_err_lo": lum_bol_g_l,
        "mag_g": mag_g,
        "mag_r": mag_r,
        "mag_v": mag_v,
        "sigma": fit_sigma[1]
    }

    return result_dict