コード例 #1
0
def read_simple_templates(velscale, lamrange):

    hdul = fits.open(UT.lgal_dir() + "/simple_mocks/template_fluxbc03.fits")
    wave_s = hdul[1].data['wave']
    flux_bulge = hdul[1].data['L_bulge']
    flux_disk = hdul[1].data['L_disk']
    hdul.close()

    wave, flux_bulge = to_common_grid(wave_s, flux_bulge, lamrange[0],
                                      lamrange[1])
    wave, flux_disk = to_common_grid(wave_s, flux_disk, lamrange[0],
                                     lamrange[1])

    mask = ((wave >= lamrange[0]) & (wave <= lamrange[1]))
    wave = wave[mask]

    flux_bulge = flux_bulge[mask]
    model1, logLam1, velscale_out = util.log_rebin([wave[0], wave[-1]],
                                                   flux_bulge,
                                                   velscale=velscale)
    model1 /= np.median(model1)
    print(velscale, velscale_out)
    flux_disk = flux_disk[mask]
    model2, logLam2, velscale_out = util.log_rebin([wave[0], wave[-1]],
                                                   flux_disk,
                                                   velscale=velscale)
    model2 /= np.median(model2)

    templates = np.column_stack([model1, model2])
    #print([wave[0], wave[-1]])

    plt.plot(np.exp(1)**logLam1, model1)
    plt.plot(np.exp(1)**logLam2, model2)

    return (logLam1, templates)
コード例 #2
0
def make_templates(wave, stars, emission, velscale, w1, w2, fits):
    """ Prepare file with templates. """
    idx = np.where((wave >= w1) & (wave <= w2))[0]
    wave = wave[idx]
    lrange = np.array([wave[0], wave[-1]])
    stars = stars[idx]
    emission = emission[idx]
    # Rebin data
    tmp, logLam, velscale = util.log_rebin(lrange, stars[:,0],
                                               velscale=velscale)
    tmp2, logLam2, velscale = util.log_rebin(lrange, emission[:,0],
                                               velscale=velscale)
    starsnew = np.zeros((len(logLam), len(stars[0])))
    for i in range(len(stars[0])):
        star, l, velscale = util.log_rebin(lrange, stars[:,i],
                                               velscale=velscale)
        starsnew[:,i] = star
    emissionnew = np.zeros((len(logLam), len(emission[0])))
    for i in range(len(emission[0])):
        em, logLam, velscale = util.log_rebin([wave[0], wave[-1]], emission[:,i],
                                               velscale=velscale)
        emissionnew[:,i] = em
    # print range(len(stars))
    # for i in range(len(stars[0])):
    #     plt.plot(logLam, starsnew[i], "-k")
    # plt.show()
    hdu1 = pf.PrimaryHDU(starsnew.T)
    hdu2 = pf.ImageHDU(emissionnew.T)
    hdu1.header["CRVAL1"] = logLam[0]
    hdu1.header["CD1_1"] = logLam[1] - logLam[0]
    hdu1.header["CRPIX1"] = 1.
    hdulist = pf.HDUList([hdu1, hdu2])
    hdulist.writeto(fits, clobber=True)
コード例 #3
0
	def prepstellarfit(self, spectrum=None, wvl=None):
		""" Prepare stacked cube for spectral fitting with pPXF. Only need to run this once per galaxy!

			Arguments:
				spectrum, wvl (1D arrays): observed spectrum to use as template (default: use stacked spectrum)
		"""

		print('Preparing for stellar kinematics fit...')

		# Define path to pPXF directory
		ppxf_dir = path.dirname(path.realpath(ppxf_package.__file__))

		# Define spectrum
		spectrum = self.stacked_spec[0] if spectrum is None else spectrum
		wvl = self.wvl_cropped if wvl is None else wvl

		# Define wavelength range
		self.lamRange1 = [wvl[0],wvl[-1]]
		fwhm_gal = 2.4/(1+self.z)  # KCWI instrumental FWHM of ~2.4A

		# Rebin spectrum into log scale to get initial velocity scale
		galaxy, logLam1, velscale = util.log_rebin(self.lamRange1, spectrum)

		# Read the list of filenames from the E-MILES SSP library
		vazdekis = glob.glob(ppxf_dir + '/miles_models/Mun*.fits')
		#vazdekis = glob.glob(ppxf_dir + '/miles_stellar/s*.fits')
		fwhm_tem = 2.51  # Vazdekis+10 spectra have a constant resolution FWHM of 2.51A.

		# Open template spectrum in order to make get the size of the template array
		hdu = fits.open(vazdekis[0])
		ssp = np.squeeze(hdu[0].data)
		h2 = hdu[0].header
		self.lamRange2 = h2['CRVAL1'] + np.array([0., h2['CDELT1']*(h2['NAXIS1'] - 1)])
		sspNew, logLam2, velscale_temp = util.log_rebin(self.lamRange2, ssp, velscale=velscale)
		self.templates = np.empty((sspNew.size, len(vazdekis)))

		# Convolve observed spectrum with quadratic difference between observed and template resolution.
		# (This is valid if shapes of instrumental spectral profiles are well approximated by Gaussians.)
		fwhm_dif = np.sqrt(np.abs(fwhm_gal**2 - fwhm_tem**2))
		self.sigma = fwhm_dif/2.355/h2['CDELT1'] # Sigma difference in pixels
		galspec = ndimage.gaussian_filter1d(spectrum, self.sigma)

		# Now logarithmically rebin this new observed spectrum
		galaxy, logLam1, velscale = util.log_rebin(self.lamRange1, galspec, velscale=velscale)

		# Open and normalize all the templates
		for j, file in enumerate(vazdekis):
			hdu = fits.open(file)
			ssp = np.squeeze(hdu[0].data)
			sspNew, self.logLam2, velscale_temp = util.log_rebin(self.lamRange2, ssp, velscale=velscale)
			self.templates[:, j] = sspNew/np.median(sspNew)  # Normalizes templates

		return
コード例 #4
0
def read_simple_templates(velscale, lamrange):

    hdul = fits.open(UT.lgal_dir() + "/simple_mocks/template_fluxbc03.fits")
    wave = hdul[1].data['wave']
    flux_bulge = hdul[1].data['L_bulge']
    flux_disk = hdul[1].data['L_disk']
    hdul.close()

    #put on constant grid, as this is assumed by log_rebin
    wave_s, flux_bulge = to_constant_grid(wave, flux_bulge, lamrange[0],
                                          lamrange[1])
    wave_s, flux_disk = to_constant_grid(wave, flux_disk, lamrange[0],
                                         lamrange[1])

    print('full model wavelength range: ', wave_s[0], wave_s[-1])
    print('requested model wavelength range:', lamrange)

    #flux_bulge = flux_bulge[mask]
    model1, logLam1, velscale_out = util.log_rebin([lamrange[0], lamrange[1]],
                                                   flux_bulge,
                                                   velscale=velscale)
    norm1 = np.median(model1)
    model1 /= norm1
    print(velscale, velscale_out)
    #flux_disk = flux_disk[mask]
    model2, logLam2, velscale_out = util.log_rebin([lamrange[0], lamrange[1]],
                                                   flux_disk,
                                                   velscale=velscale)
    norm2 = np.median(model2)
    model2 /= norm2
    print(velscale, velscale_out)

    #print([wave[0], wave[-1]])

    #protect against data goint outside of models wavelength range
    #Stelib library is defined below 3400, but at lower resolution
    if lamrange[0] < 3400.0:
        lamrange_new = [3400.0, lamrange[1]]
        print(lamrange_new)

        mask = ((np.exp(1)**logLam1 >= lamrange_new[0]) &
                (np.exp(1)**logLam1 <= lamrange_new[1]))
        logLam1 = logLam1[mask]
        logLam2 = logLam2[mask]
        model1 = model1[mask]
        model2 = model2[mask]

    templates = np.column_stack([model1, model2])

    plt.plot(np.exp(1)**logLam1, model1)
    plt.plot(np.exp(1)**logLam2, model2)

    return (logLam1, templates, [norm1, norm2])
コード例 #5
0
def load_templates_regul(velscale):
    """ Load templates into 2D array for regularization"""
    # cd into template folder to make calculations
    current_dir = os.getcwd()
    os.chdir(os.path.join(home, "miles_models"))
    # Extract the wavelength range and logarithmically rebin one spectrum
    # to the same velocity scale of the SAURON galaxy spectrum, to determine
    # the size needed for the array which will contain the template spectra.
    miles = [x for x in os.listdir(".") if x.endswith(".fits")]
    hdu = pf.open(miles[0])
    ssp = hdu[0].data
    h2 = hdu[0].header
    lamRange2 = h2["CRVAL1"] + np.array([0.0, h2["CDELT1"] * (h2["NAXIS1"] - 1)])

    sspNew, logLam2, velscale = util.log_rebin(lamRange2, ssp, velscale=velscale)
    # Ordered array of metallicities
    Zs = set([x.split("Z")[1].split("T")[0] for x in miles])
    Zs = [float(x.replace("m", "-").replace("p", "")) for x in Zs]
    Zs.sort()
    Z2 = Zs[:]
    for i in range(len(Zs)):
        if Zs[i] < 0:
            Zs[i] = "{0:.2f}".format(Zs[i]).replace("-", "m")
        else:
            Zs[i] = "p{0:.2f}".format(Zs[i])
    # Ordered array of ages
    Ts = list(set([x.split("T")[1].split(".fits")[0] for x in miles]))
    Ts.sort()
    # Create a three dimensional array to store the
    # two dimensional grid of model spectra
    #
    nAges = len(Ts)
    nMetal = len(Zs)
    templates = np.empty((sspNew.size, nAges, nMetal))

    # Here we make sure the spectra are sorted in both [M/H]
    # and Age along the two axes of the rectangular grid of templates.
    # A simple alphabetical ordering of Vazdekis's naming convention
    # does not sort the files by [M/H], so we do it explicitly below
    miles = []
    for k in range(nMetal):
        for j in range(nAges):
            filename = "{3}1.30Z{0}T{1}.fits".format(Zs[k], Ts[j], imf)
            ssp = pf.getdata(filename)
            sspNew, logLam2, velscale = util.log_rebin(lamRange2, ssp, velscale=velscale)
            templates[:, j, k] = sspNew  # Templates are *not* normalized here
            miles.append(filename)
    templates /= np.median(templates)  # Normalizes templates by a scalar
    os.chdir(current_dir)
    return templates, logLam2, Ts, Z2, miles, h2["CDELT1"]
コード例 #6
0
def rebin_spectra(cube, x, y, lamRange, lamb):
    """
    Extracts the a single spectrum for a data given the desired x,y by binning the data using pPXF util.log_rebin
    
    Parameters
    ----------
    cube : the datacube
    x : x-coordinate of the spaxel
    y : y-coordinate of the spaxel
    lamRange : the wavelength range o
    lamb : the wavelength array 

    Returns
    -------
    specNew : the binned spectrum 
    logLam : the logged wavelength array
    velscale: the velocity scale of the spectrum
    """

    x = np.atleast_1d(x)
    y = np.atleast_1d(y)
    spectrum = np.median(np.array(
        [cube[:, int(ypair), int(xpair)] for ypair, xpair in zip(y, x)]),
                         axis=0)
    c = 299792.458  # speed of light in km/s
    specNew, logLam, velscale = util.log_rebin(lamRange,
                                               spectrum,
                                               oversample=False,
                                               velscale=c *
                                               np.log(lamb[1] / lamb[0]),
                                               flux=False)
    return (specNew, logLam, velscale)
コード例 #7
0
def load_CaT_templates(FWHM_galaxy, velscale, cat_dir="FIXME"):
    """
    Load the CaT template library
    """

    CaT = glob.glob(cat_dir + 'Cun1.*.fits')
    CaT.sort()

    #CaT Templates are at a FWHM of 1.51 Angstroms
    FWHM_tem = 1.51

    #FIXME
    #This depends on where in the wavelength range you are. For SWIFT:
    #9300A- 3.44A
    #10120A- 4.55A
    #8700- 4.04
    #To get a more accurate value for a certain lamda, fit a gaussian to a skyline!

    hdu = fits.open(CaT[0])
    ssp = hdu[0].data
    h2 = hdu[0].header

    lam_temp = h2['CRVAL1'] + h2['CDELT1'] * np.arange(h2['NAXIS1'])
    lamRange2 = [np.min(lam_temp), np.max(lam_temp)]
    print("LamRange2 is {}".format(lamRange2))
    sspNew, logLam2, velscale = util.log_rebin(lamRange2,
                                               ssp,
                                               velscale=velscale)
    templates = np.empty((sspNew.size, len(CaT)))
    t_num = len(CaT)
    FWHM_dif = np.sqrt((FWHM_galaxy**2 - FWHM_tem**2))
    sigma = FWHM_dif / 2.355 / h2['CDELT1']  # Sigma difference in pixels

    print("Sigma is {}".format(sigma))

    for j, fname in enumerate(CaT):
        hdu = fits.open(fname)
        ssp = hdu[0].data
        ssp = util.gaussian_filter1d(
            ssp, sigma)  # perform convolution with variable sigma
        sspNew, logLam2, velscale = util.log_rebin(lamRange2,
                                                   ssp,
                                                   velscale=velscale)

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

    return templates, loglam2
コード例 #8
0
def mask_and_log_rebin(lamdas, spec, lamRange, velscale=None):
    """Mask and log rebin a spectrum

    """

    low_lam, high_lam = lamRange

    mask = np.where((lamdas >= low_lam) & (lamdas <= high_lam))[0]

    spec = spec[mask]

    if velscale is None:
        spec, logLam1, velscale = util.log_rebin(lamRange, spec)
    else:
        spec, logLam1, velscale = util.log_rebin(lamRange, spec)

    return spec, logLam1, velscale
コード例 #9
0
def retrieveTemplates(velscale, FWHM_WiFeS_ang, range_sel=None):
    """
    This function reads and sets up the stellar libraries for pPXF
    This function was written by Amelia Fraser-McKelvie
    """
    templatesInput = glob.glob(
        '/Users/vaishalp/Dropbox/Amelia/IC1059/Archive/StellarLibraries/INDO-US_Valdes/*.txt'
    )
    FWHM_tem_ang = 1.35  #In \AA

    #SINGLE TEMPLATE
    wv_temp1, inten_temp1 = readAsciiSpec(templatesInput[0])
    if range_sel:
        range_sel_rest = range_sel
    else:
        range_sel_rest = [np.min(wv_temp1), np.max(wv_temp1)]

    #Selection Chunk of template
    sel_temp = np.nonzero((wv_temp1 >= range_sel_rest[0])
                          & (wv_temp1 <= range_sel_rest[1]))
    wv_temp, inten_temp = wv_temp1[sel_temp], inten_temp1[sel_temp]
    sspNew, logLam2, velscale = util.log_rebin([wv_temp[0], wv_temp[-1]],
                                               inten_temp,
                                               velscale=velscale)

    #ALL TEMPLATES
    templates = np.empty((sspNew.size, len(templatesInput)))
    #convolution
    FWHM_dif = np.sqrt(FWHM_WiFeS_ang**2 - FWHM_tem_ang**2)
    sigma = FWHM_dif / 2.355 / (wv_temp[1] - wv_temp[0]
                                )  # Sigma difference in pixels
    for ii in range(len(templatesInput)):
        wv_temp2, inten_temp2 = readAsciiSpec(templatesInput[ii])
        wv_temp_ii, inten_temp_ii = wv_temp2[sel_temp], inten_temp2[sel_temp]
        ssp = ndimage.gaussian_filter1d(
            inten_temp_ii, sigma)  #One-dimensional Gaussian filter.
        sspNew, logLam2, velscale = util.log_rebin([wv_temp[0], wv_temp[-1]],
                                                   ssp,
                                                   velscale=velscale)

        templates[:, ii] = sspNew / np.median(sspNew)
        stdout.write("\rDONE %i/%i" % (ii + 1, len(templatesInput)))
        stdout.flush()

    return logLam2, templates
コード例 #10
0
def stellar_templates(velscale):
    """ Load files with stellar library used as templates. """
    current_dir = os.getcwd()
    # Template directory is also set in config.py
    os.chdir(templates_dir)
    miles = [x for x in os.listdir(".") if x.startswith("Mbi1.30") and x.endswith(".fits")]
    # Ordered array of metallicities
    Zs = set([x.split("Z")[1].split("T")[0] for x in miles])
    Zs = [float(x.replace("m", "-").replace("p", "").replace("_", ".")) for x in Zs]
    Zs.sort()
    for i in range(len(Zs)):
        if Zs[i] < 0:
            Zs[i] = "{0:.2f}".format(Zs[i]).replace("-", "m")
        else:
            Zs[i] = "p{0:.2f}".format(Zs[i])
    # Ordered array of ages
    Ts = list(set([x.split("T")[1][:7] for x in miles]))
    Ts.sort()
    # Ordered array of [alpha/Fe]
    As = sorted(list(set([x.split("Ep")[1][:4] for x in miles])))
    miles = []
    metal_ages_alphas = []
    for a in As:
        for m in Zs:
            for t in Ts:
                filename = "Mbi1.30Z{0}T{1}_iTp{2}_Ep{2}_" "linear_FWHM_2.51.fits".format(m, t, a)
                if os.path.exists(filename):
                    miles.append(filename)
                    metal_ages_alphas.append(
                        [m.replace("_", ".").replace("p", "+").replace("m", "-"), t.replace("_", "."), a]
                    )
    hdu = pf.open(miles[0])
    ssp = hdu[0].data
    h2 = hdu[0].header
    lamRange2 = h2["CRVAL1"] + np.array([0.0, h2["CDELT1"] * (h2["NAXIS1"] - 1)])
    sspNew, logLam2, velscale = util.log_rebin(lamRange2, ssp, velscale=velscale)
    templates = np.empty((sspNew.size, len(miles)))
    for j in range(len(miles)):
        hdu = pf.open(miles[j])
        ssp = hdu[0].data
        sspNew, logLam2, velscale = util.log_rebin(lamRange2, ssp, velscale=velscale)
        templates[:, j] = sspNew
    templates /= np.median(templates)
    os.chdir(current_dir)
    return templates, logLam2, h2["CDELT1"], miles
コード例 #11
0
def make_ssp_templates_bc03(velscale, lamrange, ages, metallicities):

    N = len(ages) * len(metallicities)  #total number of templates

    age_m, flux_m, wave_m, Z_m = my_utils.read_models_bc03()
    D = len(wave_m)

    #first interpolate in age, keep metallicity
    models_t = np.zeros((len(Z_m), D, len(ages)), dtype=np.float32)
    for l in range(0, D):
        for z in range(0, len(Z_m)):
            #print(l,z)
            models_t[z, l, :] = np.interp(np.log10(ages * 1e9),
                                          np.log10(age_m * 1e9), flux_m[z,
                                                                        l, :])

    #then interpolate in metallicity
    #flux_out= np.zeros( (len(metallicities), D, len(ages)), dtype=np.float32)
    templates = np.zeros((D, N))
    c = 0
    for j in range(0, len(metallicities)):
        for i in range(0, len(ages)):
            for l in range(0, D):
                templates[l, c] = np.interp(np.log10(metallicities[j]),
                                            np.log10(Z_m), models_t[:, l, i])
            c = c + 1

    #now mask and re-grid
    mask = ((wave_m >= lamrange[0]) & (wave_m <= lamrange[1]))
    wave = wave_m[mask]
    template_rb, logLam, velscale_out = util.log_rebin([wave[0], wave[-1]],
                                                       templates[mask, 0],
                                                       velscale=velscale)

    templates_out = np.zeros((len(logLam), N))
    templates_out[:, 0] = template_rb / np.median(template_rb)
    plt.plot(np.exp(1)**logLam, templates_out[:, 0])
    for i in range(1, N):
        templates_out[:, i], logLam, velscale_out = util.log_rebin(
            [wave[0], wave[-1]], templates[mask, i], velscale=velscale)
        templates_out[:,
                      i] = templates_out[:, i] / np.median(templates_out[:, i])
        plt.plot(np.exp(1)**logLam, templates_out[:, i])

    return logLam, templates_out
コード例 #12
0
def make_ssp_templates_fsps(velscale, lamrange, ages, metallicities):

    #initialise FSPS models
    sp = fsps.StellarPopulation(compute_vega_mags=False, zcontinuous=1, sfh=0)

    #total number of templates
    N = len(ages) * len(metallicities)

    #put in log10(Z/Z_solar)
    metallicities = np.log10(np.array(metallicities) / 0.019)

    #get the wavelength vector and initialise the array to hold all the templates
    wave = set_common_grid()
    templates = np.zeros((len(wave), N))

    #now loop through the given metallicities and ages to create each template in turn
    c = 0
    for Z in metallicities:
        sp.params['logzsol'] = Z
        for age in ages:
            wave_fsps, flux_fsps = sp.get_spectrum(tage=age,
                                                   peraa=True)  #in L_solar/AA
            #now to common grid
            wave, flux = to_common_grid(wave_fsps, flux_fsps, lamrange[0],
                                        lamrange[1])
            templates[:, c] = flux / np.median(flux)
            c = c + 1

    #now mask and re-grid
    mask = ((wave >= lamrange[0]) & (wave <= lamrange[1]))
    wave = wave[mask]
    template_rb, logLam, velscale_out = util.log_rebin([wave[0], wave[-1]],
                                                       templates[mask, 0],
                                                       velscale=velscale)

    templates_out = np.zeros((len(logLam), N))
    templates_out[:, 0] = template_rb
    plt.plot(np.exp(1)**logLam, templates_out[:, 0])
    for i in range(1, N):
        templates_out[:, i], logLam, velscale_out = util.log_rebin(
            [wave[0], wave[-1]], templates[mask, i], velscale=velscale)
        templates_out[:, i] = templates_out[:, i]
        plt.plot(np.exp(1)**logLam, templates_out[:, i])
    return logLam, templates_out
コード例 #13
0
def emission_templates(velscale):
    """ Load files with stellar library used as templates. """
    emission = [x for x in os.listdir(".") if x.startswith("emission") and x.endswith(".fits")]
    emission.sort()
    c = 299792.458
    FWHM_tem = 2.54  # MILES library spectra have a resolution FWHM of 2.54A.
    # Extract the wavelength range and logarithmically rebin one spectrum
    # to the same velocity scale of the SAURON galaxy spectrum, to determine
    # the size needed for the array which will contain the template spectra.
    #
    hdu = pf.open(emission[0])
    ssp = hdu[0].data
    h2 = hdu[0].header
    lamRange2 = h2["CRVAL1"] + np.array([0.0, h2["CDELT1"] * (h2["NAXIS1"] - 1)])
    sspNew, logLam2, velscale = util.log_rebin(lamRange2, ssp, velscale=velscale)
    templates = np.empty((sspNew.size, len(emission)))
    for j in range(len(emission)):
        hdu = pf.open(emission[j])
        ssp = hdu[0].data
        sspNew, logLam2, velscale = util.log_rebin(lamRange2, ssp, velscale=velscale)
        templates[:, j] = sspNew
    # templates *= 1e5 # Normalize templates
    return templates, logLam2, h2["CDELT1"], emission
コード例 #14
0
def make_templates(wave, stars, emission, velscale, w1, w2, fits):
    """ Prepare file with templates. """
    idx = np.where((wave >= w1) & (wave <= w2))[0]
    wave = wave[idx]
    lrange = np.array([wave[0], wave[-1]])
    stars = stars[idx]
    emission = emission[idx]
    # Rebin data
    tmp, logLam, velscale = util.log_rebin(lrange,
                                           stars[:, 0],
                                           velscale=velscale)
    tmp2, logLam2, velscale = util.log_rebin(lrange,
                                             emission[:, 0],
                                             velscale=velscale)
    starsnew = np.zeros((len(logLam), len(stars[0])))
    for i in range(len(stars[0])):
        star, l, velscale = util.log_rebin(lrange,
                                           stars[:, i],
                                           velscale=velscale)
        starsnew[:, i] = star
    emissionnew = np.zeros((len(logLam), len(emission[0])))
    for i in range(len(emission[0])):
        em, logLam, velscale = util.log_rebin([wave[0], wave[-1]],
                                              emission[:, i],
                                              velscale=velscale)
        emissionnew[:, i] = em
    # print range(len(stars))
    # for i in range(len(stars[0])):
    #     plt.plot(logLam, starsnew[i], "-k")
    # plt.show()
    hdu1 = pf.PrimaryHDU(starsnew.T)
    hdu2 = pf.ImageHDU(emissionnew.T)
    hdu1.header["CRVAL1"] = logLam[0]
    hdu1.header["CD1_1"] = logLam[1] - logLam[0]
    hdu1.header["CRPIX1"] = 1.
    hdulist = pf.HDUList([hdu1, hdu2])
    hdulist.writeto(fits, clobber=True)
コード例 #15
0
	def ppxf_fit(self, spectrum, noise, verbose=False):
		"""Run pPXF on one spaxel. Note: must run prepstellarfit() first!

		Arguments:
			spectrum, noise (1D array): Observed spectrum and variance array
			verbose (bool): If 'True', make diagnostic plots

		Returns:
			params (float 1D array): Best fit velocity, velocity dispersion, velocity error, velocity dispersion error
		"""

		# Make sure spectrum and noise arrays are the right length
		if spectrum.size != self.wvl_cropped.size:
			raise ValueError('Size of spectrum array %s does not match size of wvl array %s' % (spectrum.size, self.wvl_cropped.size))
		if noise.size != self.wvl_cropped.size:
			raise ValueError('Size of noise array %s does not match size of wvl array %s' % (noise.size, self.wvl_cropped.size))

		# Prep the observed spectrum
		galspec = ndimage.gaussian_filter1d(spectrum, self.sigma)
		galaxy, logLam1, velscale = util.log_rebin(self.lamRange1, galspec)
		galaxy = galaxy/np.median(galaxy)

		# Shift the template to fit the starting wavelength of the galaxy spectrum
		c = 299792.458
		dv = (self.logLam2[0] - logLam1[0])*c  # km/s

		goodPixels = util.determine_goodpixels(logLam1, self.lamRange2, 0)

		# Here the actual fit starts. The best fit is plotted on the screen
		start = [0., 200.]  # (km/s), starting guess for [V, sigma]

		if verbose: plot=True
		else: plot=False

		pp = ppxf(self.templates, galaxy, np.sqrt(noise), velscale, start,
				  goodpixels=goodPixels, plot=plot, moments=2,
				  degree=6, vsyst=dv, clean=False, quiet=True)
		if verbose:
			plt.show()

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

			print('Best-fitting redshift z:', (self.z + 1)*(1 + pp.sol[0]/c) - 1)

		return np.asarray([pp.sol[0], pp.sol[1], pp.error[0]*np.sqrt(pp.chi2), pp.error[1]*np.sqrt(pp.chi2), pp.chi2]), np.exp(logLam1), pp.bestfit, galaxy, noise
コード例 #16
0
 def __init__(self, velscale, sigma, _noread=False):
     """ Load Miles templates for simulations. """
     self.velscale = velscale
     self.sigma = sigma
     miles_path = os.path.join(context.basedir, "ppxf/miles_models")
     # Search for spectra and their properties
     fitsfiles = [_ for _ in os.listdir(miles_path) if _.endswith(".fits")]
     # Define the different values of metallicities and ages of templates
     self.metals = np.unique([
         float(
             _.split("Z")[1].split("T")[0].replace("m",
                                                   "-").replace("p", "+"))
         for _ in fitsfiles
     ])
     self.ages = np.unique([
         float(
             _.split("T")[1].split("_iP")[0].replace("m",
                                                     "-").replace("p", "+"))
         for _ in fitsfiles
     ])
     # Defining arrays
     self.ages2D, self.metals2D = np.meshgrid(self.ages, self.metals)
     self.metals1D = self.metals2D.reshape(-1)
     self.ages1D = self.ages2D.reshape(-1)
     if _noread:
         return
     templates, norms = [], []
     for metal, age in zip(self.metals1D, self.ages1D):
         template_file = os.path.join(miles_path,
                                      self.miles_filename(metal, age))
         spec = read_fits_spectrum1d(template_file)
         wave = spec.dispersion
         flux = spec.flux
         speclog, logwave, _ = util.log_rebin([wave[0], wave[-1]],
                                              flux,
                                              velscale=self.velscale)
         speclog = gaussian_filter1d(speclog, sigma / velscale)
         wave = wave[1:-2]
         flux = spectres(wave, np.exp(logwave), speclog)
         norm = np.sum(flux)
         templates.append(flux / norm)
     self.templates = np.array(templates)
     self.norms = np.array(norms)
     self.wave = wave
     return
コード例 #17
0
def emission_line_template(lines, velscale, res=2.5, intens=None, resamp=15, return_log=True):
    lines = np.atleast_1d(lines)
    if intens == None:
        intens = np.ones_like(lines) * 1e-5
    current_dir = os.getcwd()
    os.chdir(templates_dir)
    refspec = [x for x in os.listdir(".") if x.endswith(".fits") and x.startswith("Mbi")][0]
    lamb = wavelength_array(refspec)
    delta = lamb[1] - lamb[0]
    lamb2 = np.linspace(lamb[0] - delta / 2.0, lamb[-1] + delta / 2.0, len(lamb + 1) * resamp)
    sigma = res / (2.0 * np.sqrt(2.0 * np.log(2.0)))
    spec = np.zeros_like(lamb2)
    for line, intensity in zip(lines, intens):
        spec += intensity * np.exp(-(lamb2 - line) ** 2 / (2 * sigma * sigma))
    spec = np.sum(spec.reshape(len(lamb), resamp), axis=1)
    if not return_log:
        return spec
    specNew, logLam2, velscale = util.log_rebin([lamb[0], lamb[-1]], spec, velscale=velscale)
    os.chdir(current_dir)
    return specNew
コード例 #18
0
def make_gaussian_spectrum(lamdas, continuum, z, sigma_kms, peak_flux):

    scale = sigma_kms / const.c * 1000.0 * np.sqrt(2 * np.pi)
    specNew, logLam, velscale = util.log_rebin(
        np.array([lamdas[0], lamdas[-1]]), continuum)
    emission_line = gaussian(np.exp(logLam), 0.65626 * (1 + z),
                             sigma_kms / const.c * 1000.0, scale)

    #Hack to get the height roughly right...
    #Come back to this!
    ha_pixel = np.argmin(np.abs(lamdas - 0.65626 * (1 + z)))
    continuum_at_emission_line = np.mean(continuum[ha_pixel - 10:ha_pixel +
                                                   10])
    log_spec = specNew + emission_line * (continuum_at_emission_line) * (
        peak_flux - 1)

    interpolator = si.interp1d(np.exp(logLam), log_spec, bounds_error=False)
    spectrum = interpolator(lamdas)
    spectrum[~np.isfinite(spectrum)] = spectrum[-2]
    #Make the spectrum by combining the continuum and emission line
    # spectrum=continuum + lin_spec

    return spectrum
コード例 #19
0
ファイル: ssp_logrebin.py プロジェクト: Minnielam/Simple_Test

"""
Log rebin data

"""

start_wave = min(rss._wave)
end_wave = max(rss._wave)

print(start_wave, end_wave)

dataconv = item()

for yvar in range(y):
    [specNew, logLam, velscale] = log_rebin((start_wave, end_wave),
                                            rss._data[yvar, :])
    [errNew, logLam_err, velscale_err] = log_rebin((start_wave, end_wave),
                                                   (rss._error[yvar, :])**2)
    dataconv.y.append(yvar)
    dataconv.flux.append(specNew)
    dataconv.err.append(np.sqrt(errNew))
    dataconv.wave.append(np.exp(logLam))
    dataconv.specres.append(velscale)
    dataconv.lrange.append(
        [int(min(np.exp(logLam))),
         math.ceil(max(np.exp(logLam)))])

print(np.shape(dataconv.y))
print(np.shape(dataconv.flux))
print(np.array(dataconv.wave)[0, :])
print(rss._header['CRVAL1'])
コード例 #20
0
ファイル: mpl8_multi.py プロジェクト: ddzs1234/pair-m-z
def ppxf_example_kinematics_sdss(dirfile, galaxy, lam_gal, plateifu, mask,
                                 noise, redshift):

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

    z = redshift
    lam_gal = lam_gal
    mask = mask

    c = 299792.458
    frac = lam_gal[1] / lam_gal[0]

    a = np.full((1, 4563), 2.76)
    fwhm_gal = a[0][mask]

    velscale = np.log(frac) * c

    vazdekis = glob.glob(ppxf_dir + '/miles_models/Mun1.30Z*.fits')
    fwhm_tem = 2.51
    hdu = fits.open(vazdekis[0])
    ssp = hdu[0].data
    h2 = hdu[0].header
    lam_temp = h2['CRVAL1'] + h2['CDELT1'] * np.arange(h2['NAXIS1'])
    lamRange_temp = [np.min(lam_temp), np.max(lam_temp)]
    sspNew = util.log_rebin(lamRange_temp, ssp, velscale=velscale)[0]
    templates = np.empty((sspNew.size, len(vazdekis)))
    fwhm_gal = np.interp(lam_temp, lam_gal, fwhm_gal)

    fwhm_dif = np.sqrt((fwhm_gal**2 - fwhm_tem**2).clip(0))
    sigma = fwhm_dif / 2.355 / h2['CDELT1']  # Sigma difference in pixels

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

    c = 299792.458
    dv = np.log(lam_temp[0] / lam_gal[0]) * c  # km/s
    goodpixels = util.determine_goodpixels(np.log(lam_gal), lamRange_temp, z)

    vel = c * np.log(1 + z)  # eq.(8) of Cappellari (2017)
    start = [vel, 200.]  # (km/s), starting guess for [V, sigma]

    pp = stellar.ppxf(dirfile,
                      templates,
                      galaxy,
                      noise,
                      velscale,
                      start,
                      z,
                      goodpixels=goodpixels,
                      plot=True,
                      moments=4,
                      degree=12,
                      vsyst=dv,
                      clean=False,
                      lam=lam_gal)
    return pp.bestfit, pp.lam
コード例 #21
0
    def apply_ppxf(self,
                   galnoise,
                   FWHM_gal,
                   FWHM_tem,
                   z,
                   moments,
                   plot=False,
                   directory_plots=''):
        """This is the method that uses the PPxF package and applies it to the trimmed
        data. It saves the results in the class variable 'results_ppxf'. It also has the
        possibility of saving the plots that it produces.

        Parameters
        ----------
        galnoise : float
            Noise in the galaxy's data.
        FWHM_gal : float
            Full width-half maximum of the galaxy.
        FWHM_tem : float
            Full width-half maximum of the templates.
        z : float
            The galaxy's redshift.
        moments : int
            The number of moments that should be used with PPxF.
        plot : boolean, optional
            Boolean to determine if the plots should be saved.
        directory_plots :  String
            String that represents the absolute path where the plots are 
            to be saved at.

        """

        # make sure that the list of results is empty
        self.results_ppxf = []

        temp_header, ssp = self.templates[self.templates_names[0]]

        CRVAL1h3 = temp_header["CRVAL1"]
        CRPIX1h3 = temp_header["CRPIX1"]
        CDELT1h3 = temp_header["CDELT1"]
        NAXIS1h3 = temp_header["NAXIS1"]

        # calculate the wavelenght range from the templates
        lamRange2 = (CRVAL1h3 - CRPIX1h3 * CDELT1h3) + np.array(
            [0., (CDELT1h3 * NAXIS1h3 - 1.0)])

        # speed of light
        c = 299792.458

        # Initial estimate of the galaxy's velocity in km/s
        vel = c * z

        # I NEED TO UNDERSTAND WHAT THE FOLLOWING TWO LINES MEAN
        goodPixels = np.arange(
            1150,
            1820)  #(1150,1820) #[1150,1820] or perhaps an np.arange(1150,1820)

        start = [vel, 120.]

        # The current pixel being worked on
        number_of_pixel = 1

        ### Start nested loop
        for i in np.arange(self.cube.trimmed_data.shape[1]):

            for j in np.arange(self.cube.trimmed_data.shape[2]):

                # the spectrum corresponding to these pixel coordinates
                spec = self.cube.trimmed_data[:, i, j]

                galaxy, logLam1, velscale = util.log_rebin(
                    self.cube.wavelength_range, spec)

                galaxy = galaxy / np.median(galaxy)  # Normalizing the spectrum
                noise = galaxy * 0 + galnoise  # Assuming constant noise per pixel here

                sspNew, logLam2, velscale = util.log_rebin(lamRange2,
                                                           ssp,
                                                           velscale=velscale)

                templates = np.zeros((sspNew.shape[0], self.ntemplates))

                FWHM_dif = np.sqrt((FWHM_gal**2 - FWHM_tem**2))

                sigma = FWHM_dif / 2.355 / CDELT1h3

                #list_of_templates_names = list(templates_dictionary.keys())

                k = 0

                while k < self.ntemplates:
                    h3, ssp = self.templates[self.templates_names[k]]
                    ssp = util.gaussian_filter1d(
                        ssp, sigma)  # perform convolution with variable
                    sspNew = util.log_rebin(lamRange2, ssp,
                                            velscale=velscale)[0]
                    templates[:, k] = sspNew / np.median(sspNew)
                    k += 1

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

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

                self.results_ppxf.append(pp)

                if plot:
                    plt.rcParams['figure.figsize'] = (32, 20)
                    myplt = pp.plot()
                    plt.savefig(directory_plots + str(number_of_pixel) +
                                ".pdf")
                    plt.clf()
                    number_of_pixel += 1
コード例 #22
0
def ppxf_example_simulation():

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    plt.tight_layout()
    plt.pause(1)
コード例 #23
0
def degrade_templates(template_dir,
                      FWHM_templates,
                      FWHM_observation,
                      velscale_obs,
                      vel_ratio=1):
    """
    Function that combines pPXF "log_rebin" and "gaussian_filter1d" convolution 
    to return a list of template spectra that match the resolution and gridding of
    the observed data. 

    :param template_dir: A character string describing the path to the directory
        containing the raw template files. 
    :param FWHM_templates: A float describing the resolution of the templates.
    :param FWHM_observation: A float describing the resolution of the observation.
        This can be different from the instrumental resolution if the galaxy is at
        a signifcant redshift. 
    :param vescale_obs: A float describing the velocity scale in km/s per pixels of
        the observed spectra.
    :param vel_ratio: A float describing if you would like the velocity scale of 
        the templates to be sampled at a higher rate. 

    :return: An array of templates that have been degraded and rebinned to match
        the observations.

    """

    vazdekis = glob.glob(
        template_dir
    )  # returns list of all template files contained within directory
    FWHM_tem = FWHM_templates
    FWHM_gal = FWHM_observation
    velscale = velscale_obs

    hdu = fits.open(vazdekis[0])  # opening the first template file in the list
    ssp = hdu[0].data  #  and measuring the wavelength range and SSP
    h2 = hdu[0].header  #  size for initiallising the template array.
    lamRange2 = h2['CRVAL1'] + np.array(
        [0., h2['CDELT1'] *
         (h2['NAXIS1'] - 1)])  # Gives the range of the spectra wavelengths
    sspNew, logLam2, velscale_temp = util.log_rebin(lamRange2,
                                                    ssp,
                                                    velscale=velscale /
                                                    vel_ratio)
    templates = np.empty((sspNew.size, len(vazdekis)))
    FWHM_dif = np.sqrt(FWHM_gal**2 - FWHM_tem**2)
    sigma = FWHM_dif / 2.355 / h2['CDELT1']  # Sigma difference in resolutions

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

    return templates, logLam2
コード例 #24
0
ファイル: run_ppxf.py プロジェクト: kadubarbosa/hydramuse
def run_ppxf(fields, w1, w2, targetSN, tempfile, logdir, redo=False,
             ncomp=2, **kwargs):
    """ New function to run pPXF. """
    global velscale
    stars = pf.getdata(tempfile, 0)
    emission = pf.getdata(tempfile, 1)
    logLam_temp = wavelength_array(tempfile, axis=1, extension=0)
    ngas = len(emission)
    nstars = len(stars)
    templates = np.column_stack((stars.T, emission.T))
    ##########################################################################
    # Set components
    if ncomp == 1:
        components = np.zeros(nstars + ngas)
        kwargs["component"] = components
    elif ncomp == 2:
        components = np.hstack((np.zeros(nstars), np.ones(ngas))).astype(int)
        kwargs["component"] = components
    ##########################################################################
    for f in fields:
        print "Working on Field {0}".format(f[-1])
        os.chdir(os.path.join(data_dir, "combined_{0}".format(f)))
        outdir = os.path.join(os.getcwd(), logdir)
        if not os.path.exists(outdir):
            os.mkdir(outdir)
        fits = "binned_sn{0}_res2.95.fits".format(targetSN)
        data = pf.getdata(fits, 0)
        w = wavelength_array(fits, axis=1, extension=0)
        bins = wavelength_array(fits, axis=2, extension=0)
        ######################################################################
        # Slice array before fitting
        idx = np.where(np.logical_and(w >= w1, w <= w2))[0]
        data = data[:,idx]
        w = w[idx]
        ######################################################################
        for i,bin in enumerate(bins):
            output = os.path.join(outdir, "{1}_bin{2:04d}.pkl".format(targetSN,
                                                                      f, bin))
            outroot = output.replace(".pkl", "")
            if os.path.exists(output) and not redo:
                continue
            print "PPXF run {0}/{1}".format(i+1, len(bins))
            spec = data[i,:]
            signal, noise, sn = snr(spec)
            galaxy, logLam, vtemp = util.log_rebin([w[0], w[-1]],             \
                                                      spec, velscale=velscale)
            dv = (logLam_temp[0]-logLam[0])*c
            lam = np.exp(logLam)
            name = "{0}_bin{1:04d}".format(f, bin)
            noise =  np.ones_like(galaxy) * noise
            kwargs["lam"] = lam
            ###################################################################
            # Masking bad pixels
            skylines = np.array([5577,5889, 6300, 6863])
            goodpixels = np.arange(len(lam))
            for line in skylines:
                sky = np.argwhere((lam < line - 10) | (lam > line + 10)).ravel()
                goodpixels = np.intersect1d(goodpixels, sky)
            kwargs["goodpixels"] = goodpixels
            ###################################################################
            kwargs["vsyst"] = dv
            pp = ppxf(templates, galaxy, noise, velscale, **kwargs)
            title = "Field {0} Bin {1}".format(f[-1], bin)
            pp.name = name
            # Adding other things to the pp object
            pp.has_emission = True
            pp.dv = dv
            pp.w = np.exp(logLam)
            pp.velscale = velscale
            pp.ngas = ngas
            pp.ntemplates = nstars
            pp.templates = 0
            pp.id = id
            pp.name = name
            pp.title = title
            ppsave(pp, outroot=outroot)
            ppf = ppload(outroot)
            ppf = pPXF(ppf, velscale)
            ppf.plot("{1}/{0}.png".format(name, outdir))
    return
コード例 #25
0
def ppxf_example_kinematics_sdss():

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

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

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

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

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

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

    # Extract the wavelength range and logarithmically rebin one spectrum
    # to the same velocity scale of the SDSS 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
    lam_temp = h2['CRVAL1'] + h2['CDELT1'] * np.arange(h2['NAXIS1'])
    lamRange_temp = [np.min(lam_temp), np.max(lam_temp)]
    sspNew = util.log_rebin(lamRange_temp, ssp, velscale=velscale)[0]
    templates = np.empty((sspNew.size, len(vazdekis)))

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

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

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

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

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

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

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

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

    print('Elapsed time in PPXF: %.2f s' % (clock() - t))
コード例 #26
0
ファイル: ppxf.py プロジェクト: 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
コード例 #27
0
ファイル: ppxf_wrapper.py プロジェクト: luisgdh/ifscube
    def fit(self, wavelength, data, mask=None, initial_velocity=0.0, initial_sigma=150.0, fwhm_gal=2, fwhm_model=1.8,
            noise=0.05, plot_fit=False, quiet=False, deg=4, moments=4, **kwargs):
        """
        Performs the pPXF fit.
        
        Parameters
        ----------
        wavelength : numpy.ndarray
            Wavelength coordinates of the data.
        data : numpy.ndarray
            Input spectrum flux vector.
        mask : list
            List of masked regions, as pairs of wavelength coordinates.
        initial_velocity : float
            Initial guess for radial velocity.
        initial_sigma : float
            Initial guess for the velocity dispersion.
        fwhm_gal : float
            Full width at half maximum of a resolution element in the observed spectrum in units of pixels.
        fwhm_model : float
            The same as the above for the models.
        noise : float or numpy.ndarray
            If float it as assumed as the signal to noise ratio, and will be horizontally applied to the whole
            spectrum. If it is an array, it will be interpreted as individual noise values for each pixel.
        plot_fit : bool
            Plots the resulting fit.
        quiet : bool
            Prints information on the fit.
        deg : int
            Degree of polynomial function to be fit in addition to the stellar population spectrum.
        moments : int
            Number of moments in the Gauss-Hermite polynomial. A simple Gaussian would be 2.
        kwargs
            Additional keyword arguments passed directly to ppxf.

        Returns
        -------
        pp
            pPXF output object.

        See Also
        --------
        ppxf, ppxf_util
        """

        self.mask = mask

        fw = (wavelength >= self.fitting_window[0]) & (wavelength < self.fitting_window[1])

        lam_range1 = wavelength[fw][[0, -1]]
        gal_lin = copy.deepcopy(data[fw])

        self.obs_flux = gal_lin

        galaxy, log_lam1, velscale = ppxf_util.log_rebin(lam_range1, gal_lin)

        # Here we use the goodpixels as the fitting window
        gp = np.arange(len(log_lam1))
        lam1 = np.exp(log_lam1)
        self.obs_wavelength = lam1

        if self.mask is not None:
            if len(self.mask) == 1:
                gp = gp[(lam1 < self.mask[0][0]) | (lam1 > self.mask[0][1])]
            else:
                m = np.array([(lam1 < i[0]) | (lam1 > i[1]) for i in self.mask])
                gp = gp[np.sum(m, 0) == m.shape[0]]

        self.good_pixels = gp

        lam_range2 = self.base_wavelength[[0, -1]]
        ssp = self.base[0]

        ssp_new, log_lam2, velscale = ppxf_util.log_rebin(lam_range2, ssp, velscale=velscale)
        templates = np.empty((ssp_new.size, len(self.base)))

        fwhm_dif = np.sqrt(fwhm_gal ** 2 - fwhm_model ** 2)
        # Sigma difference in pixels
        sigma = fwhm_dif / 2.355 / self.base_delta

        for j in range(len(self.base)):
            ssp = self.base[j]
            ssp = gaussian_filter(ssp, sigma)
            ssp_new, log_lam2, velscale = ppxf_util.log_rebin(lam_range2, ssp, velscale=velscale)
            # Normalizes templates
            templates[:, j] = ssp_new / np.median(ssp_new)

        c = constants.c.value * 1.e-3
        dv = (log_lam2[0] - log_lam1[0]) * c  # km/s
        # z = np.exp(vel/c) - 1

        # Here the actual fit starts.
        start = [initial_velocity, initial_sigma]  # (km/s), starting guess for [V,sigma]

        # Assumes uniform noise accross the spectrum
        def make_noise(galaxy, noise):
            noise = galaxy * noise
            noise_mask = (~np.isfinite(noise)) | (noise <= 0.0)
            mean_noise = np.mean(noise[~noise_mask])
            noise[noise_mask] = mean_noise
            return noise
        if isinstance(noise, float):
            noise = make_noise(galaxy, noise)
        elif isinstance(noise, np.ndarray):
            noise, log_lam1, velscale = ppxf_util.log_rebin(lam_range1, copy.deepcopy(noise)[fw])
        self.noise = noise

        self.normalization_factor = np.nanmean(galaxy)
        galaxy = copy.deepcopy(ma.getdata(galaxy / self.normalization_factor))
        noise = copy.deepcopy(ma.getdata(np.abs(noise / self.normalization_factor)))

        assert np.all((noise > 0) & np.isfinite(noise)), 'Invalid values encountered in noise spectrum.'
        if len(gp.shape) > 1:
            gp = gp[0]
        pp = ppxf.ppxf(templates, galaxy, noise, velscale, start, goodpixels=gp, moments=moments, degree=deg, vsyst=dv,
                       quiet=quiet, **kwargs)

        self.solution = pp

        if plot_fit:
            self.plot_fit()

        return pp
コード例 #28
0
ファイル: wifis_ppxf.py プロジェクト: REMeyer/WIFISProc
def ppxf_wifis(spectrumfl, z_guess=0.004, sigma_guess=170.):

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

    # Read a galaxy spectrum and define the wavelength range
    #
    hdu = fits.open(spectrumfl)
    gal_lin = hdu[0].data
    wl_lin = hdu[1].data
    noise_lin = hdu[2].data

    #wh_fit = np.where((wl_lin >= 11600) & (wl_lin <= 13000))[0]
    wh_fit = np.where((wl_lin >= 11600) & (wl_lin <= 13000))[0]
    gal_lin = gal_lin[wh_fit]
    wl_lin = wl_lin[wh_fit]
    noise_lin = noise_lin[wh_fit]

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

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

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

    # Read the list of filenames from the Single Stellar Population library
    # by Vazdekis (2010, MNRAS, 404, 1639) http://miles.iac.es/. A subset
    # of the library is included for this example with permission
    #vazdekis = glob.glob('/home/elliot/mcmcgemini/spec/vcj_ssp/*t05.0*Zp0.0*.s100')
    vazdekis = glob.glob('/home/elliot/mcmcgemini/spec/vcj_ssp/*.s100')

    FWHM_tem = 1.63  # Vazdekis+10 spectra have a constant resolution FWHM of 2.51A.
    #FWHM_tem = 3  # Vazdekis+10 spectra have a constant resolution FWHM of 2.51A.
    velscale_ratio = 3  # adopts 2x higher spectral sampling for templates than for galaxy

    # 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 = np.loadtxt(vazdekis[0])
    ssp = hdu[:, 74]

    modelwl = hdu[:, 0]
    wh_wifis = np.where((modelwl >= 8000) & (modelwl <= 13500))[0]
    modelwl_wifis = modelwl[wh_wifis]
    wl_rebin = np.linspace(modelwl_wifis[0],
                           modelwl_wifis[-1],
                           num=len(modelwl_wifis))
    ssp_rebin = np.interp(wl_rebin, modelwl_wifis, ssp[wh_wifis])

    lamRange2 = [wl_rebin[0], wl_rebin[-1]]
    sspNew, logLam2, velscale_temp = util.log_rebin(lamRange2,
                                                    ssp_rebin,
                                                    velscale=velscale /
                                                    velscale_ratio)

    #h2 = hdu[0].header
    #lamRange2 = h2['CRVAL1'] + np.array([0., h2['CDELT1']*(h2['NAXIS1'] - 1)])
    fullage = np.array([1.0, 3.0, 5.0, 7.0, 9.0, 11.0, 13.5])
    #fullZ = np.array([-1.5, -1.0, -0.5, 0.0, 0.2])
    fullZ = np.array([-0.5, 0.0, 0.2])
    #templates = np.empty((sspNew.size, len(vazdekis)))
    templates = np.empty((sspNew.size, len(fullage), len(fullZ)))

    conroydict = {}
    for fl in vazdekis:
        flspl = fl.split('/')[-1]
        mnamespl = flspl.split('_')
        age = float(mnamespl[3][1:])
        Zsign = mnamespl[4][1]
        Zval = float(mnamespl[4][2:5])
        if Zsign == "m":
            Zval = -1.0 * Zval
        conroydict[(age, Zval)] = fl

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

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

    for j in range(len(fullage)):
        for k in range(len(fullZ)):
            #for j, file in enumerate(vazdekis):
            x = pd.read_csv(conroydict[(fullage[j], fullZ[k])],
                            delim_whitespace=True,
                            header=None)
            hdu = np.array(x)
            #hdu = np.loadtxt(conroydict[(fullage[j],fullZ[j])])
            ssp = hdu[:, 73]

            ssp_rebin = np.interp(wl_rebin, modelwl_wifis, ssp[wh_wifis])

            ssp = ndimage.gaussian_filter1d(ssp_rebin, sigma)
            sspNew, logLam2, velscale_temp = util.log_rebin(lamRange2,
                                                            ssp,
                                                            velscale=velscale /
                                                            velscale_ratio)
            templates[:, j,
                      k] = sspNew / np.median(sspNew)  # Normalizes templates

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

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

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

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

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

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

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

    return pp
コード例 #29
0
z = vel/c
velscale_ratio = 1

h2, lamRange2, templates = read_models(vazdekis, z)


### Cosmological Redshift Correction ###    
lamRange1 = lamRange1 / (1+z)
FWHM_gal = FWHM_gal / (1+z)

goodlam = (lam/(1+z) > 3900) & (lam/(1+z) < 6797)
lam = lam[goodlam]
lamRange1 = np.array([lam[0], lam[-1]]) / (1+z)
summedspec = summedspec[goodlam]

gal, logLam1, velscale = util.log_rebin(lamRange1, summedspec)
median = np.nanmedian(gal)
#galaxy = galaxy / median
noise = np.zeros(spec.shape[0])
for i in range(spec.shape[0]):
    noise[i] = DER_SNR(spec[i,:,:])
noise = noise[goodlam]/median

z2 = copy.copy(z)
z=0  #Bring Galaxy to Rest Frame
gal = gal/median
#galaxy = galaxy[overlaplam]
#noise = noise[overlaplam]
lam_temp = h2['CRVAL1'] + h2['CDELT1']*np.arange(h2['NAXIS1'])
    
コード例 #30
0
def create_emiles_templates(lam_range, fwhm_gal, velscale, ages, metals):
    """
    Uploads and modifies stellar template spectra from the EMILES library.
    Performs the following steps:
        1.) Cuts the spectra to the specified wavelengths
        2.) Log rebins the spectra to a specified velocity scale
        3.) Convolves the spectra to a certain instrumental resolution

    :param lam_range:
    :param ages:
    :param metals:
    :param fwhm_gal:
    :param velscale:
    :return:
    """

    # Use the EMILES SSP library as our template spectra
    file_dir = path.dirname(path.realpath(__file__))

    # Metallicities available in the EMILES library
    metals_avail = [
        -2.27, -1.79, -1.49, -1.26, -0.96, -0.66, -0.35, -0.25, 0.06, 0.15,
        0.26, 0.4
    ]

    # SSP ages available
    ages_avail = [
        0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.15, 0.20, 0.25, 0.30,
        0.35, 0.40, 0.45, 0.50, 0.6, 0.7, 0.8, 0.9, 1.0, 1.25, 1.50, 1.75, 2.0,
        2.25, 2.50, 2.75, 3.0, 3.25, 3.5, 3.75, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5,
        7.0, 7.5, 8.0, 8.5, 9.0, 9.5, 10.0, 10.5, 11.0, 11.5, 12.0, 12.5, 13.0,
        13.5, 14.0
    ]

    # Gather the specified SSP spectra
    if (ages == 'all') & (metals == 'all'):

        emiles = glob.glob(
            file_dir +
            '/emiles_models/Pa00/EMILES_PADOVA00_BASE_CH_FITS/*.fits')

    elif (ages == 'all') & np.isscalar(metals):
        if metals in metals_avail:

            if metals < 0:
                mstr = 'm' + str(metals)
            else:
                mstr = 'p' + str(metals)

            emiles = glob.glob(
                file_dir +
                '/emiles_models/Pa00/EMILES_PADOVA00_BASE_CH_FITS/*' + mstr +
                '*.fits')

        else:
            raise ValueError('Specified metallicity not'
                             'part of EMILES library.')

    elif np.isscalar(ages) & (metals == 'all'):

        if ages in ages_avail:

            astr = '{0:07.4f}'.format(ages)
            emiles = glob.glob(
                file_dir +
                '/emiles_models/Pa00/EMILES_PADOVA00_BASE_CH_FITS/*T' + astr +
                '*.fits')

        else:
            raise ValueError('Specified SSP age not' 'part of EMILES library.')

    elif ages == 'all':

        emiles = []

        for m in metals:

            if m in metals_avail:
                if m < 0:
                    mstr = 'm' + str(metals)
                else:
                    mstr = 'p' + str(metals)

                emiles += glob.glob(
                    file_dir +
                    '/emiles_models/Pa00/EMILES_PADOVA00_BASE_CH_FITS/*' +
                    mstr + '*.fits')

            else:
                raise ValueError('M/H = {0} not in EMILES library'.format(m))

    elif metals == 'all':

        emiles = []

        for a in ages:

            if a in ages_avail:
                astr = '{0:07.4f}'.format(a)
                emiles += glob.glob(
                    file_dir +
                    '/emiles_models/Pa00/EMILES_PADOVA00_BASE_CH_FITS/*T' +
                    astr + '*.fits')
            else:
                raise ValueError(
                    'SSP Age = {0} not in EMILES library'.format(a))

    elif np.isscalar(ages) & np.isscalar(metals):

        if (ages in ages_avail) & (metals in metals_avail):

            if metals < 0:
                mstr = 'm' + str(metals)
            else:
                mstr = 'p' + str(metals)

            astr = '{0:07.4f}'.format(ages)

            emiles = glob.glob(
                file_dir +
                '/emiles_models/Pa00/EMILES_PADOVA00_BASE_CH_FITS/*Z' + mstr +
                '*T' + astr + '*.fits')

        else:
            raise ValueError('Check the SSP age and/or metallicity provided!')

    elif np.isscalar(ages):

        if ages in ages_avail:

            astr = '{0:07.4f}'.format(ages)
            emiles = []

            for m in metals:

                if m in metals_avail:
                    if m < 0:
                        mstr = 'm' + str(m)
                    else:
                        mstr = 'p' + str(m)

                    emiles += glob.glob(
                        file_dir +
                        '/emiles_models/Pa00/EMILES_PADOVA00_BASE_CH_FITS/*Z' +
                        mstr + '*T' + astr + '*.fits')

                else:
                    raise ValueError(
                        'M/H = {0} not in EMILES library'.format(m))
        else:
            raise ValueError('Specified SSP age not in EMILES library.')

    elif np.isscalar(metals):

        if metals in metals_avail:

            emiles = []

            if metals < 0:
                mstr = 'm' + str(metals)
            else:
                mstr = 'p' + str(metals)

            for a in ages:

                if a in ages_avail:
                    astr = '{0:07.4f}'.format(a)
                    emiles.append(
                        file_dir +
                        '/emiles_models/Pa00/EMILES_PADOVA00_BASE_CH_FITS/Ech1.30Z'
                        + mstr + 'T' + astr + '_iTp0.00_baseFe.fits')

                else:
                    raise ValueError(
                        'SSP Age = {0} not in EMILES library'.format(a))
        else:
            raise ValueError('M/H = {0} not in EMILES library'.format(metals))

    else:

        emiles = []

        for a in ages:

            if a in ages_avail:
                astr = '{0:07.4f}'.format(a)
            else:
                raise ValueError(
                    'SSP Age = {0} not in EMILES library'.format(a))

            for m in metals:

                if m in metals_avail:
                    if m < 0:
                        mstr = 'm' + str(m)
                    else:
                        mstr = 'p' + str(m)
                else:
                    raise ValueError(
                        'M/H = {0} not in EMILES library'.format(m))

                emiles.append(
                    file_dir +
                    '/emiles_models/Pa00/EMILES_PADOVA00_BASE_CH_FITS/Ech1.30Z'
                    + mstr + 'T' + astr + '_iTp0.00_baseFe.fits')

    print('Using {0} stellar templates.'.format(len(emiles)))

    # Extract the wavelength range and logarithmically rebin one spectrum
    # to a velocity scale equal to the galaxy spectrum, to determine
    # the size needed for the array which will contain the template spectra.
    spec0 = fits.getdata(emiles[0])
    header = fits.getheader(emiles[0])
    lam_emiles = (np.arange(header['NAXIS1']) * header['CDELT1'] +
                  header['CRVAL1'])
    lam_emiles = lam_emiles / 1e4  # Convert from Angstrom to micron

    mask = (lam_emiles >= lam_range[0]) & (lam_emiles <= lam_range[1])
    spec0 = spec0[mask]
    lam_emiles = lam_emiles[mask]
    spec0_new, log_lam, velscale = ppxf_util.log_rebin(
        [lam_emiles[0], lam_emiles[-1]], spec0, velscale=velscale)
    templates = np.empty((spec0_new.size, len(emiles)))

    # Convolve each stellar template with a Gaussian to match the instrumental resolution
    # of our data. Then log rebin each one and store in "templates".
    fwhm_emiles = 2.35482 * 60.
    sigma_kms = np.sqrt(fwhm_gal**2 - fwhm_emiles**2) / 2.355
    sigma_pix = sigma_kms / ckms * lam_emiles / (lam_emiles[1] - lam_emiles[0])

    for i, f in enumerate(emiles):

        hdu = fits.open(f)
        spec = hdu[0].data
        spec_cut = spec[mask]
        spec_conv = ppxf_util.gaussian_filter1d(spec_cut, sigma_pix)
        spec_new, log_lam, velscale_temp = ppxf_util.log_rebin(
            [lam_emiles[0], lam_emiles[-1]], spec_conv, velscale=velscale)
        templates[:, i] = spec_new / np.median(spec_new)

    # Remove any templates that are just NaNs
    nan_temps = np.any(np.isnan(templates), axis=0)
    temps = templates[:, ~nan_temps]

    print('Removed {0} templates.'.format(np.sum(nan_temps)))
    print('Now using {0} templates.'.format(temps.shape[1]))

    return temps, log_lam
コード例 #31
0
def load_CvD_templates(
    FWHM_galaxy,
    lamRange1,
    velscale,
    z=0.0,
    cvd_dir=os.path.expanduser('~/z/Data/stellarpops/CvD1.2')):
    """
    FWHM_galaxy: this is the FWHM of the galaxy spectrum in Angstoms.
    lamRange1: start and stop wavelength values of the galaxy spectrum
    z: the rough redshift
    """

    import glob
    from sam_python.CvD12tools import loadCvD12spec

    cvd = glob.glob('{}/t*.ssp'.format(cvd_dir))
    cvd.sort()

    #CvD Templates are at resolution 2000, so work out lamda/R for the middle wavelength in your array
    FWHM_tem = np.median(lamRange1 * (1 + z)) / 2000

    #FIXME
    #This depends on where in the wavelength range you are. For SWIFT:
    #9300A- 3.44A
    #10120A- 4.55A
    #8700- 4.04
    #To get a more accurate value for a certain lamda, fit a gaussian to a skyline!

    #Use Simon's CvDTools function to read in the CvD models and get them into proper units
    cvd_data = loadCvD12spec(cvd[0])
    #They're returned in Ryan's spectrum class. spec.lam is wavelengths, spec.flam is flux in lamda units
    lams = cvd_data.lam
    lamRange2 = np.array([lams[0], lams[-1]])
    cdelt = lams[10] - lams[9]

    FWHM_dif = np.sqrt((FWHM_galaxy**2 - FWHM_tem**2).clip(0))
    sigma = FWHM_dif / 2.355 / cdelt  # Sigma difference in pixels
    #Log Rebin one spectrum to get the length of the templates array right
    ssp = cvd_data.flam[0]

    sspNew, logLam2, velscale = util.log_rebin(lamRange2,
                                               ssp,
                                               velscale=velscale)

    t_num = 63  #There are 63 spectra in the CvD models
    templates = np.empty((len(sspNew), t_num))

    #Do the same for all the models

    for j, filename in enumerate(cvd):
        cvd_data = loadCvD12spec(filename)
        print(filename)
        for ssp in cvd_data.flam:
            #print(ssp)
            ssp = ndimage.gaussian_filter1d(ssp, sigma)
            sspNew, logLam2, velscale = util.log_rebin(lamRange2,
                                                       ssp,
                                                       velscale=velscale)
            templates[:,
                      j] = sspNew / np.median(sspNew)  # Normalizes templates

    return templates, logLam2, lamRange2
コード例 #32
0
def fit_single_spectrum(lam_gal,
                        flux_gal,
                        templates,
                        velscale,
                        start,
                        velscale_ratio=1,
                        noise=None,
                        goodpixels=None,
                        dv=0,
                        add_poly_deg=4,
                        smooth=False,
                        smooth_sigma_pix=None,
                        clean=False,
                        plot=False):
    """
    Fit a single spectrum with PPXF
    :param lam_gal:
    :param flux_gal:
    :param templates:
    :param velscale:
    :param start:
    :param velscale_ratio:
    :param noise:
    :param goodpixels:
    :param dv:
    :param add_poly_deg:
    :return:
    """

    # Smooth the spectrum to the resolution of the EMILES templates if requested
    # The smoothing length should be provided by the user
    if smooth:
        flux_gal = ppxf_util.gaussian_filter1d(flux_gal, smooth_sigma_pix)

    # Log rebin the spectrum to the given velocity scale
    flux_rebin_gal, log_lam_gal, velscale = ppxf_util.log_rebin(
        [lam_gal[0], lam_gal[-1]], flux_gal, velscale=velscale)

    # Generate a default goodpixels vector
    if goodpixels is None:
        goodpixels = np.arange(len(flux_rebin_gal))

    # Normalize the spectrum to avoid rounding error
    norm = np.nanmedian(flux_rebin_gal)
    flux_rebin_gal /= np.nanmedian(flux_rebin_gal)

    # Generate a noise spectrum if noise is None. Use 15% of the flux
    if noise is None:
        noise = 0.15 * np.abs(flux_rebin_gal)
        noise[np.isnan(noise) | (noise == 0)] = 1.0

    # Remove NaNs and infs from goodpixels and change them to 0 in the spectrum
    nonfinite_pix = ~np.isfinite(flux_rebin_gal)
    flux_rebin_gal[nonfinite_pix] = 0.0

    for i in range(len(nonfinite_pix)):
        if nonfinite_pix[i]:
            test = np.where(goodpixels == i)[0]
            if len(test) > 0:
                goodpixels = np.delete(goodpixels, test[0])

    # Require at least 50% of the original pixels to do a fit
    if np.float(len(goodpixels)) / np.float(len(log_lam_gal)) > 0.25:

        # Run ppxf
        pp = ppxf.ppxf(templates,
                       flux_rebin_gal,
                       noise,
                       velscale,
                       start,
                       plot=False,
                       moments=2,
                       degree=add_poly_deg,
                       vsyst=dv,
                       goodpixels=goodpixels,
                       velscale_ratio=velscale_ratio,
                       clean=clean)

        vel = pp.sol[0]
        disp = pp.sol[1]
        chi2 = pp.chi2
        temp_weights = pp.weights
        gal = pp.galaxy
        bestfit = pp.bestfit
        stellar = pp.bestfit - pp.apoly
        apoly = pp.apoly
        residual = pp.galaxy - pp.bestfit

        if plot:

            pp.plot()
            fig = plt.gcf()

            return vel, disp, chi2, temp_weights, gal, bestfit, stellar, apoly, residual, norm, fig

        else:

            return vel, disp, chi2, temp_weights, gal, bestfit, stellar, apoly, residual, norm

    else:

        return 'Not enough pixels to fit.'
コード例 #33
0
def fit_cube(cube,
             lam_gal,
             fwhm_gal,
             z=0,
             velscale=None,
             noise_cube=None,
             velscale_ratio=1,
             ages='all',
             metals='all',
             lam_range_min_temp=1.4,
             lam_range_max_temp=2.5,
             dv_mask=800.,
             mask_h2_lines=True,
             mask_ionized_lines=True,
             mask_telluric=False,
             mask_broad_bry=False,
             mask_stellar_bry=False,
             mask_hband_bry=False,
             velocity_guess=0.,
             dispersion_guess=100.,
             add_poly_deg=4,
             smooth=False,
             parallel=False,
             ncores=None):
    """
    Fit an entire IFU cube
    :param cube: data cube to be fit
    :param lam_gal: wavelength array of the data cube
    :param fwhm_gal: instrumental resolution of the data in km/s
    :param z: redshift of the data
    :param velscale: optional, velocity scale to rebin to
    :param noise_cube: optional, error cube
    :param velscale_ratio: optional, velocity scale ratio to determine the velocity scale to rebin the templates
    :param ages: optional, which stellar age templates to use. Default is 'all'
    :param metals: optional, which metallicity templates to use. Default is 'all'
    :param lam_range_min_temp: optional, minimum wavelength of the template spectra. Default is 1.4 micron.
    :param lam_range_max_temp: optional, maximum wavelength of the template spectra. Default is 2.5 micron.
    :param dv_mask: optional, Width of mask to apply to emission lines in km/s. Default is 800 km/s.
    :param mask_h2_lines: optional, Whether to mask all H2 emission lines. Default is True.
    :param mask_ionized_lines: optional, Whether to mask all ionized gas lines. Default is True.
    :param mask_telluric: optional, Whether to mask regions with strong telluric residuals. Default is False.
    :param mask_broad_bry: optional, Whether to mask a broad Bry component. Default is False.
    :param mask_stellar_bry: optional, Whether to mask Bry feature from telluric star. Default is False.
    :param mask_hband_bry: optional, Whether to mask H-band Bry emission lines. Default is False.
    :param velocity_guess: optional, Initial guess for the stellar velocity. Default is 0 km/s.
    :param dispersion_guess: optional, Initial guess for the stellar velocity dispersion. Default is 100 km/s.
    :param add_poly_deg: optional, Degree of additive polynomial. Default is 4.
    :param smooth: optional, Whether to smooth the data cube to the template resolution before fitting. Default is False.
    :param parallel: optional, Whether to use parallel processing. Default is False.
    :param ncores: optional, If using parallel processing, how many CPUS to use. Default is None.
    :return: result: Very large list of the fitting results of each spaxel
             waves: The rebinned wavelength array

    Notes
    -----
    Use construct_fit_products(result, cube.shape) to reconstruct the fit products into maps and cubes.
    """

    # Grab a single spectrum from the cube to setup the wavelength scale
    test_spec = cube[:, 0, 0]
    test_spec_new, loglam_gal, velscale = ppxf_util.log_rebin(
        [lam_gal[0], lam_gal[-1]], test_spec, velscale=velscale)

    # Setup the stellar template spectra
    lam_range_temp = [lam_range_min_temp, lam_range_max_temp]
    if not smooth:
        temps, loglam_temp = create_emiles_templates(lam_range_temp, fwhm_gal,
                                                     velscale / velscale_ratio,
                                                     ages, metals)
    else:
        temps, loglam_temp = create_emiles_templates(lam_range_temp,
                                                     2.35482 * 60.,
                                                     velscale / velscale_ratio,
                                                     ages, metals)

    # Setup the parameters for the PPXF fit
    dv, gp, start = setup_fit(loglam_gal,
                              loglam_temp,
                              z=z,
                              dv_mask=dv_mask,
                              velscale_ratio=velscale_ratio,
                              mask_h2_lines=mask_h2_lines,
                              mask_ionized_lines=mask_ionized_lines,
                              mask_broad_bry=mask_broad_bry,
                              mask_stellar_bry=mask_stellar_bry,
                              mask_hband_bry=mask_hband_bry,
                              velocity_guess=velocity_guess,
                              dispersion_guess=dispersion_guess,
                              smooth=smooth,
                              mask_telluric=mask_telluric)

    # If smoothing the cube spectra, setup the smoothing lengths per pixel
    if smooth:
        fwhm_emiles = 60. * 2.35482
        sigma_kms = np.sqrt(fwhm_emiles**2 - fwhm_gal**2) / 2.35482
        sigma_pix = sigma_kms / ckms * lam_gal / (lam_gal[1] - lam_gal[0])
    else:
        sigma_pix = None

    # Fit each of the spectra in the cube using parallel processing, if requested
    nrows = cube.shape[1]
    ncolumns = cube.shape[2]
    ind = np.indices((nrows, ncolumns))
    xx = ind[0].ravel()
    yy = ind[1].ravel()
    pixels = [(xx[i], yy[i]) for i in range(len(xx))]

    if parallel:
        import multiprocessing
        if ncores is None:
            ncores = multiprocessing.cpu_count()

        pool = multiprocessing.Pool(ncores)

        if noise_cube is not None:
            result = [
                (p,
                 pool.apply_async(fit_single_spectrum,
                                  args=(lam_gal, cube[:, p[0], p[1]], temps,
                                        None, start, velscale_ratio,
                                        noise_cube[:, p[0], p[1]], gp, dv,
                                        add_poly_deg, smooth, sigma_pix)))
                for p in pixels
            ]

        else:
            result = [
                (p,
                 pool.apply_async(fit_single_spectrum,
                                  args=(lam_gal, cube[:, p[0], p[1]], temps,
                                        None, start, velscale_ratio, None, gp,
                                        dv, add_poly_deg, smooth, sigma_pix)))
                for p in pixels
            ]

        result = [(p[0], p[1].get()) for p in result]

        pool.close()

    else:

        if noise_cube is not None:
            result = [(p,
                       fit_single_spectrum(lam_gal, cube[:, p[0], p[1]], temps,
                                           None, start, velscale_ratio,
                                           noise_cube[:, p[0], p[1]], gp, dv,
                                           add_poly_deg, smooth, sigma_pix))
                      for p in pixels]

        else:
            result = [(p,
                       fit_single_spectrum(lam_gal, cube[:, p[0],
                                                         p[1]], temps, None,
                                           start, velscale_ratio, None, gp, dv,
                                           add_poly_deg, smooth, sigma_pix))
                      for p in pixels]

    return result, np.exp(loglam_gal)
コード例 #34
0
def apply_pPXF(spectra_file,
               fwhm_instrument,
               redshift,
               noise,
               vel_ratio=1,
               template_dir=None,
               fwhm_templates=None,
               templates=None,
               logLam2=None):
    """
    Function to fit galaxy spectra using pPXF.

    If provided with the template directory and resolution, this function will run the degrade_templates()
    function in order to match the resolution of the observation and the templates. Else, if the degraded
    templates are already provided, this function will just use the templates provided to fit the observed 
    galaxy spectra. 

    :param spectra_file: A string describing the path to the SimSpin spectra file.
    :param fwhm_instrument: A float describing the resolution of the observed spectra.
    :param redshift: A float describing the redshift, z, of the observed galaxy.
    :param noise: A float describing the level of noise within the observed spectra.
    :param vel_ratio: A float describing the sampling rate of the templates relative
        to the observed spectra. Default value is 1.
    
    If wishing to degrade templates to the appropriate resolution, 

    :param template_dir: A string describing the path to the directory in which the template files are 
        located. Default is None.
    :param fwhm_templates: A float describing the resolution of the template spectra. Default 
        is None.

    If you have already degraded the templates to the appropriate resolution for a previous 
    fit, you can provide these variables directly to the function to avoid recalculating the
    comupationally expensive degradation and rebinning:

    :param templates: A matrix containing the rebinned and degraded templates. Default is None.
    :param logLam2: An array describing the wavelength labels of the templates. Default is None.

    """
    hdu = fits.open(spectra_file)
    dim = hdu[0].data.shape
    mid = np.array([round(dim[1] / 2), round(dim[2] / 2)])

    gal_lin = hdu[0].data  # pulling in the spectra for each pixel
    h1 = hdu[0].header
    lamRange1 = h1['CRVAL3'] + (np.array([-h1['CRPIX3'], h1['CRPIX3']]) *
                                h1['CDELT3'])  # wavelength range
    FWHM_gal = fwhm_instrument  # SAMI has an instrumental resolution FWHM of 2.65A.

    z = redshift  # Initial estimate of the galaxy redshift
    lamRange1 = lamRange1 / (
        1 + z)  # Compute approximate restframe wavelength range
    FWHM_gal = FWHM_gal / (1 + z)  # Adjust resolution in Angstrom

    galaxy, logLam1, velscale = util.log_rebin(lamRange1, gal_lin[:, mid[0],
                                                                  mid[1]])

    if not templates or not logLam2:
        assert isinstance(template_dir, str) and isinstance(
            fwhm_templates, float
        ), 'Please provide the path to the template directory and template resolution'
        templates, logLam2 = degrade_templates(template_dir,
                                               fwhm_templates,
                                               FWHM_observation=FWHM_gal,
                                               velscale_obs=velscale,
                                               vel_ratio=vel_ratio)

    velocity = np.empty([dim[1], dim[2]])
    dispersion = np.empty([dim[1], dim[2]])

    for x in range(0, dim[1]):
        for y in range(0, dim[2]):

            if all(np.isnan(gal_lin[:, x, y])):
                velocity[x, y] = None
                dispersion[x, y] = None

            else:
                galaxy, logLam1, velscale = util.log_rebin(
                    lamRange1, gal_lin[:, x, y])
                galaxy = galaxy / np.median(
                    galaxy)  # Normalize spectrum to avoid numerical issues
                noise = np.full_like(
                    galaxy, noise)  # Assume constant noise per pixel here

                if velscale_ratio > 1:
                    dv = (np.mean(logLam2[:velscale_ratio]) -
                          logLam1[0]) * c  # km/s
                else:
                    dv = (logLam2[0] - logLam1[0]) * c  # km/s

                start = [100, 200.]  # (km/s), starting guess for [V, sigma]

                pp = ppxf.ppxf(templates,
                               galaxy,
                               noise,
                               velscale,
                               start,
                               plot=False,
                               moments=2,
                               quiet=True,
                               degree=4,
                               vsyst=dv,
                               velscale_ratio=vel_ratio)

                velocity[x, y] = pp.sol[0]
                dispersion[x, y] = pp.sol[1]

    return velocity, dispersion
コード例 #35
0
def kinematics_sdss(cube_id, y_data_var, fit_range):
    file_loc = "ppxf_results" + "/cube_" + str(int(cube_id))
    if not os.path.exists(file_loc):
        os.mkdir(file_loc)

    # reading cube_data
    cube_file = (
        "/Volumes/Jacky_Cao/University/level4/project/cubes_better/cube_" +
        str(cube_id) + ".fits")
    hdu = fits.open(cube_file)
    t = hdu[1].data

    spectra = cube_reader.spectrum_creator(cube_file)

    # using our redshift estimate from lmfit
    cube_result_file = ("cube_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("cube_results/cube_" + str(int(cube_id)) + "/cube_" +
                          str(int(cube_id)) + "_cbd_x.npy")
    if (np.sum(y_data_var) == 0):
        cube_y_data = np.load("cube_results/cube_" + str(int(cube_id)) +
                              "/cube_" + str(int(cube_id)) + "_cbs_y.npy")
    else:
        cube_y_data = y_data_var

    cube_x_original = cube_x_data
    cube_y_original = cube_y_data

    # masking the data to ignore initial 'noise' / non-features
    initial_mask = (cube_x_data > 3540 * (1 + z))
    cube_x_data = cube_x_original[initial_mask]
    cube_y_data = cube_y_original[initial_mask]

    # calculating the signal to noise
    sn_region = np.array([4000, 4080]) * (1 + z)
    sn_region_mask = ((cube_x_data > sn_region[0]) &
                      (cube_x_data < sn_region[1]))

    cube_y_sn_region = cube_y_data[sn_region_mask]
    cy_sn_mean = np.mean(cube_y_sn_region)
    cy_sn_std = np.std(cube_y_sn_region)
    cy_sn = cy_sn_mean / cy_sn_std

    #print("s/n:")
    #print(cy_sn, cy_sn_mean, cy_sn_std)

    # cube noise
    cube_noise_data = cube_noise()
    spectrum_noise = cube_noise_data['spectrum_noise']
    spec_noise = spectrum_noise[initial_mask]

    # will need this for when we are considering specific ranges
    if (isinstance(fit_range, str)):
        pass
    else:
        rtc = fit_range * (1 + z
                           )  # create a new mask and mask our x and y data
        rtc_mask = ((cube_x_data > rtc[0]) & (cube_x_data < rtc[1]))

        cube_x_data = cube_x_data[rtc_mask]
        cube_y_data = cube_y_data[rtc_mask]

        spec_noise = spec_noise[rtc_mask]

    lamRange = np.array([np.min(cube_x_data), np.max(cube_x_data)])
    specNew, logLam, velscale = log_rebin(lamRange, cube_y_data)
    lam = np.exp(logLam)

    loglam = np.log10(lam)
    # Only use the wavelength range in common between galaxy and stellar library.
    mask = (loglam > np.log10(3540)) & (loglam < np.log10(9464))
    flux = specNew[mask]

    galaxy = flux / np.median(
        flux)  # Normalize spectrum to avoid numerical issues
    loglam_gal = loglam[mask]
    lam_gal = 10**loglam_gal

    # galaxy spectrum not scaled
    galaxy_ns = flux

    segmentation_data = hdu[2].data
    seg_loc_rows, seg_loc_cols = np.where(segmentation_data == cube_id)
    signal_pixels = len(seg_loc_rows)

    spec_noise = spec_noise[mask]

    noise = (spec_noise * np.sqrt(signal_pixels)) / np.median(flux)

    # sky noise
    sky_noise = cube_reader.sky_noise("data/skyvariance_csub.fits")
    skyNew, skyLogLam, skyVelScale = log_rebin(lamRange,
                                               sky_noise[initial_mask])
    skyNew = skyNew[mask]

    c = 299792.458  # speed of light in km/s
    frac = lam_gal[1] / lam_gal[0]  # Constant lambda fraction per pixel
    dlam_gal = (frac - 1) * lam_gal  # Size of every pixel in Angstrom

    data_shape = np.shape(galaxy)
    wdisp = np.full(data_shape, 1,
                    dtype=float)  # Intrinsic dispersion of every pixel

    fwhm_gal = 2.51 * wdisp * dlam_gal  # Resolution FWHM of every pixel, in Angstroms
    velscale = np.log(frac) * c  # Constant velocity scale in km/s per pixel

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

    # Read the list of filenames from the Single Stellar Population library
    # by Vazdekis (2010, MNRAS, 404, 1639) http://miles.iac.es/. A subset
    # of the library is included for this example with permission
    #template_set = glob.glob('miles_models/Mun1.30Z*.fits')
    #template_set = glob.glob('jacoby_models/jhc0*.fits')
    #fwhm_tem = 4.5 # instrumental resolution in Ångstroms.

    # NOAO Coudé templates
    template_set = glob.glob("noao_templates/*.fits")
    fwhm_tem = 1.35

    # Extract the wavelength range and logarithmically rebin one spectrum
    # to the same velocity scale of the SDSS galaxy spectrum, to determine
    # the size needed for the array which will contain the template spectra.
    #
    #hdu = fits.open(template_set[0])
    #ssp = hdu[0].data
    #h2 = hdu[0].header
    #lam_temp = h2['CRVAL1'] + h2['CDELT1']*np.arange(h2['NAXIS1'])

    hdu = fits.open(template_set[0])
    noao_data = hdu[1].data[0]
    ssp = noao_data[1]
    lam_temp = noao_data[0]

    lamRange_temp = [np.min(lam_temp), np.max(lam_temp)]
    sspNew = util.log_rebin(lamRange_temp, ssp, velscale=velscale)[0]
    templates = np.empty((sspNew.size, len(template_set)))

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

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

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

    spacing = lam_temp[1] - lam_temp[0]
    sigma = fwhm_dif / 2.355 / spacing  # Sigma difference in pixels

    for j, fname in enumerate(template_set):
        hdu = fits.open(fname)
        #ssp = hdu[0].data

        noao_data = hdu[1].data[0]
        ssp = noao_data[1]

        ssp = util.gaussian_filter1d(
            ssp, sigma)  # perform convolution with variable sigma
        sspNew = util.log_rebin(lamRange_temp, ssp, velscale=velscale)[0]
        templates[:, j] = sspNew / np.median(sspNew)  # Normalizes templates

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

    #lam_gal_alt = lam_gal * (1+z)
    #lamRange_temp = [np.min(lam_temp), np.max(lam_temp)]
    goodpixels = util.determine_goodpixels(np.log(lam_gal), lamRange_temp, z)

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

    f = io.StringIO()
    with redirect_stdout(f):
        pp = ppxf(templates,
                  galaxy,
                  noise,
                  velscale,
                  start,
                  sky=skyNew,
                  goodpixels=goodpixels,
                  plot=True,
                  moments=4,
                  degree=12,
                  vsyst=dv,
                  clean=False,
                  lam=lam_gal)

    ppxf_variables = pp.sol
    ppxf_errors = pp.error

    red_chi2 = pp.chi2
    best_fit = pp.bestfit

    x_data = cube_x_data[mask]
    y_data = cube_y_data[mask]

    print(ppxf_variables)
    #plt.show()

    if ((np.sum(y_data_var) == 0) and isinstance(fit_range, str)):
        np.save(file_loc + "/cube_" + str(int(cube_id)) + "_lamgal", lam_gal)
        np.save(file_loc + "/cube_" + str(int(cube_id)) + "_flux", flux)

        np.save(file_loc + "/cube_" + str(int(cube_id)) + "_x", x_data)
        np.save(file_loc + "/cube_" + str(int(cube_id)) + "_y", y_data)

        np.save(file_loc + "/cube_" + str(int(cube_id)) + "_noise", noise)

        # if best fit i.e. perturbation is 0, save everything

        kinematics_file = open(
            file_loc + "/cube_" + str(int(cube_id)) + "_kinematics.txt", 'w')

        np.save(file_loc + "/cube_" + str(int(cube_id)) + "_model", best_fit)

        print("Rough reduced chi-squared from ppxf: " + str(pp.chi2))

        data_to_file = f.getvalue()

        kinematics_file.write(data_to_file)
        kinematics_file.write("")

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

        kinematics_file.write('Elapsed time in PPXF: %.2f s' %
                              (process_time() - t) + "\n")

        plt.tight_layout()
        graph_loc = "ppxf_results" + "/cube_" + str(int(cube_id))
        if not os.path.exists(graph_loc):
            os.mkdir(graph_loc)

        kinematics_graph = (graph_loc + "/cube_" + str(int(cube_id)) +
                            "_kinematics.pdf")
        plt.savefig(kinematics_graph)
        #plt.show()
        plt.close("all")
    if not isinstance(fit_range, str):
        # saving graphs if not original range
        fit_range = fit_range * (1 + z)
        fitting_plotter(cube_id, fit_range, x_data, y_data, best_fit, noise)

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

    return {
        'reduced_chi2': red_chi2,
        'noise': noise,
        'variables': ppxf_variables,
        'y_data': galaxy,
        'x_data': lam_gal,
        'redshift': z,
        'y_data_original': cube_y_original,
        'non_scaled_y': galaxy_ns,
        'model_data': best_fit,
        'noise_original': spec_noise,
        'errors': ppxf_errors
    }
コード例 #36
0
def ppxf_example_kinematics_sauron():

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

    # Read a galaxy spectrum and define the wavelength range
    #

    cube_id = 468
    cube_file = "data/cubes/cube_" + str(cube_id) + ".fits"
    hdu = fits.open(cube_file)
    gal_lin = hdu[0].data
    h1 = hdu[0].header

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

    mask = (cube_x_data > 5000) & (cube_x_data < 6000)
    cube_y_data = cube_y_data[mask]

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

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

    galaxy, logLam1, velscale = util.log_rebin(lamRange1, cube_y_data)

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

    print(galaxy)
    # 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(ppxf_dir + '/miles_models/Mun1.30Z*.fits')
    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

    # 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, file in enumerate(vazdekis):
        hdu = fits.open(file)
        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
    if velscale_ratio > 1:
        dv = (np.mean(logLam2[:velscale_ratio]) - logLam1[0]) * c  # km/s
    else:
        dv = (logLam2[0] - logLam1[0]) * c  # km/s

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

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

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

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

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