示例#1
0
def _load_single_eMILES_spec(filename, basedir='/mnt/sda11/Miles/models'):
    """Load an e-Miles file and return a spectrum class, with attributes like Z, age, etc accessible

    Inputs:
    -- A filename. Can be full path or just the file. 

    Outputs:
    -- A spectrum object
    """

    model_name = os.path.basename(filename)

    userdict = {}

    if filename.split('.')[-1] == 'fits':
        hdu = fits.open('{}'.format(filename))
        header = hdu[0].header
        data = hdu[0].data

        lamdas = header['CRVAL1'] + (np.arange(header['NAXIS1'], dtype=float) +
                                     1.0 - header['CRPIX1']) * header['CDELT1']
        #lamdas=np.array([(x+1-header['CRPIX1'])*header['CDELT1']+header['CRVAL1'] for x in xrange(len(data))])

    else:
        lamdas, data = np.genfromtxt('{}'.format(filename),
                                     unpack=True,
                                     skip_header=61)

    if 'bi' in model_name:
        userdict['IMF_type'] = 'bimodal'
        IMF_pos = model_name.index("bi") + 2

        userdict['IMF'] = model_name[IMF_pos:IMF_pos + 4]

    if 'NaFe' in model_name:
        nafe_pos = model_name.index('NaFe') + 4
        userdict['NaFe'] = model_name[nafe_pos:nafe_pos + 3]
    else:
        userdict['NaFe'] = 0.0

    z_pos = model_name.index('Z') + 1
    userdict['Z'] = model_name[z_pos:z_pos + 5]

    age_pos = model_name.index('T') + 1
    userdict['age'] = model_name[age_pos:age_pos + 6]

    spec = s.spectrum(lamdas, data, userdict=userdict)
    return spec
示例#2
0
def load_eMILES_spectra(basedir='/Data/stellarpops/Miles',
                        NaFe=0.0,
                        Zs=None,
                        verbose=True,
                        imf_type='bi'):
    """
    Load all the eMILES spectra and save into a dictionary. The dictionary keys are IMF values (e.g. 'bi1.30'), and the values are spectrum classes
    of spectra with that IMF and a variety of ages. So to get the flams of all the spectra with a Chabrier IMF, you'd do

    spectra['bi1.30'].flam

    """

    import os
    basedir = os.path.expanduser(basedir)
    if NaFe == 0.0:
        if imf_type == 'bi':
            folder = 'bi_base_set'
        elif imf_type == 'uni':
            folder = 'uni_base_set'
    elif NaFe == 0.3:
        folder = 'NaFep03/{}'.format(imf_type)
    elif NaFe == 0.6:
        folder = 'NaFep06/{}'.format(imf_type)
    elif NaFe == 0.9:
        folder = 'NaFep09/{}'.format(imf_type)
    elif NaFe == 1.2:
        folder = 'NaFep12/{}'.format(imf_type)
    else:
        raise NameError('NaFe abundance not understood')

    #Solar metallicity

    if Zs == None:
        #Full e-Miles models have 7 metallicities
        #Na enhanced ones only have 3
        if NaFe == 0.0:
            Zs = [
                'm2.32', 'm1.71', 'm1.31', 'm0.71', 'm0.40', 'p0.00', 'p0.22'
            ]
        else:
            Zs = ['m0.40', 'p0.00', 'p0.22']

    if imf_type == 'bi':
        IMFs = [
            'bi0.30', 'bi0.80', 'bi1.00', 'bi1.30', 'bi1.50', 'bi1.80',
            'bi2.00', 'bi2.30', 'bi2.50', 'bi2.80', 'bi3.00', 'bi3.30'
        ]
    else:
        IMFs = [
            'un0.30', 'un0.80', 'un1.00', 'un1.30', 'un1.50', 'un1.80',
            'un2.00', 'un2.30', 'un2.50', 'un2.80', 'un3.00', 'un3.30'
        ]

    #Empty dictionary to fill
    specs = {}

    #Work out the lamda array and get the length of the spectrum
    hdu = fits.open('{}'.format(
        "{}/uni_base_set/E{}Z{}T17.7828_iPp0.00_baseFe.fits".format(
            basedir, 'un0.30', 'p0.00')))
    header = hdu[0].header
    data = hdu[0].data
    lamdas = header['CRVAL1'] + (np.arange(header['NAXIS1'], dtype=float) +
                                 1.0 - header['CRPIX1']) * header['CDELT1']

    if verbose:
        print "Loading eMILES spectra from {}/{}".format(basedir, folder)
    for IMF in IMFs:
        #Empty array to store the flams
        #Get the number of ages by globbing for the first metallicity
        #import pdb; pdb.set_trace()
        tmp = glob.glob("{}/{}/E{}Z{}T??.????_iPp0.00_baseFe*".format(
            basedir, folder, IMF, Zs[-1]))

        #assert len(tmp)==12, 'Something may be wrong- expecting 12 spectra in with ages > 5 gyr, got {}'.format(len(tmp))
        flams = np.empty([len(Zs), len(tmp), len(data)])
        for i, Z in enumerate(Zs):

            #Get all the files with Z=Z and IMF=IMF
            files = glob.glob("{}/{}/E{}Z{}T??.????_iPp0.00_baseFe*".format(
                basedir, folder, IMF, Z))

            #Sort so the ages are in order
            files.sort(key=lambda x: float(x.split('T')[-1][:7]))

            #Empty arrays to store all the ages
            ages = np.empty(len(files))

            for j, file in enumerate(files):

                #Base MILES are fits files, [Na/Fe] enhanced are ascii
                if file.split('.')[-1] == 'fits':
                    hdu = fits.open('{}'.format(file))
                    data = hdu[0].data
                    hdu.close()

                else:
                    lamdas, data = pandas_read_file('{}'.format(file))

                #import pdb; pdb.set_trace()
                age_pos = file.index('T') + 1
                ages[j] = float(file[age_pos:age_pos + 6])

                flams[i, j, :] = data

        ages = np.repeat(ages, len(Zs)).reshape(len(files), len(Zs)).T
        Z_values = np.repeat(Zs, len(files)).reshape(len(Zs), len(files))

        userdict = {}
        userdict['NaFe'] = NaFe

        sp = s.spectrum(lamspec=flams,
                        lam=lamdas,
                        age=ages,
                        Z=Z_values,
                        IMF=IMF,
                        model='eMILES',
                        wavesyst="air",
                        userdict=userdict)
        specs[IMF] = sp
        if verbose:
            print "Loaded {} SSPs with {} IMF".format(
                len(files) * len(Zs), IMF)

    return specs
示例#3
0
def convolve_eMILES_spectra(spec, sigma, verbose=False):
    """Convolve an e-MILES spectrum up to a given sigma. This is made slightly tricky by the fact that the e-MILES spectra are at a fixed FWHM of lambda=2.5A
    below 8950A, but at sigma=60 km/s above that. We split the spectra at 8950A, convolve both appropriately, then join back together.

    Inputs:
    -- spec: an e-MILES spectrum object
    -- sigma: a velocity dispersion to convolve to
    -- verbose: a boolean

    Outputs:
    -- an e-Miles spectrum object

    Info:
    -- Wavelength should be in Angstroms


    ###### TO DO ######

    Sort out what happens at 8950 angstroms- at the moment, the two convolutions do funny things around the join
    """

    #Do this to keep the userdict of the spectrum, instead of making a new spectrum and losing that dictionary
    import copy
    final_spec = copy.deepcopy(spec)

    lamdas = np.array(spec.lam)
    data = np.array(spec.flam)

    assert (lamdas[0] < 8950.0) & (
        lamdas[-1] > 8950.0
    ), "Are the wavelengths in Angstroms? Looks like the wavelength array doesn't contain 8950 A"

    lamda_mask = lamdas < 8950.0

    final_flams = []

    low_lam_spec = s.spectrum(lamdas[lamda_mask], data[lamda_mask])
    high_lam_spec = s.spectrum(lamdas[~lamda_mask], data[~lamda_mask])

    #e-MILES spectral res above 8950A is 60km/s
    high_lam_specsig = 60.0
    assert sigma > high_lam_specsig, "Sigma must be greater than the spectral resolution of the models"
    convsig = np.sqrt(sigma**2 - high_lam_specsig**2)

    high_lam_spec.gaussVelConvolve(0.0, convsig, verbose=verbose)
    # loglams=high_lam_spec.loglam
    # interp=si.interp1d(loglams, high_lam_spec.conflam[0], fill_value='extrapolate')
    # linlams=high_lam_spec.lam
    # high_lam_flam=interp(linlams)

    #import ipdb; ipdb.set_trace()

    #high_lam_spec.interp_log_to_lin(verbose=verbose)

    #e-MILES spectral res below 8950A is 2.5A

    #For now, we only care about the spectra around NaI 8190
    #At this wavelength, with a FWHM of 2.5A, delta v is 39km/s

    low_lam_specsig = 39.0
    assert sigma > low_lam_specsig, "Sigma must be greater than the spectral resolution of the models"
    convsig = np.sqrt(sigma**2 - high_lam_specsig**2)

    low_lam_spec.gaussVelConvolve(0.0, convsig, verbose=verbose)
    # loglams=low_lam_spec.loglam
    # interp=si.interp1d(loglams, low_lam_spec.conflam[0], fill_value='extrapolate')
    # linlams=low_lam_spec.lam
    # low_lam_flam=interp(linlams)
    #low_lam_spec.interp_log_to_lin(verbose=verbose)

    final_flams.append(np.concatenate([low_lam_flam, high_lam_flam]))

    final_spec.flam = np.array(final_flams)
    setattr(final_spec, 'sigma', sigma)

    return final_spec
示例#4
0
def element_enhanced_spec(specs,
                          index,
                          out_sigma,
                          element,
                          enhancement=0.3,
                          cvd_dir='/Data/stellarpops/CvD1.2',
                          verbose=True):

    import CvD12tools as cvd

    if element == 'Na':
        element_index = 1

    cvd13 = cvd.loadCvD12spec('{}/t13.5_solar.ssp'.format(cvd_dir))
    cvdvar = cvd.loadCvD12spec('{}/t13.5_varelem.ssp'.format(cvd_dir))

    CvDlam = cvd13.lam
    fefac = (cvdvar.flam[element_index] / cvd13.flam[3] - 1) * (
        (10**(enhancement) - 1.0) / (10**(0.3) - 1.0))

    fefac = s.vac2air(fefac)

    if verbose:
        print "Created fefac: shape is {}".format(fefac.shape)

    #CvD spectra have a spectral resolution of ~2000
    #This is a sigma of ~63km/s

    #Miles models have a FWHM of 2.5A below 8950A, 60km/s above it.
    #60km/s is a resolving power of 2121: R=c/sigma*sqrt(8ln2)=2121
    #At NaI, FWHM of 2.5A is a sigma of 38.9km/s, or R=3276

    #Need to discuss- but will not convolve either model above 8950A for the moment
    #Below, I'll convolve MILES up to a sigma of 63 km/s
    #should test whether this makes a difference!

    if index['red_stop'] < 8950.0:

        if verbose:
            print "Index is below 8950A. Need to convolve MILES up to CvD resolution"

        #Convolve the Miles models up to the CvD resolution
        CvD_sigma = 63.0
        if index['nfeat'] > 0.0:
            model_sigma = const.c * 2.5 / (np.sqrt(8. * np.log(2.0)) *
                                           index['ind_start'][0] * 1000.0)
        else:
            model_sigma = const.c * 2.5 / (np.sqrt(8. * np.log(2.0)) *
                                           index['cont_stop'][0] * 1000.0)

        assert CvD_sigma > model_sigma, 'Cant convolve to a resolution below the model resolution'
        conv_sigma = np.sqrt(CvD_sigma**2 - model_sigma**2)

        MILES_at_CvD_res = s.cutAndGaussVelConvolve(specs,
                                                    index,
                                                    conv_sigma,
                                                    n_sig=30.0)

        if verbose:

            print "Made MILES spec at CvD resolution. Shape is {}".format(
                MILES_at_CvD_res.flam.shape)

    else:
        MILES_at_CvD_res = specs.copy()

    #Cut Miles specs to finish at the same lamda as the CvD specs
    MILES_at_CvD_res.clipSpectralRange(MILES_at_CvD_res.lam[0], CvDlam[-1])

    if verbose:
        print "Clipped the MILES spectra to end at 2.4um. Shape is {}".format(
            MILES_at_CvD_res.flam.shape)

    #Clip CvDspecs to start and stop at the same lamda as the (clipped or not clipped) MILES ones
    lamda_mask = np.where((CvDlam > MILES_at_CvD_res.lam[0])
                          & (CvDlam < MILES_at_CvD_res.lam[-1]))[0]
    fefac = fefac[lamda_mask]

    #interpolate MILES and CvD onto the same wavelength grid
    fefac_interp = si.interp1d(CvDlam[lamda_mask],
                               fefac,
                               fill_value='extrapolate')
    correction = fefac_interp(MILES_at_CvD_res.lam)

    if verbose:
        print "Interpolated Miles and CvD to lie on the same wavelength array"

    newspec = s.spectrum(
        lam=MILES_at_CvD_res.lam,
        lamspec=np.exp(np.log(MILES_at_CvD_res.flam) + correction),
        wavesyst='air')
    if verbose:
        print "Made new spectrum"
    # if index['name']=='TiO89' and specs.IMF=='bi1.30':
    #     import matplotlib.pyplot as plt
    #     plt.clf()
    #     plt.figure()
    #     import pdb; pdb.set_trace()
    """
    for i, flam in enumerate(MILES_at_CvD_res.flam.reshape(-1, MILES_at_CvD_res.flam.shape[-1])):

        assert len(flam)==len(correction), "Lengths of arrays aren't equal!"
        # import pdb; pdb.set_trace()
        # import matplotlib.pyplot as plt
        # plt.figure()
        # plt.plot(MILES_at_CvD_res.lam, flam, c='k')
        flam=np.exp(np.log(flam)+correction)
        # plt.plot(MILES_at_CvD_res.lam, flam, c='r')
        # plt.show()
        # import pdb; pdb.set_trace()


        if verbose:
            print "Applied Correction to spec {} of {}".format(i, MILES_at_CvD_res.flam.reshape(-1, MILES_at_CvD_res.flam.shape[-1]).shape[0])
    """

    return newspec
示例#5
0
def loadM05spec(fname, massfile=None, agecol=0, zcol=1, lamcol=2, fluxcol=3, \
                angstscale=1.0, fluxscale=1.0, skip=0, minAge=None, maxAge=None, \
                Zdict={"10m4":0.00025, "0001":0.001, "001":0.01, \
                "002":0.02, "004":0.04, "007":0.07}, \
                resolution=[None,{'vis':(5,10), 'nir':(20,100)}], ):
    """
    Author: Ryan Houghton (14/4/11)

    Purpose: To load in a stellar pop spectrum from M05, keeping
       - Age (Gyr)
       - Metallicity Z
       - Wavelength (AA)
       - Flux density (erg/s/AA/cm^2)

    Inputs:
       fname - the filename to load data from
       Zdict - a dictionary to work out model metallicity from filename
    """

    # get metallicity
    Z = Zdict[fname.split(".")[1].split("z")[-1]]

    # read in the raw data
    alldata = np.real(np.loadtxt(fname, usecols=[agecol, zcol, lamcol, fluxcol], \
                    dtype="D", unpack=True)) # dtype="F" isn't good enough

    # get the array size and spec size
    nx, ny = alldata.shape

    loc = (np.where(alldata[0, :] == alldata[0, 0]))[0]
    nlam = len(loc)

    # check if file has whole number of spectra
    assert (float(ny) / float(nlam) % 1) == 0.0, "NLINE != N*NLAM ?!"

    nspec = ny / nlam

    # init
    specs = np.zeros((nspec, nlam))
    ages = np.zeros(nspec)
    lam = np.zeros(nlam)

    specs[0, :] = alldata[3, loc]
    lam[:] = alldata[2, loc]
    ages[0] = alldata[0, loc[0]]  # in Gyrs

    # sort the other data
    for ix in range(1, nspec):
        loc = (np.where(alldata[0, :] == alldata[0, ix * nlam]))[0]
        specs[ix, :] = alldata[3, loc]
        ages[ix] = alldata[0, loc[0]]

    # convert: erg/s/AA => * 1.0 /(4.*pi*D[cm]**2) => erg/s/cm**2/AA (@10pc)
    factor = (1.0 / (10.0 * t.pc * 100)**2.0) / (4.0 * np.pi)
    specs = specs * factor

    # load the stellar masses
    if massfile != None:
        massdata = np.real(np.loadtxt(massfile, unpack=True, dtype="D"))
        zloc = np.where(massdata[0] == alldata[1][0])[0]
        mass = massdata[2, zloc]
    else:
        mass = None

    # cut ages if given
    if (minAge is not None) & (maxAge is not None):
        aloc = np.where((ages >= minAge) & (ages <= maxAge))[0]
    elif (minAge is not None) & (maxAge is None):
        aloc = np.where(ages >= minAge)[0]
    elif (minAge is None) & (maxAge is not None):
        aloc = np.where(ages <= maxAge)[0]
    else:
        aloc = np.arange(len(ages))
    ages = ages[aloc]
    specs = specs[aloc, :]

    spectra = t.spectrum(lam=lam, lamspec=specs, age=ages, Z=Z, mass=mass, model="M05", \
                         resolution=resolution, wavesyst="air")

    return spectra