def emission_templates(velscale): """ Load files with stellar library used as templates. """ current_dir = os.getcwd() # Template directory is also set in setyp.py os.chdir(template_dir) emission = [x for x in os.listdir(".") if x.startswith("emission") and x.endswith(".fits")] emission.sort() c = 299792.458 FWHM_tem = 2.1 # 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 os.chdir(current_dir) return templates, logLam2, h2["CDELT1"], emission
def emission_templates(velscale): """ Load files with stellar library used as templates. """ current_dir = os.getcwd() # Template directory is also set in setup.py os.chdir(templates_dir) emission = [x for x in os.listdir(".") if x.startswith("emission") and x.endswith(".fits") and x not in ["emission_FWHM_2.7.fits", "emission_FWHM_3.7.fits"]] emission.sort() c = 299792.458 FWHM_tem = 2.5 # 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.,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)): ssp = pf.getdata(emission[j]) w = wavelength_array(emission[j]) dsigma = np.sqrt((3.7**2 - 2.7**2))/2.335/(w[1]-w[0]) ssp = gaussian_filter1d(ssp, dsigma) sspNew, logLam2, velscale = util.log_rebin(lamRange2, ssp, velscale=velscale) templates[:,j] = sspNew templates *= 1e5 # Normalize templates os.chdir(current_dir) return templates, logLam2, h2['CDELT1'], emission
def emission_templates(velscale): """ Load files with stellar library used as templates. """ current_dir = os.getcwd() # Template directory is also set in setyp.py os.chdir(template_dir) emission = [x for x in os.listdir(".") if x.startswith("emission") and x.endswith(".fits")] emission.sort() c = 299792.458 FWHM_tem = 2.1 # 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.,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 os.chdir(current_dir) return templates, logLam2, h2['CDELT1'], emission
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")] print miles raw_input() hdu = pf.open(miles[0]) ssp = hdu[0].data h2 = hdu[0].header lamRange2 = h2['CRVAL1'] + np.array([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 = "Mun1.30Z{0}T{1}.fits".format(Zs[k], Ts[j]) 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']
def setup_bc03_library(wave, velscale=None, FWHM_gal=1, version='N', in_log=True): FWHM_tem = 1 # this number is crap as the spectra are synthetic in part... lam, full, full_data, logAge_grid, metal_grid = load_bc03_library( version=version) templates = np.zeros_like(full) new_ssp = np.interp(wave, lam, ssp, right=0, left=0) lamRange_temp = [wave[0], wave[-1]] delta = np.diff(lam)[0] if in_log: sspNew, logLam2, velscale = util.log_rebin(lamRange_temp, ssp, velscale=0.99999 * velscale) else: sspNew = ssp logLam2 = np.log(lam) # templates = np.empty((sspNew.size, nAges, nMetal)) FWHM_dif = np.sqrt(FWHM_gal**2 - FWHM_tem**2) sigma = FWHM_dif / 2.355 / delta # Sigma difference in pixels for i, d in enumerate(data): tmp = os.path.join(os.path.dirname(basefile), 'BasesDir', d['specfile']) lam, ssp = np.loadtxt(tmp, unpack=True) full[:, i % nAges, int(i / nAges)] = ssp new_lam = wave #new_lam = np.arange(new_lam[0], new_lam[-1],1) new_ssp = np.interp(new_lam, lam, ssp, right=0, left=0) # new_lam = np.arange(lam[0], lam[-1],1) # new_ssp = np.interp(new_lam,lam, ssp, right=0, left=0) # mask = (new_lam > wave.min()-1) & (new_lam < wave.max()+2) lam = new_lam #[mask] ssp = new_ssp #[mask] ssp = ndimage.gaussian_filter1d(ssp, sigma) if in_log: sspNew, logLam2, velscale = util.log_rebin(lamRange_temp, ssp, velscale=velscale) else: sspNew = ssp templates[:, i % nAges, int(i / nAges)] = sspNew logAge_grid[i % nAges, int(i / nAges)] = np.log10(d['age'] / 1.e9) metal_grid[i % nAges, int(i / nAges)] = np.log10(d['Z']) return templates, lamRange_temp, logAge_grid, metal_grid #, lam_full, full
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")] print miles raw_input() 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 = "Mun1.30Z{0}T{1}.fits".format(Zs[k], Ts[j]) 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"]
def load_sky(filenames, velscale, full_output=False): """ Load and rebin sky files. """ skydata = fits_to_matrix(filenames) h1 = pf.getheader(filenames[0]) lamRange1 = h1['CRVAL1'] + np.array([0.,h1['CDELT1']*(h1['NAXIS1']-1)]) sky1, logLam1, velscale = util.log_rebin(lamRange1, skydata[:,0], velscale=velscale) skylog = np.zeros((sky1.shape[0], len(filenames))) for i in range(len(filenames)): skylog[:,i], logLam1, velscale = util.log_rebin(lamRange1, skydata[:,i], velscale=velscale) if full_output: return skylog, logLam1 return skylog
def smooth_and_rebin(bc03, bc03_lambda, gal_lambda, velscale, FWHM_gal=1, FWHM_tem=0): """ Smooth and rebin the BC03 spectra based on the sampling of the galaxy to be fitted. Input : - bc03 : the spectral templates - bc03_lambda : the template wavelength array - gal_lambda : the galaxy wavelength array, which the rebinning is going to match. Note tha the galaxy must have already been log-rebinned. - velscale : velocity scale of the galaxy, for log rebinning - FWHM_gal : FWHM of the galaxy resolution - FWHM_tem : FWHM of the template resolution Output : - templates : the smoothed and rebinned version of the BC03 input """ templates = np.empty(((gal_lambda.size,)+(bc03.shape[1],bc03.shape[2]))) for i in range(bc03.shape[1]): for j in range(bc03.shape[2]): ssp = bc03[:,i,j] #resize the templates to the log-rebinned galaxy wavelengths new_ssp = np.interp(gal_lambda, bc03_lambda, ssp, right=0, left=0) #log-rebin the interpolated templates #the output velscape should be close to identical to the input #Likewise, logLam2 == np.log(gal_lambda) to about 1e-5 sspNew, logLam2, velscale = util.log_rebin([gal_lambda[0],gal_lambda[-1]], new_ssp, velscale=velscale) #smooth the log-rebinned templates FWHM_dif = np.sqrt(FWHM_gal**2 - FWHM_tem**2) sigma = FWHM_dif/2.355/np.diff(gal_lambda)[0] smoothed_ssp = ndimage.gaussian_filter1d(new_ssp, sigma) templates[:, i, j] = smoothed_ssp return templates
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("Mun") 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() miles = [] metal_ages = [] for m in Zs: for t in Ts: filename = "Mun1.30Z{0}T{1}_linear_FWHM_2.7.fits".format(m, t) if os.path.exists(filename): miles.append(filename) metal_ages.append([m.replace("_", ".").replace("p", "+").replace("m", "-"),t.replace("_", ".")]) hdu = pf.open(miles[0]) ssp = hdu[0].data h2 = hdu[0].header lamRange2 = h2['CRVAL1'] + np.array([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)): ssp = pf.getdata(miles[j]) w = wavelength_array(miles[j]) dsigma = np.sqrt((3.7**2 - 2.7**2))/2.335/(w[1]-w[0]) ssp = gaussian_filter1d(ssp, dsigma) sspNew, logLam2, velscale = util.log_rebin(lamRange2, ssp, velscale=velscale) templates[:,j] = sspNew os.chdir(current_dir) return templates, logLam2, h2['CDELT1'], miles
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(template_dir) miles = [x for x in os.listdir(".") if x.startswith("Mun") 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]) Zs = [str(x).replace(".", "_") for x in Zs] # Ordered array of ages Ts = list(set([x.split("T")[1].split(".fits")[0].replace("_", ".") for x in miles])) Ts.sort() Ts = [str(x).replace(".", "_") for x in Ts] miles = [] metal_ages = [] for m in Zs: for t in Ts: filename = "Mun1_30Z{0}T{1}.fits".format(m, t) if os.path.exists(filename): miles.append(filename) metal_ages.append([m.replace("_", ".").replace("p", "+").replace("m", "-"),t.replace("_", ".")]) hdu = pf.open(miles[0]) ssp = hdu[0].data h2 = hdu[0].header lamRange2 = h2['CRVAL1'] + np.array([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 os.chdir(current_dir) return templates, logLam2, h2['CDELT1'], miles
def build_templates(lam, ssp, wave, velscale, sigma): new_ssp = np.interp(wave, lam, ssp, right=0, left=0) lam=wave ssp=new_ssp ssp = ndimage.gaussian_filter1d(new_ssp,sigma) sspNew, logLam2, velscale = util.log_rebin([wave[0],wave[-1]], ssp, velscale=velscale) return sspNew
def __init__(self, spec, velscale, pklfile=None): """ Load the pkl file from previous ppxf fit and define some atributes. """ if pklfile == None: pklfile = spec.replace(".fits", ".pkl") with open(pklfile) as f: pp = pickle.load(f) self.__dict__ = pp.__dict__.copy() self.spec = spec if not os.path.exists(os.path.join(os.getcwd(), spec)): self.spec = os.path.join(data_dir, spec) self.velscale = velscale self.w = wavelength_array(os.path.join(data_dir, spec)) self.flux = pf.getdata(self.spec) self.flux_log, self.logw, velscale = util.log_rebin( [self.w[0], self.w[-1]], self.flux, velscale=velscale) self.w_log = np.exp(self.logw) self.header = pf.getheader(os.path.join(data_dir, spec)) self.lam = self.header['CRVAL1'] + np.array( [0., self.header['CDELT1'] * (self.header['NAXIS1'] - 1)]) ###################################################################### # # Read templates star_templates, self.logLam2, self.delta, miles = stellar_templates( velscale) ###################################################################### # Convolve our spectra to match MILES resolution FWHM_tem = 2.51 FWHM_spec = 2.1 FWHM_dif = np.sqrt(FWHM_tem**2 - FWHM_spec**2) sigma = FWHM_dif / 2.355 / self.delta # Sigma difference in pixels ###################################################################### spec_lin = ndimage.gaussian_filter1d(self.flux, sigma) # Rebin to logarithm scale galaxy, self.logLam1, velscale = util.log_rebin(self.lam, spec_lin, velscale=velscale) self.dv = (self.logLam2[0] - self.logLam1[0]) * c # if self.sky != None: # sky = self.weights[-1] * self.sky.T[0] # self.bestfit -= sky # self.galaxy -= sky # skyspec = os.path.join(sky_data_dir, spec.replace("fin1", "sky1")) # sky_lin = pf.getdata(skyspec) return
def rebin(self): self.stellar_templates = get_stellar_templates( self.FWHM_gal, library=self.params.library) ## smooth spectrum to fit with templates resolution if self.FWHM_gal < self.stellar_templates.FWHM_tem: sigma = self.stellar_templates.FWHM_dif / 2.355 / self.CDELT # Change in px self.bin_lin = ndimage.gaussian_filter1d(self.bin_lin, sigma) self.bin_lin_noise = np.sqrt( ndimage.gaussian_filter1d(self.bin_lin_noise**2, sigma)) ## rebin spectrum logarthmically self.bin_log, self.logLam_bin, self.velscale = util.log_rebin( self.lamRange, self.bin_lin) bin_log_noise, logLam_bin, _ = util.log_rebin(self.lamRange, self.bin_lin_noise**2) self.bin_log_noise = np.sqrt(bin_log_noise) self.lambdaq = np.exp(self.logLam_bin)
def __init__(self, spec, velscale, pklfile=None): """ Load the pkl file from previous ppxf fit and define some atributes. """ if pklfile == None: pklfile = spec.replace(".fits", ".pkl") with open(pklfile) as f: pp = pickle.load(f) self.__dict__ = pp.__dict__.copy() self.spec = spec if not os.path.exists(os.path.join(os.getcwd(), spec)): self.spec = os.path.join(data_dir, spec) self.velscale = velscale self.w = wavelength_array(os.path.join(data_dir, spec)) self.flux = pf.getdata(self.spec) self.flux_log, self.logw, velscale = util.log_rebin( [self.w[0], self.w[-1]], self.flux, velscale=velscale) self.w_log = np.exp(self.logw) self.header = pf.getheader(os.path.join(data_dir, spec)) self.lam = self.header['CRVAL1'] + np.array([0., self.header['CDELT1']*(self.header['NAXIS1']-1)]) ###################################################################### # # Read templates star_templates, self.logLam2, self.delta, miles= stellar_templates(velscale) ###################################################################### # Convolve our spectra to match MILES resolution FWHM_tem = 2.51 FWHM_spec = 2.1 FWHM_dif = np.sqrt(FWHM_tem**2 - FWHM_spec**2) sigma = FWHM_dif/2.355/self.delta # Sigma difference in pixels ###################################################################### spec_lin = ndimage.gaussian_filter1d(self.flux,sigma) # Rebin to logarithm scale galaxy, self.logLam1, velscale = util.log_rebin(self.lam, spec_lin, velscale=velscale) self.dv = (self.logLam2[0]-self.logLam1[0])*c # if self.sky != None: # sky = self.weights[-1] * self.sky.T[0] # self.bestfit -= sky # self.galaxy -= sky # skyspec = os.path.join(sky_data_dir, spec.replace("fin1", "sky1")) # sky_lin = pf.getdata(skyspec) return
def setup_bc03_library(wave, velscale=None, FWHM_gal=1, version='N', in_log=True): FWHM_tem = 1 # this number is crap as the spectra are synthetic in part... lam, full, full_data, logAge_grid, metal_grid = load_bc03_library(version=version) templates = np.zeros_like(full) new_ssp = np.interp(wave,lam, ssp, right=0, left=0) lamRange_temp = [wave[0],wave[-1]] delta = np.diff(lam)[0] if in_log: sspNew, logLam2, velscale = util.log_rebin(lamRange_temp, ssp, velscale=0.99999*velscale) else: sspNew=ssp logLam2=np.log(lam) # templates = np.empty((sspNew.size, nAges, nMetal)) FWHM_dif = np.sqrt(FWHM_gal**2 - FWHM_tem**2) sigma = FWHM_dif/2.355/delta # Sigma difference in pixels for i,d in enumerate(data): tmp=os.path.join(os.path.dirname(basefile),'BasesDir',d['specfile']) lam,ssp = np.loadtxt(tmp, unpack=True) full[:,i%nAges,int(i/nAges)] = ssp new_lam = wave #new_lam = np.arange(new_lam[0], new_lam[-1],1) new_ssp = np.interp(new_lam,lam, ssp, right=0, left=0) # new_lam = np.arange(lam[0], lam[-1],1) # new_ssp = np.interp(new_lam,lam, ssp, right=0, left=0) # mask = (new_lam > wave.min()-1) & (new_lam < wave.max()+2) lam=new_lam#[mask] ssp=new_ssp#[mask] ssp = ndimage.gaussian_filter1d(ssp,sigma) if in_log: sspNew, logLam2, velscale = util.log_rebin(lamRange_temp, ssp, velscale=velscale) else: sspNew=ssp templates[:,i%nAges,int(i/nAges)] = sspNew logAge_grid[i%nAges,int(i/nAges)] = np.log10(d['age']/1.e9) metal_grid[i%nAges,int(i/nAges)] = np.log10(d['Z']) return templates, lamRange_temp, logAge_grid, metal_grid#, lam_full, full
def build_templates(lam, ssp, wave, velscale, sigma): new_ssp = np.interp(wave, lam, ssp, right=0, left=0) lam = wave ssp = new_ssp ssp = ndimage.gaussian_filter1d(new_ssp, sigma) sspNew, logLam2, velscale = util.log_rebin([wave[0], wave[-1]], ssp, velscale=velscale) return sspNew
def rebin(self): self.stellar_templates = get_stellar_templates(self.FWHM_gal, library=self.params.library) ## smooth spectrum to fit with templates resolution if self.FWHM_gal < self.stellar_templates.FWHM_tem: sigma = self.stellar_templates.FWHM_dif/2.355/self.CDELT # Change in px self.bin_lin = ndimage.gaussian_filter1d(self.bin_lin, sigma) self.bin_lin_noise = np.sqrt(ndimage.gaussian_filter1d( self.bin_lin_noise**2, sigma)) self.FWHM_gal = self.stellar_templates.FWHM_tem ## rebin spectrum logarthmically self.bin_log, self.logLam_bin, self.velscale = util.log_rebin(self.lamRange, self.bin_lin) bin_log_noise, logLam_bin, _ = util.log_rebin(self.lamRange, self.bin_lin_noise**2) self.bin_log_noise = np.sqrt(bin_log_noise) self.lambdaq = np.exp(self.logLam_bin)
def getdata_NEW(spec, wavelength, l1, l2): tmp = np.column_stack((wavelength, spec)) mn = tmp[tmp[:, 0] > l1] mx = tmp[tmp[:, 0] < l2] lamRange = [mn[0, 0], mx[-1, 0]] galaxy, logLam1, velscale = util.log_rebin(lamRange, spec) return galaxy, logLam1, velscale
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(template_dir) miles = [x for x in os.listdir(".") if x.startswith("Mun") 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]) Zs = [str(x).replace(".", "_") for x in Zs] # Ordered array of ages Ts = list(set([x.split("T")[1].split(".fits")[0].replace("_", ".") for x in miles])) Ts.sort() Ts = [str(x).replace(".", "_") for x in Ts] miles = [] metal_ages = [] for m in Zs: for t in Ts: filename = "Mun1_30Z{0}T{1}.fits".format(m, t) if os.path.exists(filename): miles.append(filename) metal_ages.append([m.replace("_", ".").replace("p", "+").replace("m", "-"), t.replace("_", ".")]) 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 os.chdir(current_dir) return templates, logLam2, h2["CDELT1"], miles
def fit_spectra(bin_sci_j, ppxf_bestfit_j, plot=False): """ """ with fits.open(bin_sci_j) as hdu: odata = hdu[0].data ohdr = hdu[0].header bestfit = fits.getdata(ppxf_bestfit_j) lamRange = ohdr['CRVAL1'] + np.array([1. - ohdr['CRPIX1'], ohdr['NAXIS1'] - ohdr['CRPIX1']]) * ohdr['CD1_1'] x = np.linspace(lamRange[0], lamRange[1], ohdr['NAXIS1']) # Log bin the spectra to match the best fit absorption template galaxy, logLam1, velscale = util.log_rebin(lamRange, odata) log_bins = np.exp(logLam1) emlns, lnames, lwave = util.emission_lines(logLam1, lamRange, 2.3) # Hgamma 4340.47, Hbeta 4861.33, OIII [4958.92, 5006.84] # lwave = [4340.47, 4861.33, 4958.92, 5006.84] # find the index of the emission lines iHg = (np.abs(log_bins - lwave[0])).argmin() iHb = (np.abs(log_bins - lwave[1])).argmin() iOIII = (np.abs(log_bins - lwave[2])).argmin() iOIIIb = (np.abs(log_bins - (lwave[2] - 47.92))).argmin() # There are BOTH absorption and emission features about the wavelength of Hgamma and Hbeta, so we need # to use a specialized fitting function (convolved Guassian and Lorentzian -> pVoight) to remove the # emission lines popt_Hg, pcov_Hg = fit_ems_pvoightcont(log_bins, galaxy, x, odata, iHg, bestfit) popt_Hb, pcov_Hb = fit_ems_pvoightcont(log_bins, galaxy, x, odata, iHb, bestfit) # There are only emission features about the OIII doublet so we only fit the emission line with a Gaussian popt_OIII, pcov_OIII = fit_ems_lincont(x, odata, iOIII, bestfit, x[954], [x[952], x[956]]) popt_OIIIb, pcov_OIIIb = fit_ems_lincont(x, odata, iOIIIb, bestfit) em_fit = gauss_lorentz(x, popt_Hg[0], popt_Hg[1], popt_Hg[2], popt_Hg[3], popt_Hg[4], popt_Hg[5]) + \ gauss_lorentz(x, popt_Hb[0], popt_Hb[1], popt_Hb[2], popt_Hb[3], popt_Hb[4], popt_Hb[5]) + \ gauss_lorentz(x, popt_OIII[0], popt_OIII[1], popt_OIII[2], popt_OIII[3], popt_OIII[4], popt_OIII[5]) + \ gauss_lorentz(x, popt_OIIIb[0], popt_OIIIb[1], popt_OIIIb[2], popt_OIIIb[3], popt_OIIIb[4], popt_OIIIb[5]) abs_fit = odata - em_fit if plot: plt.plot(x, odata, '-k', label="spectra") # plt.plot(x, bestfit, '--r', label="bestfit absorption line") plt.plot(x, abs_fit, '-b', label="absorption spectra - gauss") plt.legend() plt.show() return abs_fit, em_fit
def get_templates(velscale, l1, l2): library = glob('/Volumes/VINCE/OAC/libraries/Valdes/clean_short/*') ssp = pd.read_table(library[0], header=None)[0].values FWHM_gal = 13.1 FWHM_tem = 1.35 FWHM_dif = np.sqrt(FWHM_gal**2 - FWHM_tem**2) scale = 0.4 sigma = FWHM_dif / 2.355 / scale lamRange2 = [3465., 9469.] sspNew, logLam2, _ = util.log_rebin(lamRange2, ssp, velscale=velscale) library_size = sspNew.size templates = np.empty((library_size, len(library))) for j in range(len(library)): tmp = pd.read_table(library[j], sep=r'\s+', header=None) ssp = tmp[0].values ssp = ndimage.gaussian_filter1d(ssp, sigma) sspNew, logLam2, _ = util.log_rebin(lamRange2, ssp, velscale=velscale) templates[:, j] = sspNew / np.median(sspNew) return logLam2, templates
def __init__(self, spec, velscale): """ Load the pkl file from previous ppxf fit and define some attributes. """ with open(spec.replace(".fits", ".pkl")) as f: pp = pickle.load(f) self.__dict__ = pp.__dict__.copy() self.spec = os.path.join(data_dir, spec) self.vhelio = pf.getval(os.path.join(data_dir, self.spec), "VHELIO") self.velscale = velscale self.w = wavelength_array(os.path.join(data_dir, spec)) self.flux = pf.getdata(self.spec) self.flux_log, self.logw, velscale = util.log_rebin( [self.w[0], self.w[-1]], self.flux, velscale=velscale) self.w_log = np.exp(self.logw) self.calc_sn() return
def check_ppxf(spec, velscale): """ Checking if velocity os star is zero""" templates, logLam2, delta, miles= stellar_templates(velscale) FWHM_dif = np.sqrt(FWHM_tem**2 - FWHM_spec**2) sigma = FWHM_dif/2.355/delta # Sigma difference in pixels star = pf.getdata(spec) star1 = ndimage.gaussian_filter1d(star, sigma) w = wavelength_array(spec) lamRange= np.array([w[0], w[-1]]) galaxy, logLam1, velscale = util.log_rebin(lamRange, star1, \ velscale=velscale) sn = snr(star1) noise = np.ones_like(galaxy) / sn dv = (logLam2[0]-logLam1[0])*c pp = ppxf(templates, galaxy, noise, velscale, [0,50], plot=True, moments=2, degree=5, mdegree=-1, vsyst=dv) plt.show() return
def w_temp(velscale): """ Make templates array""" current_dir = os.getcwd() os.chdir(template_dir) miles = [x for x in os.listdir(".") if x.endswith(".fits")] miles.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(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) os.chdir(current_dir) return np.exp(logLam2)
def ppxf_gas_kinematics_parallel_loop(bin_sci_j, ppxf_bestfit_j, gal_hdr, templates, velscale, sigma, lamRange1, logLam2, start, plot, moments, regul_err, reg_dim, component, bias, quiet): gal_data = fits.getdata(bin_sci_j, 0) gal_data_new = ndimage.gaussian_filter1d(gal_data, sigma) galaxy, logLam1, velscale = util.log_rebin(lamRange1, gal_data_new, velscale=velscale) noise = galaxy * 0 + 1 # Assume constant noise per pixel here # The galaxy and the template spectra do not have the same starting wavelength. # For this reason an extra velocity shift DV has to be applied to the template # to fit the galaxy spectrum. We remove this artificial shift by using the # keyword VSYST in the call to PPXF below, so that all velocities are # measured with respect to DV. This assume the redshift is negligible. # In the case of a high-redshift galaxy one should de-redshift its # wavelength to the rest frame before using the line below as described # in PPXF_KINEMATICS_EXAMPLE_SAURON. # c = 299792.458 dv = (logLam2[0] - logLam1[0]) * c # km/s t = clock() pp = ppxf(templates, galaxy, noise, velscale, start, plot=plot, moments=moments, degree=-1, mdegree=10, vsyst=dv, clean=False, regul=1. / regul_err, reg_dim=reg_dim, component=component, bias=bias, quiet=quiet) print ('pPXF sol of bin {}: {}'.format(j, pp.sol)) print ('pPXF error of bin {}: {}'.format(j, pp.error)) hdu_best = fits.PrimaryHDU() hdu_best.data = pp.bestfit hdu_best.header['CD1_1'] = gal_hdr['CD1_1'] hdu_best.header['CDELT1'] = gal_hdr['CD1_1'] hdu_best.header['CRPIX1'] = gal_hdr['CRPIX1'] hdu_best.header['CRVAL1'] = gal_hdr['CRVAL1'] hdu_best.header['NAXIS1'] = pp.bestfit.size hdu_best.header['CTYPE1'] = 'LINEAR' # corresponds to sampling of values above hdu_best.header['DC-FLAG'] = '1' # 0 = linear, 1= log-linear sampling hdu_best.writeto(ppxf_bestfit_j, clobber=True) return pp
def emission_line_template(lines, velscale, res=2.54, 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() # Template directory is also set in setyp.py os.chdir(template_dir) refspec = [x for x in os.listdir(".") if x.endswith(".fits")][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
def run_ppxf(spectra, velscale): """ Run pPXF in a list of spectra""" print "Loading templates" templates, logLam2, delta = load_templates(velscale) for i, spec in enumerate(spectra): print "pPXF run of spectrum {0} ({1} of {2})".format(spec, i+1, len(spectra)) print "Preparing files..." # Read a galaxy spectrum and define the wavelength range hdu = pf.open(spec) spec_lin = hdu[0].data h1 = hdu[0].header lamRange1 = h1['CRVAL1'] + np.array([0.,h1['CDELT1']*(h1['NAXIS1']-1)]) # Convolve our spectra to match MILES resolution FWHM_dif = np.sqrt(FWHM_tem**2 - FWHM_spec**2) sigma = FWHM_dif/2.355/delta # Sigma difference in pixels spec_lin = ndimage.gaussian_filter1d(spec_lin,sigma) # Rebin to logarithm scale galaxy, logLam1, velscale = util.log_rebin(lamRange1, spec_lin, velscale=velscale) noise = 0. * galaxy + 0.1 dv = (logLam2[0]-logLam1[0])*c start, goodPixels = read_setup_file(spec, logLam1) print "First interaction of pPXF.." pp0 = ppxf(templates, galaxy, noise, velscale, start, goodpixels=goodPixels, plot=False, moments=4, degree=6, mdegree=4, vsyst=dv) print "Calculating realistic noise for input..." rms0 = galaxy[goodPixels] - pp0.bestfit[goodPixels] noise0 = 1.4826 * np.median(np.abs(rms0 - np.median(rms0))) noise0 = 0. * galaxy + noise0 print "Second run of pPXF..." pp = ppxf(templates, galaxy, noise0, velscale, pp0.sol, goodpixels=goodPixels, plot=False, moments=4, degree=6, mdegree=4, vsyst=dv, lam=lamRange1) print "Finished! Now saving results..." with open(spec.replace(".fits", ".pkl"), "w") as f: pickle.dump(pp, f) return
def w_temp(velscale): """ Make templates array""" current_dir = os.getcwd() os.chdir(template_dir) miles = [x for x in os.listdir(".") if x.endswith(".fits")] miles.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(miles[0]) ssp = hdu[0].data h2 = hdu[0].header lamRange2 = h2['CRVAL1'] + np.array( [0., h2['CDELT1'] * (h2['NAXIS1'] - 1)]) sspNew, logLam2, velscale = util.log_rebin(lamRange2, ssp, velscale=velscale) os.chdir(current_dir) return np.exp(logLam2)
def smooth_and_rebin(bc03, bc03_lambda, gal_lambda, velscale, FWHM_gal=1, FWHM_tem=0): """ Smooth and rebin the BC03 spectra based on the sampling of the galaxy to be fitted. Input : - bc03 : the spectral templates - bc03_lambda : the template wavelength array - gal_lambda : the galaxy wavelength array, which the rebinning is going to match. Note tha the galaxy must have already been log-rebinned. - velscale : velocity scale of the galaxy, for log rebinning - FWHM_gal : FWHM of the galaxy resolution - FWHM_tem : FWHM of the template resolution Output : - templates : the smoothed and rebinned version of the BC03 input """ templates = np.empty( ((gal_lambda.size, ) + (bc03.shape[1], bc03.shape[2]))) for i in range(bc03.shape[1]): for j in range(bc03.shape[2]): ssp = bc03[:, i, j] #resize the templates to the log-rebinned galaxy wavelengths new_ssp = np.interp(gal_lambda, bc03_lambda, ssp, right=0, left=0) #log-rebin the interpolated templates #the output velscape should be close to identical to the input #Likewise, logLam2 == np.log(gal_lambda) to about 1e-5 sspNew, logLam2, velscale = util.log_rebin( [gal_lambda[0], gal_lambda[-1]], new_ssp, velscale=velscale) #smooth the log-rebinned templates FWHM_dif = np.sqrt(FWHM_gal**2 - FWHM_tem**2) sigma = FWHM_dif / 2.355 / np.diff(gal_lambda)[0] smoothed_ssp = ndimage.gaussian_filter1d(new_ssp, sigma) templates[:, i, j] = smoothed_ssp return templates
def emission_line_template(lines, velscale, res=2.54, 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() # Template directory is also set in setyp.py os.chdir(template_dir) refspec = [x for x in os.listdir(".") if x.endswith(".fits")][0] lamb = wavelength_array(refspec) delta = lamb[1] - lamb[0] lamb2 = np.linspace(lamb[0]-delta/2., lamb[-1] + delta/2., len(lamb+1)*resamp) sigma = res / (2. * np.sqrt(2. * np.log(2.))) 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
def subtract_besftfit(bin_sci, ppxf_bestfit, em_bin_sci): """ """ for j in range(len(glob.glob(bin_sci.format('*')))): if not os.path.exists(em_bin_sci.format(j)): with fits.open(bin_sci.format(j)) as hdu: odata = hdu[0].data ohdr = hdu[0].header bestfit = fits.getdata(ppxf_bestfit.format(j)) lamRange = ohdr['CRVAL1'] + np.array([1. - ohdr['CRPIX1'], ohdr['NAXIS1'] - ohdr['CRPIX1']]) * ohdr['CD1_1'] # Log bin the spectra to match the best fit absorption template galaxy, logLam, velscale = util.log_rebin(lamRange, odata) ems_fit = np.subtract(galaxy, bestfit) ems_hdu = fits.PrimaryHDU() ems_hdu.data = ems_fit ems_hdu.header = fits.getheader(bin_sci.format(j), 0) ems_hdu.writeto(em_bin_sci.format(j))
def setup_spectral_library(velscale, FWHM_gal): # Read the list of filenames from the Single Stellar Population library # by Vazdekis et al. (2010, MNRAS, 404, 1639) http://miles.iac.es/. # # For this example I downloaded from the above website a set of # model spectra with default linear sampling of 0.9A/pix and default # spectral resolution of FWHM=2.51A. I selected a Salpeter IMF # (slope 1.30) and a range of population parameters: # # [M/H] = [-1.71, -1.31, -0.71, -0.40, 0.00, 0.22] # Age = np.linspace(np.log10(1), np.log10(17.7828), 26) # # This leads to a set of 156 model spectra with the file names like # # Mun1.30Zm0.40T03.9811.fits # # IMPORTANT: the selected models form a rectangular grid in [M/H] # and Age: for each Age the spectra sample the same set of [M/H]. # # We assume below that the model spectra have been placed in the # directory "miles_models" under the current directory. # vazdekis = glob.glob('miles_models/Mun1.30*.fits') vazdekis.sort() FWHM_tem = 2.51 # Vazdekis+10 spectra have a 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 lamRange_temp = h2['CRVAL1'] + np.array( [0., h2['CDELT1'] * (h2['NAXIS1'] - 1)]) sspNew, logLam2, velscale = util.log_rebin(lamRange_temp, ssp, velscale=velscale) # Create a three dimensional array to store the # two dimensional grid of model spectra # nAges = 26 nMetal = 6 templates = np.empty((sspNew.size, nAges, nMetal)) # 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. # FWHM_dif = np.sqrt(FWHM_gal**2 - FWHM_tem**2) sigma = FWHM_dif / 2.355 / h2['CDELT1'] # Sigma difference in pixels # These are the array where we want to store # the characteristics of each SSP model # logAge_grid = np.empty((nAges, nMetal)) metal_grid = np.empty((nAges, nMetal)) # These are the characteristics of the adopted rectangular grid of SSP models # logAge = np.linspace(np.log10(1), np.log10(17.7828), nAges) metal = [-1.71, -1.31, -0.71, -0.40, 0.00, 0.22] # 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 # metal_str = ['m1.71', 'm1.31', 'm0.71', 'm0.40', 'p0.00', 'p0.22'] for k, mh in enumerate(metal_str): files = [s for s in vazdekis if mh in s] for j, filename in enumerate(files): hdu = fits.open(filename) ssp = hdu[0].data ssp = ndimage.gaussian_filter1d(ssp, sigma) sspNew, logLam2, velscale = util.log_rebin(lamRange_temp, ssp, velscale=velscale) templates[:, j, k] = sspNew # Templates are *not* normalized here logAge_grid[j, k] = logAge[j] metal_grid[j, k] = metal[k] return templates, lamRange_temp, logAge_grid, metal_grid
def get_templates(self, galaxy, velscale=None, use_all_temp=False): import glob # for searching for files if self.library == 'vazdekis': templateFiles = glob.glob( '%s/libraries/python/ppxf/spectra/Rbi1.30z*.fits' % (cc.home_dir)) f = fits.open(templateFiles[0]) v2 = f[0].data self.wav = np.arange(f[0].header['NAXIS1'])*f[0].header['CDELT1'] + \ f[0].header['CRVAL1'] # Using same keywords as fits headers CRVAL_temp = f[0].header['CRVAL1'] # starting wavelength NAXIS_temp = f[0].header['NAXIS1'] # Number of entries # wavelength increments (resolution?) CDELT_temp = f[0].header['CDELT1'] f.close() self.lamRange_template = CRVAL_temp + np.array( [0, CDELT_temp * (NAXIS_temp - 1)]) log_temp_template, self.logLam_template, self.velscale = \ util.log_rebin(self.lamRange_template, self.wav)#, velscale=velscale) self.ntemp = len(templateFiles) self.templatesToUse = np.arange(self.ntemp) self.templates = np.zeros((len(log_temp_template), self.ntemp)) self.lin_templates = np.zeros( (len(f[0].header['NAXIS1']), self.ntemp)) ## Reading the contents of the files into the array templates. ## Including rebinning them. for i in range(self.ntemp): f = fits.open(templateFiles[i]) self.lin_templates[:, i] = f[0].data f.close() if self.FWHM_tem < self.FWHM_gal: sigma = self.FWHM_dif / 2.355 / CDELT_temp # Sigma difference (px) conv_temp = ndimage.gaussian_filter1d( self.lin_templates[:, i], sigma) else: conv_temp = self.lin_templates[:, i] ## Rebinning templates logarthmically log_temp_template, self.logLam_template, _ = util.log_rebin( self.lamRange_template, conv_temp) #, velscale=self.velscale) self.templates[:, i] = log_temp_template elif self.library == 'Miles': # Miles stars # Finding the template files # There is some issue with the memory structure of the university macs # (HFS+), meaning these templates can only be loaded once if located on # the home directory, but more if on the Data partition... if cc.device != 'uni': templateFiles = glob.glob( '%s/models/miles_library/m0[0-9][0-9][0-9]V' % (cc.home_dir)) else: templateFiles = glob.glob( '%s/Data/' % (cc.base_dir) + 'idl_libraries/ppxf/MILES_library/m0[0-9][0-9][0-9]V') # self.wav is wavelength, v2 is spectrum self.wav, v2 = np.loadtxt(templateFiles[0], unpack='True') # Using same keywords as fits headers CRVAL_temp = self.wav[0] # starting wavelength NAXIS_temp = np.shape(v2)[0] # Number of entries # wavelength increments (resolution?) CDELT_temp = (self.wav[NAXIS_temp - 1] - self.wav[0]) / (NAXIS_temp - 1) self.lamRange_template = CRVAL_temp + np.array( [0, CDELT_temp * (NAXIS_temp - 1)]) log_temp_template, self.logLam_template, self.velscale = \ util.log_rebin(self.lamRange_template, self.wav, velscale=velscale) if use_all_temp or galaxy is None: self.ntemp = len(templateFiles) self.templatesToUse = np.arange(self.ntemp) else: self.templatesToUse = use_templates(galaxy, cc.device == 'glamdring') self.ntemp = len(self.templatesToUse) self.templates = np.zeros((len(log_temp_template), self.ntemp)) self.lin_templates = np.zeros((len(v2), self.ntemp)) ## Reading the contents of the files into the array templates. ## Including rebinning them. for i in range(self.ntemp): if use_all_temp: _, self.lin_templates[:, i] = np.loadtxt(templateFiles[i], unpack='True') else: _, self.lin_templates[:, i] = np.loadtxt( templateFiles[self.templatesToUse[i]], unpack='True') if self.FWHM_tem < self.FWHM_gal: sigma = self.FWHM_dif / 2.355 / CDELT_temp # Sigma difference (px) conv_temp = ndimage.gaussian_filter1d( self.lin_templates[:, i], sigma) else: conv_temp = self.lin_templates[:, i] ## Rebinning templates logarthmically log_temp_template, self.logLam_template, _ = util.log_rebin( self.lamRange_template, conv_temp, velscale=self.velscale) self.templates[:, i] = log_temp_template
def remove_emission_lines(bin_sci, ppxf_bestfit, abs_bin_sci, plot=True, bad_lines=[]): """ Fit the absorption spectra with a pVoight function by parameterizing the bestfit template as output from pPXF, fit the emission lines over the absorption features with a sum of a gaussian and lorentz and subtract from the original spectra. Where the emission lines are isolated, just interpolate the continuum values from the best fit template and replace. This requires a very good fit between the galaxy spectra and the bestfit spectra. I recommend running this with plot=True to check for any residuals. """ for j in range(len(glob.glob(bin_sci.format('*')))): bin_sci_j = bin_sci.format(j) ppxf_bestfit_j = ppxf_bestfit.format(j) if not os.path.exists(abs_bin_sci.format(j)): if plot: print('>>>>> Removing emission lines from spectra {}'.format(j)) with fits.open(bin_sci_j) as hdu: odata = hdu[0].data ohdr = hdu[0].header bestfit = fits.getdata(ppxf_bestfit_j) lamRange = ohdr['CRVAL1'] + np.array([1. - ohdr['CRPIX1'], ohdr['NAXIS1'] - ohdr['CRPIX1']]) * ohdr['CD1_1'] lin_bins = np.linspace(lamRange[0], lamRange[1], ohdr['NAXIS1']) # Log bin the spectra to match the best fit absorption template galaxy, logLam1, velscale = util.log_rebin(lamRange, odata) log_bins = np.exp(logLam1) emlns, lnames, lwave = util.emission_lines(logLam1, lamRange, 2.3) # Hgamma 4340.47, Hbeta 4861.33, OIII [4958.92, 5006.84] # lwave = [4340.47, 4861.33, 4958.92, 5006.84] # find the index of the emission lines iHg = (np.abs(log_bins - lwave[0])).argmin() iHb = (np.abs(log_bins - lwave[1])).argmin() iOIII = (np.abs(log_bins - lwave[2])).argmin() iOIIIb = (np.abs(log_bins - (lwave[2] - 47.92))).argmin() # There are BOTH absorption and emission features about the wavelength of Hgamma and Hbeta, so we need # to use a specialized fitting function (convolved Guassian and Lorentzian -> pVoight) to remove the # emission lines popt_Hg, pcov_Hg = remove_lines.fit_ems_pvoightcont(log_bins, galaxy, lin_bins, odata, iHg, bestfit) popt_Hb, pcov_Hb = remove_lines.fit_ems_pvoightcont(log_bins, galaxy, lin_bins, odata, iHb, bestfit) em_fit = remove_lines.gauss_lorentz(lin_bins, popt_Hg[0], popt_Hg[1], popt_Hg[2], popt_Hg[3], popt_Hg[4], popt_Hg[5]) + \ remove_lines.gauss_lorentz(lin_bins, popt_Hb[0], popt_Hb[1], popt_Hb[2], popt_Hb[3], popt_Hb[4], popt_Hb[5]) abs_feat_fit = odata - em_fit abs_fit = remove_lines.em_chop(lin_bins, abs_feat_fit, iOIII, log_bins, bestfit) abs_fit = remove_lines.em_chop(lin_bins, abs_fit, iOIIIb, log_bins, bestfit) for lin in bad_lines: ibad = (np.abs(lin_bins - lin)).argmin() abs_fit = remove_lines.em_chop(lin_bins, abs_fit, ibad, log_bins, bestfit) if plot: plt.plot(lin_bins, odata, '-k', label="spectra") # plt.plot(lin_bins, bestfit, '--r', label="bestfit absorption line") plt.plot(lin_bins, abs_fit, '-b', label="absorption spectra") plt.legend() plt.show() abs_hdu = fits.PrimaryHDU() abs_hdu.data = abs_fit abs_hdu.header = fits.getheader(bin_sci.format(j), 0) abs_hdu.writeto(abs_bin_sci.format(j))
def ppxf_example_simulation(): file_dir = path.dirname(path.realpath(__file__)) # path of this procedure hdu = fits.open(file_dir + '/veltemps/bd-013097_M2III_rebinflux_rest.fits' ) # BUCKET: which star should I choose??? # '/miles_models/Mun1.30Zp0.00T12.5893_iPp0.00_baseFe_linear_FWHM_2.51.fits') # Solar metallicitly, Age=12.59 Gyr ssp = hdu[ 1].data # hdu[1] is science header for us; hdu[0] is generic header h = hdu[1].header lamRange = h['CRVAL1'] + np.array([0., h['CD1_1'] * (h['NAXIS1'] - 1)]) c = 299792.458 # speed of light in km/s velscale = c * h['CD1_1'] / 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 # find worst SNR, and worst h3, h4 (highest) h3 = 0.1 # Adopted G-H parameters of the LOSVD h4 = 0.04 # 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] # (len m array of random on [0, 1) sigmaV = np.linspace( 0.5, 4, m ) # Range of sigma in *pixels* [=sigma(km/s)/velScale] # m evenly spaced [0.5,...,4] 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 # FFT convolution: multiplication in the frequency domain corresponds to convolution in the time domain 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.4) # 0.2 result[j, :] = pp.sol print('Calculation time: %.2f s' % (clock() - t)) # large scale kinematics to estimate dark matter fraction within effective radii, and stellar mass-light ratio # mass distribution discribed by GME and dark matter dstribution different # work with sarah to use JAM; figure out why we ever use schwarzschild model if JAM is faster (what assumptions are # we making/what regimes do we have to be in to use JAM? plt.clf() plt.subplot(221) plt.plot(sigmaV * velscale, result[:, 0] - velV * velscale, '+k') # BUCKET: why is V multiplied by velscale? 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') # BUCKET: why is sigma multiplied by velscale? 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.show() # pause(1)
def ppxf_example_two_components(): file_dir = path.dirname(path.realpath(__file__)) # path of this procedure hdu = fits.open(file_dir + '/miles_models/Mun1.30Zp0.00T12.5893.fits' ) # Solar metallicitly, Age=12.59 Gyr gal_lin = hdu[0].data h1 = hdu[0].header lamRange1 = h1['CRVAL1'] + np.array( [0., h1['CDELT1'] * (h1['NAXIS1'] - 1)]) c = 299792.458 # speed of light in km/s velscale = c * h1['CDELT1'] / max( lamRange1) # Do not degrade original velocity sampling model1, logLam1, velscale = util.log_rebin(lamRange1, gal_lin, velscale=velscale) model1 /= np.median(model1) hdu = fits.open(file_dir + '/miles_models/Mun1.30Zp0.00T01.0000.fits' ) # Solar metallicitly, Age=1.00 Gyr gal_lin = hdu[0].data model2, logLam1, velscale = util.log_rebin(lamRange1, gal_lin, velscale=velscale) model2 /= np.median(model2) model = np.column_stack([model1, model2]) galaxy = np.empty_like(model) # These are the input values in spectral pixels # for the (V,sigma) of the two kinematic components # vel = np.array([0., 250.]) / velscale sigma = np.array([200., 100.]) / velscale # The synthetic galaxy model consists of the sum of two # SSP spectra with age of 1Gyr and 13Gyr respectively # with different velocity and dispersion # for j in range(len(vel)): dx = int(abs(vel[j]) + 4. * sigma[j]) # Sample the Gaussian at least to vel+4*sigma v = np.linspace(-dx, dx, 2 * dx + 1) losvd = np.exp(-0.5 * ((v - vel[j]) / sigma[j])**2) # Gaussian LOSVD losvd /= np.sum(losvd) # normaize LOSVD galaxy[:, j] = signal.fftconvolve(model[:, j], losvd, mode="same") galaxy[:, j] /= np.median(model[:, j]) galaxy = np.sum(galaxy, axis=1) sn = 100. noise = galaxy / sn galaxy = np.random.normal(galaxy, noise) # add noise to galaxy # Adopts two templates per kinematic component # templates = np.column_stack([model1, model2, model1, model2]) # Start both kinematic components from the same guess. # With multiple stellar kinematic components # a good starting guess is essential # start = [np.mean(vel) * velscale, np.mean(sigma) * velscale] start = [start, start] goodPixels = np.arange(20, 6000) t = clock() plt.clf() plt.subplot(211) plt.title("Two components pPXF fit") print("+++++++++++++++++++++++++++++++++++++++++++++") pp = ppxf(templates, galaxy, noise, velscale, start, goodpixels=goodPixels, plot=True, degree=4, moments=[2, 2], component=[0, 0, 1, 1]) plt.subplot(212) plt.title("Single component pPXF fit") print("---------------------------------------------") start = start[0] pp = ppxf(templates, galaxy, noise, velscale, start, goodpixels=goodPixels, plot=True, degree=4, moments=2) print("=============================================") print("Total elapsed time %.2f s" % (clock() - t)) plt.tight_layout() plt.pause(1)
def run_ppxf(spectra, velscale, ncomp=None, has_emission=True, mdegree=-1, degree=20, plot=False, sky=None, start=None, moments=None, log_dir=None, w1=4000., w2=7000.): """ Run pPXF in a list of spectra""" if isinstance(spectra, str): spectra = [spectra] ########################################################################## # Load templates for both stars and gas star_templates = pf.getdata(os.path.join(templates_dir, 'miles_FWHM_3.7.fits'), 0) logLam2 = pf.getdata(os.path.join(templates_dir, 'miles_FWHM_3.7.fits'), 1) miles = np.loadtxt(os.path.join(templates_dir, 'miles_FWHM_3.7.txt'), dtype=str).tolist() gas_templates = pf.getdata(os.path.join(templates_dir, 'emission_FWHM_3.7.fits'), 0) logLam_gas = pf.getdata(os.path.join(templates_dir, 'emission_FWHM_3.7.fits'), 1) gas_files = np.loadtxt(os.path.join(templates_dir, 'emission_FWHM_3.7.txt'), dtype=str).tolist() ngas = len(gas_files) ntemplates = len(miles) ########################################################################## # Join templates in case emission lines are used. if has_emission: templates = np.column_stack((star_templates, gas_templates)) templates_names = np.hstack((miles, gas_files)) else: templates = star_templates templates_names = miles ngas = 0 ########################################################################## if sky == None: nsky = 0 else: nsky = sky.shape[1] if ncomp == 1: components = 0 elif ncomp == 2: components = np.hstack((np.zeros(len(star_templates[0])), np.ones(len(gas_templates[0])))) if moments == None: moments = [4] if ncomp == 1 else [4,2] for i, spec in enumerate(spectra): print "pPXF run of spectrum {0} ({1} of {2})".format(spec, i+1, len(spectra)) plt.clf() ###################################################################### # Read galaxy spectrum and define the wavelength range hdu = pf.open(spec) spec_lin = hdu[0].data h1 = pf.getheader(spec) lamRange1 = h1['CRVAL1'] + np.array([0.,h1['CDELT1']*(h1['NAXIS1']-1)]) ###################################################################### # Rebin to log scale galaxy, logLam1, velscale = util.log_rebin(lamRange1, spec_lin, velscale=velscale) ###################################################################### # First guess for the noise noise = np.ones_like(galaxy) * np.std(galaxy - medfilt(galaxy, 5)) ###################################################################### # Calculate difference of velocity between spectrum and templates # due to different initial wavelength dv = (logLam2[0]-logLam1[0])*c ###################################################################### # Set first guess from setup files if start is None: start = [v0s[spec.split("_")[0]], 50] goodPixels = None ###################################################################### # Expand start variable to include multiple components if ncomp > 1: start = [start, [start[0], 30]] ###################################################################### # Select goodpixels w = np.exp(logLam1) goodpixels = np.argwhere((w>w1) & (w<w2)).T[0] # First pPXF interaction pp0 = ppxf(templates, galaxy, noise, velscale, start, goodpixels=goodpixels, plot=False, moments=moments, degree=degree, mdegree=mdegree, vsyst=dv, component=components, sky=sky) rms0 = galaxy[goodpixels] - pp0.bestfit[goodpixels] noise0 = 1.4826 * np.median(np.abs(rms0 - np.median(rms0))) noise0 = np.zeros_like(galaxy) + noise0 # Second pPXF interaction, realistic noise estimation pp = ppxf(templates, galaxy, noise0, velscale, start, goodpixels=goodpixels, plot=False, moments=moments, degree=degree, mdegree=mdegree, vsyst=dv, component=components, sky=sky) plt.title(spec.replace("_", "-")) plt.show(block=False) plt.savefig("{1}/{0}".format(spec.replace(".fits", ".png"), log_dir)) ###################################################################### # Adding other things to the pp object pp.template_files = templates_names pp.has_emission = has_emission pp.dv = dv pp.w = w pp.velscale = velscale pp.spec = spec pp.ngas = ngas pp.ntemplates = ntemplates pp.nsky = nsky pp.templates = 0 ###################################################################### # Save fit to pickles file to keep session ppsave(pp, "{1}/{0}".format(spec.replace(".fits", ""), log_dir)) pp = ppload("{1}/{0}".format(spec.replace(".fits", ""), log_dir)) ###################################################################### ppf = pPXF(spec, velscale, pp) ppf.plot("{1}/{0}".format(spec.replace(".fits", ".png"), log_dir)) ###################################################################### # # Save to output text file # if ncomp > 1: # pp.sol = pp.sol[0] # pp.error = pp.error[0] # sol = [val for pair in zip(pp.sol, pp.error) for val in pair] # sol = ["{0:12s}".format("{0:.3g}".format(x)) for x in sol] # sol.append("{0:12s}".format("{0:.3g}".format(pp0.chi2))) # sol = ["{0:30s}".format(spec)] + sol return
def interface_sets(nstart, nend): full_time = clock() ####### setting up some variables for magnitudes and indices calculation ######## c = 299792.458 filter_list = 'sdss_manga_pipeline.res' zero_point = 'AB' redshift = 0.0 indices_list = 'MaNGA_range.def' print(' > gathering info') ######## setting up directories and files ######## data_dir = '/mnt/lustre/smg/stellar_libraries/MaNGA/' output_dir = '/mnt/lustre/smg/stellar_libraries/MaNGA/parameters/she-ra/beta_tests/combo_test/' file_spectra = 'MaStar_spectra_ebv_gaia_rb_isopars' ##### templates information ##### templates_dir = '/mnt/lustre/smg/stellar_libraries/atlas9marcs/' templates_parameters = 'templates_grid_parameters_R2000.fits' header = fits.open(templates_dir + templates_parameters) templ_id, templ_teff, templ_logg, templ_metal = header[1].data[ 'NAME'], header[1].data['TEFF'], header[1].data['LOGG'], header[ 1].data['METAL'] templ_gmag, templa_rmag, templ_imag = header[1].data['Gmag'], header[ 1].data['Rmag'], header[1].data['Imag'] tot_templ = len(templ_id) estimate_teff = interp1d(templ_gmag - templ_imag, templ_teff, bounds_error=False, fill_value='extrapolate') ######## reading spectra file ######## print(' > reading information from the spectra file') header = fits.open(data_dir + 'spectra/' + file_spectra + '.fits') mangaid, mastarid, starid = header[1].data['mangaid'], header[1].data[ 'mastarid'], header[1].data['id'] plate, ifudesign, mjd = header[1].data['plate'], header[1].data[ 'ifudesign'], header[1].data['mjd'] ra, dec = header[1].data['objra'], header[1].data['objdec'] wave, flux = header[1].data['wave'], header[1].data['flux'] minlogg = header[1].data['minlogg'] bad_info = np.where(minlogg <= -8) minlogg[bad_info] = 'NaN' nspectra = int(header[1].header['naxis2']) naxis1 = int(np.shape(wave)[1]) crval1 = min(wave[0, :]) crval2 = max(wave[0, :]) cdelt1 = abs(crval1 - crval2) / naxis1 new_wave = np.linspace(start=crval1, stop=crval2, num=naxis1) ######## setting up arrays ######## parameters = np.zeros((nspectra, 9)) selected_parameters = np.zeros((nspectra, 3)) ppxf_info = np.zeros((nspectra, 3)) indices = np.zeros((nspectra, 42)) mags = np.zeros((nspectra, 5)) ############################################################################################################################################# ######## running things for each spectrum ######## print(' > working on each spectrum') for i in range(nstart, nend + 1): start_time = clock() new_flux = np.interp(new_wave, wave[i, :], flux[i, :]) new_flux = new_flux * 1e-17 # to have 10^-17 erg/s/cm2/Angstrom as units ######## calculating the E(B-V) from Schlegel+98 as a function of coordinates ######## print(' > calculating the E(B-V) from Schlegel+98') ebv = get_dust_radec(ra[i], dec[i], 'ebv') ######## calculating the reddening as a function of wavelength ######## print(' > calculating the reddening') reddening = dust_allen_py(ebv, new_wave) ######## in order to correct, the flux needs to be divided by the reddening ######## corrected_flux = new_flux / reddening bad_flux = np.isnan(corrected_flux) | np.isinf(corrected_flux) | ( corrected_flux <= 0.0) corrected_flux[bad_flux] = 0.0 ######## calculating the indices ######## print( ' > calculating indices within the MaNGA wavelength range' ) indices[i, :], err_indices = calculate_indices(new_wave, corrected_flux, indices_list, mastarid[i], ef=0, rv=0, plot=False, sim=False) ######## calculating the magnitudes ######## print(' > calculating SDSS magnitudes') mags[i, :] = calculate_magnitudes(new_wave, corrected_flux, filter_list, zero_point, redshift) ######## calculating the first guess for the stellar parameters ######## print( ' > calculating the first set of teff, logg, and metallicity' ) parameters[i, 0] = estimate_teff( mags[i, 1] - mags[i, 3]) # Teff as a function of (g-i) parameters[i, 1] = minlogg[i] # from isoparsfit - isochrone fitting parameters[i, 2] = -1.0 # open metallicity bad_data = np.isnan(parameters[i, 0]) | np.isinf( parameters[i, 0]) | np.isnan(parameters[i, 1]) | np.isinf( parameters[i, 1]) | np.isnan(parameters[i, 2]) | np.isinf( parameters[i, 2]) if bad_data: parameters[i, :] = -999 ppxf_info[i, :] = -999 ##### making a preliminary output file ##### print(' > making a preliminary output file') f = open(output_dir + 'prelim_output/' + mastarid[i] + '_she-ra', 'w') f.write('%r %r %r %i %i %i %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n' \ %(mangaid[i], mastarid[i], starid[i], \ plate[i], ifudesign[i], mjd[i], \ ra[i],dec[i], \ parameters[i,0],parameters[i,1],parameters[i,2], \ parameters[i,3],parameters[i,4],parameters[i,5], \ parameters[i,6],parameters[i,7],parameters[i,8], \ ppxf_info[i,0], ppxf_info[i,1], ppxf_info[i,2], \ mags[i,0], mags[i,1], mags[i,2], mags[i,3], mags[i,4], \ indices[i,0], indices[i,1], indices[i,2], indices[i,3], indices[i,4], \ indices[i,5], indices[i,6], indices[i,7], indices[i,8], indices[i,9], \ indices[i,10], indices[i,11], indices[i,12], indices[i,13], indices[i,14], \ indices[i,15], indices[i,16], indices[i,17], indices[i,18], indices[i,19], \ indices[i,20], indices[i,21], indices[i,22], indices[i,23], indices[i,24], \ indices[i,25], indices[i,26], indices[i,27], indices[i,28], indices[i,29], \ indices[i,30], indices[i,31], indices[i,32], indices[i,33], indices[i,34], \ indices[i,35], indices[i,36], indices[i,37], indices[i,38], indices[i,39], \ indices[i,40], indices[i,41])) f.close() print(' > spectrum ' + str(i) + ' done') continue print(' > Teff = ' + str(parameters[i, 0]) + ' K') print(' > logg = ' + str(parameters[i, 1]) + ' dex') print(' > metal = ' + str(parameters[i, 2]) + ' dex') # not a real prior ######## calculating the second guess for the stellar parameters ######## if (parameters[i, 0] >= 12000): teff_guess = 2000 if (parameters[i, 0] < 12000): teff_guess = 1000 logg_guess = 0.80 # fixed value metal_guess = 2.00 # open metallicity selected_parameters[i, 0] = parameters[i, 0] selected_parameters[i, 1] = parameters[i, 1] selected_parameters[i, 2] = parameters[i, 2] if (parameters[i, 0] <= min(templ_teff)): selected_parameters[i, 0] = min(templ_teff) + teff_guess if (parameters[i, 0] >= max(templ_teff)): selected_parameters[i, 0] = max(templ_teff) - teff_guess if ((parameters[i, 0] >= 12000) & (parameters[i, 1] <= 3.5)): selected_parameters[i, 1] = 3.5 if ((parameters[i, 0] >= 8000) & (parameters[i, 0] <= 12000) & (parameters[i, 1] <= 2.0)): selected_parameters[i, 1] = 2.5 if ((parameters[i, 0] >= 6000) & (parameters[i, 0] <= 8000) & (parameters[i, 1] <= 1.0)): selected_parameters[i, 1] = 1.5 if ((parameters[i, 0] >= 2500) & (parameters[i, 0] <= 6000) & (parameters[i, 1] <= 0.0)): selected_parameters[i, 1] = 0.0 if ((parameters[i, 0] >= 2500) & (parameters[i, 0] <= 12000) & (parameters[i, 1] >= 5.0)): selected_parameters[i, 1] = 5.0 if (parameters[i, 2] <= min(templ_metal)): selected_parameters[i, 2] = min(templ_metal) + metal_guess if (parameters[i, 2] >= max(templ_metal)): selected_parameters[i, 2] = max(templ_metal) - metal_guess ok_set = np.where( (np.abs(templ_teff - selected_parameters[i, 0]) <= teff_guess) & (np.abs(templ_logg - selected_parameters[i, 1]) <= logg_guess) & (np.abs(templ_metal - selected_parameters[i, 2]) <= metal_guess)) templ_set_id = templ_id[ok_set] templ_set_teff = templ_teff[ok_set] templ_set_logg = templ_logg[ok_set] templ_set_metal = templ_metal[ok_set] if not templ_set_id.size: parameters[i, 3] = -999 parameters[i, 4] = -999 parameters[i, 5] = -999 parameters[i, 6] = -999 parameters[i, 7] = -999 parameters[i, 8] = -999 ##### making a preliminary output file ##### print(' > making a preliminary output file') f = open(output_dir + 'prelim_output/' + mastarid[i] + '_she-ra', 'w') f.write('%r %r %r %i %i %i %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n' \ %(mangaid[i], mastarid[i], starid[i], \ plate[i], ifudesign[i], mjd[i], \ ra[i],dec[i], \ parameters[i,0],parameters[i,1],parameters[i,2], \ parameters[i,3],parameters[i,4],parameters[i,5], \ parameters[i,6],parameters[i,7],parameters[i,8], \ ppxf_info[i,0], ppxf_info[i,1], ppxf_info[i,2], \ mags[i,0], mags[i,1], mags[i,2], mags[i,3], mags[i,4], \ indices[i,0], indices[i,1], indices[i,2], indices[i,3], indices[i,4], \ indices[i,5], indices[i,6], indices[i,7], indices[i,8], indices[i,9], \ indices[i,10], indices[i,11], indices[i,12], indices[i,13], indices[i,14], \ indices[i,15], indices[i,16], indices[i,17], indices[i,18], indices[i,19], \ indices[i,20], indices[i,21], indices[i,22], indices[i,23], indices[i,24], \ indices[i,25], indices[i,26], indices[i,27], indices[i,28], indices[i,29], \ indices[i,30], indices[i,31], indices[i,32], indices[i,33], indices[i,34], \ indices[i,35], indices[i,36], indices[i,37], indices[i,38], indices[i,39], \ indices[i,40], indices[i,41])) f.close() print(' > spectrum ' + str(i) + ' done') continue ##### setting up the MaStar for pPXF ##### velscale = c * cdelt1 / max( new_wave) # do not degrade original velocity sampling log_corrected_flux, log_new_wave, velscale = util.log_rebin( [min(new_wave), max(new_wave)], corrected_flux, velscale=velscale) ##### setting up the selected templates ##### templ_set = len(templ_set_id) templates = np.zeros((len(log_corrected_flux), templ_set)) for j in range(0, templ_set): hdu = fits.open(templates_dir + 'grid/' + templ_set_id[j] + '.fits') template_wave = np.linspace(start=hdu[0].header['CRVAL1'], stop=hdu[0].header['CRVAL2'], num=hdu[0].header['NAXIS1']) template_flux = np.interp(new_wave, template_wave, hdu[0].data) bad_flux = np.isnan(template_flux) | np.isinf(template_flux) | ( template_flux <= 0.0) template_flux[bad_flux] = 0.0 log_template_flux, log_template_wave, velscale = util.log_rebin( [min(new_wave), max(new_wave)], template_flux, velscale=velscale) log_template_flux /= np.median(log_template_flux) templates[:, j] = log_template_flux ##### setting up the variables to run pPXF ##### start = [0, 10] noise = np.ones_like(log_corrected_flux) sol = ppxf(templates, log_corrected_flux, noise, velscale, start, lam=np.exp(log_new_wave), degree=10, moments=2, quiet=True) sol.plot() plt.title(mastarid[i]) #plt.show() plt.savefig(output_dir + 'fsf/' + mastarid[i] + '_bestfit.png') plt.close() ##### gathering the chi2, vel and sigma from the pPXF output ##### print( ' > gathering the information from the full-spectrum fitting' ) ppxf_info[i, 0] = sol.chi2 ppxf_info[i, 1] = sol.sol[0] ppxf_info[i, 2] = sol.sol[1] print(' > chi2/dof = ' + str(ppxf_info[i, 0])) print(' > vel = ' + str(ppxf_info[i, 1]) + ' km s-1') print(' > sigma = ' + str(ppxf_info[i, 2]) + ' km s-1') prelim_teff = [] prelim_logg = [] prelim_metal = [] ppxf_weights = sol.weights[:] ##### calculating the weighted parameters ##### for j in range(0, templ_set): prelim_teff.append(sol.weights[j] * templ_set_teff[j] / np.sum(sol.weights[:])) prelim_logg.append(sol.weights[j] * templ_set_logg[j] / np.sum(sol.weights[:])) prelim_metal.append(sol.weights[j] * templ_set_metal[j] / np.sum(sol.weights[:])) parameters[i, 3] = np.sum(prelim_teff[:]) parameters[i, 4] = np.sum(prelim_logg[:]) parameters[i, 5] = np.sum(prelim_metal[:]) + 0.3 bad_data = np.isnan(parameters[i, 3]) | np.isinf( parameters[i, 3]) | np.isnan(parameters[i, 4]) | np.isinf( parameters[i, 4]) | np.isnan(parameters[i, 5]) | np.isinf( parameters[i, 5]) if bad_data: parameters[i, 3] = -999 parameters[i, 4] = -999 parameters[i, 5] = -999 parameters[i, 6] = -999 parameters[i, 7] = -999 parameters[i, 8] = -999 ##### making a preliminary output file ##### print(' > making a preliminary output file') f = open(output_dir + 'prelim_output/' + mastarid[i] + '_she-ra', 'w') f.write('%r %r %r %i %i %i %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n' \ %(mangaid[i], mastarid[i], starid[i], \ plate[i], ifudesign[i], mjd[i], \ ra[i],dec[i], \ parameters[i,0],parameters[i,1],parameters[i,2], \ parameters[i,3],parameters[i,4],parameters[i,5], \ parameters[i,6],parameters[i,7],parameters[i,8], \ ppxf_info[i,0], ppxf_info[i,1], ppxf_info[i,2], \ mags[i,0], mags[i,1], mags[i,2], mags[i,3], mags[i,4], \ indices[i,0], indices[i,1], indices[i,2], indices[i,3], indices[i,4], \ indices[i,5], indices[i,6], indices[i,7], indices[i,8], indices[i,9], \ indices[i,10], indices[i,11], indices[i,12], indices[i,13], indices[i,14], \ indices[i,15], indices[i,16], indices[i,17], indices[i,18], indices[i,19], \ indices[i,20], indices[i,21], indices[i,22], indices[i,23], indices[i,24], \ indices[i,25], indices[i,26], indices[i,27], indices[i,28], indices[i,29], \ indices[i,30], indices[i,31], indices[i,32], indices[i,33], indices[i,34], \ indices[i,35], indices[i,36], indices[i,37], indices[i,38], indices[i,39], \ indices[i,40], indices[i,41])) f.close() print(' > spectrum ' + str(i) + ' done') continue print( ' > calculating the second set of teff, logg, and metallicity' ) print(' > Teff = ' + str(parameters[i, 3]) + ' K') print(' > logg = ' + str(parameters[i, 4]) + ' dex') print(' > metal = ' + str(parameters[i, 5]) + ' dex') ##### gathering the parameters of the template with the largest weight ##### print( ' > calculating the third set of teff, logg, and metallicity' ) ok_heavy = np.where(ppxf_weights == max(ppxf_weights)) parameters[i, 6] = templ_set_teff[ok_heavy] parameters[i, 7] = templ_set_logg[ok_heavy] parameters[i, 8] = templ_set_metal[ok_heavy] + 0.3 print(' > Teff = ' + str(parameters[i, 6]) + ' K') print(' > logg = ' + str(parameters[i, 7]) + ' dex') print(' > metal = ' + str(parameters[i, 8]) + ' dex') print(' > elapsed time for one MaStar %.2f s' % (clock() - start_time)) ##### making a preliminary output file ##### print(' > making a preliminary output file') f = open(output_dir + 'prelim_output/' + mastarid[i] + '_she-ra', 'w') f.write('%r %r %r %i %i %i %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n' \ %(mangaid[i], mastarid[i], starid[i], \ plate[i], ifudesign[i], mjd[i], \ ra[i],dec[i], \ parameters[i,0],parameters[i,1],parameters[i,2], \ parameters[i,3],parameters[i,4],parameters[i,5], \ parameters[i,6],parameters[i,7],parameters[i,8], \ ppxf_info[i,0], ppxf_info[i,1], ppxf_info[i,2], \ mags[i,0], mags[i,1], mags[i,2], mags[i,3], mags[i,4], \ indices[i,0], indices[i,1], indices[i,2], indices[i,3], indices[i,4], \ indices[i,5], indices[i,6], indices[i,7], indices[i,8], indices[i,9], \ indices[i,10], indices[i,11], indices[i,12], indices[i,13], indices[i,14], \ indices[i,15], indices[i,16], indices[i,17], indices[i,18], indices[i,19], \ indices[i,20], indices[i,21], indices[i,22], indices[i,23], indices[i,24], \ indices[i,25], indices[i,26], indices[i,27], indices[i,28], indices[i,29], \ indices[i,30], indices[i,31], indices[i,32], indices[i,33], indices[i,34], \ indices[i,35], indices[i,36], indices[i,37], indices[i,38], indices[i,39], \ indices[i,40], indices[i,41])) f.close() print(' > spectrum ' + str(i) + ' done') #sys.stdout.write(" > spectrum = %d %s \r" % (i+1,'done')) #sys.stdout.flush() print(' > all spectra done') print(' > elapsed time for all MaStar %.2f s' % (clock() - full_time))
#redshift = 2472. #ngc772 #redshift = 1485. #ngc1022 #redshift = 1032. #vcc784 #redshift = 674. #vcc1146 redshift = 401. #vcc1619 #redshift = 1359 #VCC0355 start = [redshift,10.] #(km/s), starting guess for [V,sigma] specdir='/Users/nluetzge/Science/UCDs/' #----------------------------------Read all files----------------------------------------- # Read the combined spectrum hdu = fits.open(name+'_spec.fits') gal=hdu[0].data hdr=hdu[0].header lamRange1=hdr['CRVAL1'] + np.array([0.,hdr['CDELT1']*(hdr['NAXIS1']-1.)]) galaxy, logLam1, velscale = util.log_rebin(lamRange1, gal) # Read the template if make_temp: temp_files = glob(specdir+Z+'/nifstemp*fits') nfiles=len(temp_files) for i in range(nfiles): hdu = fits.open(temp_files[i]) h_template=hdu[0].header temp_lin=hdu[0].data # the wavelength range calculated from the header data naxis=h_template['NAXIS1'] lamRange2=h_template['CRVAL1'] + np.array([0.,h_template['CDELT1']*(h_template['NAXIS1']-1.)]) tt, logLam2,velscale = util.log_rebin(lamRange2, temp_lin, velscale=velscale)
def ppxf_simulation_example(): dir = 'spectra/' file = dir + 'Rbi1.30z+0.00t12.59.fits' hdu = pyfits.open(file) ssp = hdu[0].data h = hdu[0].header lamRange = h['CRVAL1'] + np.array([0., h['CDELT1'] * (h['NAXIS1'] - 1)]) star, logLam, velscale = util.log_rebin(lamRange, ssp) # 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=1) # 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 vel = 0.3 # velocity in *pixels* [=V(km/s)/velScale] h3 = 0.1 # Adopted G-H parameters of the LOSVD h4 = -0.1 sn = 60. # Adopted S/N of the Monte Carlo simulation m = 300 # Number of realizations of the simulation sigmaV = np.linspace( 0.8, 4, m) # Range of sigma in *pixels* [=sigma(km/s)/velScale] result = np.zeros((m, 4)) # This will store the results t = clock() np.random.seed(123) # for reproducible results for j in range(m): sigma = sigmaV[j] dx = int( abs(vel) + 4.0 * sigma) # Sample the Gaussian and GH at least to vel+4*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) / (np.sqrt(2. * np.pi) * sigma * factor ) # 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.random(), sigma * np.random.uniform(0.85, 1.15) ]) * velscale # Convert to km/s pp = ppxf(star, galaxy, noise, velscale, start, goodpixels=np.arange(dx, galaxy.size - dx), plot=False, moments=4, bias=0.5) result[j, :] = pp.sol print('Calculation time: %.2f s' % (clock() - t)) plt.clf() plt.subplot(221) plt.plot(sigmaV * velscale, result[:, 0] - vel * velscale, '+k') plt.plot(sigmaV * velscale, sigmaV * velscale * 0, '-r') plt.ylim(-40, 40) plt.xlabel('$\sigma_{in}\ (km\ s^{-1})$') plt.ylabel('$V - V_{in}\ (km\ s^{-1}$)') plt.subplot(222) plt.plot(sigmaV * velscale, result[:, 1] - sigmaV * velscale, '+k') plt.plot(sigmaV * velscale, sigmaV * velscale * 0, '-r') plt.ylim(-40, 40) plt.xlabel('$\sigma_{in}\ (km\ s^{-1})$') plt.ylabel('$\sigma - \sigma_{in}\ (km\ s^{-1}$)') plt.subplot(223) plt.plot(sigmaV * velscale, result[:, 2], '+k') plt.plot(sigmaV * velscale, sigmaV * velscale * 0 + h3, '-r') plt.ylim(-0.2 + h3, 0.2 + h3) plt.xlabel('$\sigma_{in}\ (km\ s^{-1})$') plt.ylabel('$h_3$') plt.subplot(224) plt.plot(sigmaV * velscale, result[:, 3], '+k') plt.plot(sigmaV * velscale, sigmaV * velscale * 0 + h4, '-r') plt.ylim(-0.2 + h4, 0.2 + h4) plt.xlabel('$\sigma_{in}\ (km\ s^{-1})$') plt.ylabel('$h_4$') plt.tight_layout() plt.pause(0.01)
def ppxf_simulation_example(): hdu = fits.open('miles_models/Mun1.30Zp0.00T12.5893.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 np.random.seed(133) # for reproducible results 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) + 4.0 * sigma) # Sample the Gaussian and GH at least to vel+4*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.random(), sigma * np.random.uniform(0.85, 1.15), 0, 0 ]) * 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.3, oversample=None) result[j, :] = pp.sol print('Calculation time: %.2f s' % (clock() - t)) plt.clf() plt.subplot(221) plt.plot(sigmaV * velscale, (result[:, 0] / velscale - velV) / sigmaV, '+k') plt.axhline(0, color='r') plt.axvline(velscale, linestyle='dashed') plt.axvline(2 * velscale, linestyle='dashed') plt.ylim(-0.3, 0.3) plt.xlabel(r'$\sigma_{\rm in}\ (km\ s^{-1})$') plt.ylabel(r'$(V - V_{\rm in})/\sigma_{\rm in}$') plt.text(2.05 * velscale, -0.2, r'2$\times$velscale') plt.subplot(222) plt.plot(sigmaV * velscale, np.log10(result[:, 1] / (velscale * sigmaV)), '+k') plt.axhline(0, color='r') plt.axvline(velscale, linestyle='dashed') plt.axvline(2 * velscale, linestyle='dashed') plt.ylim(-0.15, 0.15) plt.xlabel(r'$\sigma_{in}\ (km\ s^{-1})$') plt.ylabel(r'$\log(\sigma/\sigma_{\rm in})$') plt.text(2.05 * velscale, -0.1, 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.15 + h3, 0.15 + h3) plt.xlabel(r'$\sigma_{\rm in}\ (km\ s^{-1})$') plt.ylabel('$h_3$') plt.text(2.05 * velscale, h3 - 0.1, 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.15 + h4, 0.15 + h4) plt.xlabel(r'$\sigma_{\rm in}\ (km\ s^{-1})$') plt.ylabel('$h_4$') plt.text(2.05 * velscale, h4 - 0.1, r'2$\times$velscale') plt.tight_layout() plt.pause(0.01)
def ppxf_kinematics_example_sauron(): # Read a galaxy spectrum and define the wavelength range # dir = 'spectra/' file = dir + 'NGC4550_SAURON.fits' hdu = pyfits.open(file) gal_lin = hdu[0].data h1 = hdu[0].header lamRange1 = h1['CRVAL1'] + np.array([0.,h1['CDELT1']*(h1['NAXIS1']-1)]) FWHM_gal = 4.2 # SAURON has an instrumental resolution FWHM of 4.2A. # If the galaxy is at a significant redshift (z > 0.03), one would need to apply # a large velocity shift in PPXF to match the template to the galaxy spectrum. # This would require a large initial value for the velocity (V > 1e4 km/s) # in the input parameter START = [V,sig]. This can cause PPXF to stop! # The solution consists of bringing the galaxy spectrum roughly to the # rest-frame wavelength, before calling PPXF. In practice there is no # need to modify the spectrum before the usual LOG_REBIN, given that a # red shift corresponds to a linear shift of the log-rebinned spectrum. # One just needs to compute the wavelength range in the rest-frame # and adjust the instrumental resolution of the galaxy observations. # This is done with the following three commented lines: # # z = 1.23 # Initial estimate of the galaxy redshift # lamRange1 = lamRange1/(1+z) # Compute approximate restframe wavelength range # FWHM_gal = FWHM_gal/(1+z) # Adjust resolution in Angstrom galaxy, logLam1, velscale = util.log_rebin(lamRange1, gal_lin) galaxy = galaxy/np.median(galaxy) # Normalize spectrum to avoid numerical issues noise = galaxy*0 + 0.0049 # Assume constant noise per pixel here # Read the list of filenames from the Single Stellar Population library # by Vazdekis (1999, ApJ, 513, 224). A subset of the library is included # for this example with permission. See http://purl.org/cappellari/software # for suggestions of more up-to-date stellar libraries. # vazdekis = glob.glob(dir + 'Rbi1.30z*.fits') vazdekis.sort() FWHM_tem = 1.8 # Vazdekis spectra have a resolution FWHM of 1.8A. # 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 = pyfits.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 = util.log_rebin(lamRange2, ssp, velscale=velscale) 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 in range(len(vazdekis)): hdu = pyfits.open(vazdekis[j]) ssp = hdu[0].data ssp = ndimage.gaussian_filter1d(ssp,sigma) sspNew, logLam2, velscale = util.log_rebin(lamRange2, ssp, velscale=velscale) 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 = (logLam2[0]-logLam1[0])*c # km/s vel = 450. # Initial estimate of the galaxy velocity in km/s z = np.exp(vel/c) - 1 # Relation between velocity and redshift in pPXF goodPixels = util.determine_goodpixels(logLam1, lamRange2, z) # Here the actual fit starts. The best fit is plotted on the screen. # Gas emission lines are excluded from the pPXF fit using the GOODPIXELS keyword. # start = [vel, 180.] # (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) 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))
def __init__(self, pathname, velscale, FWHM_gal, metal=None, FWHM_tem=2.51, normalize=False, max_age=None): """ Produces an array of logarithmically-binned templates by reading the spectra from the Single Stellar Population (SSP) library by Vazdekis et al. (2010, MNRAS, 404, 1639) http://miles.iac.es/. The code checks that the model specctra form a rectangular grid in age and metallicity and properly sorts them in both parameters. The code also returns the age and metallicity of each template by reading these parameters directly from the file names. The templates are broadened by a Gaussian with dispersion sigma_diff = np.sqrt(sigma_gal**2 - sigma_tem**2). Thie script relies on the files naming convention adopted by the MILES library, where SSP spectra have the form below Mun1.30Zm0.40T00.0794_iPp0.00_baseFe_linear_FWHM_2.51.fits This code can be easily adapted by the users to deal with other stellar libraries, different IMFs or different abundances. :param pathname: path with wildcards returning the list files to use (e.g. 'miles_models/Mun1.30*.fits'). The files must form a Cartesian grid in age and metallicity and the procedure returns an error if they do not. :param velscale: desired velocity scale for the output templates library in km/s (e.g. 60). This is generally the same or an integer fraction of the velscale of the galaxy spectrum. :param FWHM_gal: vector or scalar with the FWHM of the instrumental resolution of the galaxy spectrum in Angstrom. :param normalize: set to True to normalize each template to mean=1. This is useful to compute light-weighted stellar population quantities. :param max_age: optional maximum age in Gyr for the MILES models. This can be useful e.g. to limit the templates age to be younger than the age of the Universe at a given redshift. :param metal: optionally select a *single* metallicity [M/H] (e.g. metal = 0 to select only the spectra with Solar metallicity). :return: The following variables are stored as attributes of the miles class: .templates: array has dimensions templates[npixels, n_ages, n_metals]; .log_lam_temp: natural np.log() wavelength of every pixel npixels; .age_grid: (Gyr) has dimensions age_grid[n_ages, n_metals]; .metal_grid: [M/H] has dimensions metal_grid[n_ages, n_metals]. .n_ages: number of different ages .n_metal: number of different metallicities """ files = glob.glob(pathname) assert len(files) > 0, "Files not found %s" % pathname all = [age_metal(f) for f in files] all_ages, all_metals = np.array(all).T ages, metals = np.unique(all_ages), np.unique(all_metals) n_ages, n_metal = len(ages), len(metals) assert set(all) == set([(a, b) for a in ages for b in metals]), \ 'Ages and Metals do not form a Cartesian grid' # 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(files[0]) ssp = hdu[0].data h2 = hdu[0].header lam_range_temp = h2['CRVAL1'] + np.array( [0, h2['CDELT1'] * (h2['NAXIS1'] - 1)]) sspNew, log_lam_temp = util.log_rebin(lam_range_temp, ssp, velscale=velscale)[:2] templates = np.empty((sspNew.size, n_ages, n_metal)) age_grid = np.empty((n_ages, n_metal)) metal_grid = np.empty((n_ages, n_metal)) # Convolve the whole Vazdekis library of spectral templates # with the quadratic difference between the galaxy and the # Vazdekis instrumental resolution. Logarithmically rebin # and store each template as a column in the array TEMPLATES. # Quadratic sigma difference in pixels Vazdekis --> galaxy # 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 # Here we make sure the spectra are sorted in both [M/H] and Age # along the two axes of the rectangular grid of templates. for j, age in enumerate(ages): for k, met in enumerate(metals): p = all.index((age, met)) hdu = fits.open(files[p]) ssp = hdu[0].data if np.isscalar(FWHM_gal): if sigma > 0.1: # Skip convolution for nearly zero sigma ssp = ndimage.gaussian_filter1d(ssp, sigma) else: ssp = util.gaussian_filter1d( ssp, sigma) # convolution with variable sigma sspNew = util.log_rebin(lam_range_temp, ssp, velscale=velscale)[0] if normalize: sspNew /= np.mean(sspNew) templates[:, j, k] = sspNew age_grid[j, k] = age metal_grid[j, k] = met if max_age is not None: w = age_grid[:, 0] <= max_age templates = templates[:, w, :] age_grid = age_grid[w, :] metal_grid = metal_grid[w, :] n_ages, n_metal = age_grid.shape if metal is not None: w = metal_grid[0, :] == metal templates = np.squeeze(templates[:, :, w]) age_grid = np.squeeze(age_grid[:, w]) metal_grid = np.squeeze(metal_grid[:, w]) n_ages, n_metal = age_grid.size, 1 self.norm = np.median(templates) self.templates = templates / self.norm # Normalize by a scalar self.log_lam_temp = log_lam_temp self.age_grid = age_grid self.metal_grid = metal_grid self.n_ages = n_ages self.n_metal = n_metal
def __init__(self, galaxy='ngc3557', slit_h=4.5, slit_w=2, slit_pa=30, method='Rampazzo_aperture', r1=0, r2=None, debug=False): print galaxy if 'Rampazzo' in method: from Rampazzo import rampazzo # Default is nuclear region: 0<r<R_e/16 if r2 is None: r2 = get_R_e(galaxy)/16 data = rampazzo(galaxy, method='aperture', slit_h=slit_h, r1=r1, r2=r2, debug=debug) if 'gradient' in method: if '2' in method: data.method = 'gradient2' else: data.method = 'gradient' elif method == 'Ogando': from Ogando import ogando data = ogando(galaxy, debug=debug, slit_h=slit_h, slit_w=slit_w, slit_pa=slit_pa) elif method == 'Miles': from Miles import miles data = miles(galaxy) gal_spec, gal_noise = data.get_spec() gal_spec, lam, cut = apply_range(gal_spec, window=201, repeats=3, lam=data.lam, set_range=np.array([4200,10000]), return_cuts=True) gal_noise = gal_noise[cut] lamRange = np.array([lam[0],lam[-1]])/(1+data.z) ## ----------================= Templates ====================--------- FWHM_gal = 2.5 # VIMOS documentation (and fits header) FWHM_gal = FWHM_gal/(1+data.z) # Adjust resolution in Angstrom stellar_templates = get_stellar_templates(galaxy, FWHM_gal) velscale = stellar_templates.velscale e_templates = get_emission_templates(gas, lamRange, stellar_templates.logLam_template, FWHM_gal) if gas: templates = np.column_stack((stellar_templates.templates, e_templates.templates)) else: templates = stellar_templates.templates component = [0]*len(stellar_templates.templatesToUse) + e_templates.component templatesToUse = np.append(stellar_templates.templatesToUse, e_templates.templatesToUse) element = ['stellar'] + e_templates.element start = [[data.vel, data.sig]] * (max(component) + 1) moments = [stellar_moments] + [gas_moments] * max(component) ## ----------============== Final calibrations ==============--------- ## smooth spectrum to fit with templates resolution if FWHM_gal < stellar_templates.FWHM_tem: sigma = stellar_templates.FWHM_dif/2.355/data.f[0].header['CDELT3'] gal_spec = ndimage.gaussian_filter1d(gal_spec, sigma) gal_noise = np.sqrt(ndimage.gaussian_filter1d(gal_noise**2, sigma)) ## rebin spectrum logarthmically gal_spec_log, logLam_bin, _ = util.log_rebin(lamRange, gal_spec, velscale=velscale) gal_noise_log, logLam_bin, _ = util.log_rebin(lamRange, gal_noise**2, velscale=velscale) gal_noise_log = np.sqrt(gal_noise_log) gal_noise_log = gal_noise_log + 0.0000000000001 dv = (stellar_templates.logLam_template[0]-logLam_bin[0])*c # km/s # Find the pixels to ignore to avoid being distracted by gas emission #; lines or atmospheric absorbsion line. goodPixels = determine_goodpixels(logLam_bin,stellar_templates.lamRange_template, data.vel, data.z, gas=gas!=0) lambdaq = np.exp(logLam_bin) ## ----------=================== pPXF =======================--------- pp = ppxf(templates, gal_spec_log, gal_noise_log, velscale, start, goodpixels=goodPixels, moments=moments, degree=-1, vsyst=dv, component=component, lam=lambdaq, plot=not quiet, quiet=quiet, mdegree=10) self.pp = pp # Only use stellar templates (Remove emission lines) stellar_spec = gal_spec_log - \ pp.matrix[:, -e_templates.ntemp:].dot(pp.weights[-e_templates.ntemp:]) conv_spec = pp.matrix[:, :-e_templates.ntemp].dot(pp.weights[:-e_templates.ntemp]) # Generate the unconvolved spectra ('0 km/s' resolution) unc_lam = stellar_templates.wav unc_spec = stellar_templates.lin_templates.dot(pp.weights[:stellar_templates.ntemp]) unc_spec *= np.polynomial.legendre.legval(np.linspace(-1,1, len(unc_spec)), np.append(1, pp.mpolyweights)) ## ----------============== Absorption Line =================--------- lines = ['G4300', 'Fe4383', 'Ca4455', 'Fe4531', 'H_beta', 'Fe5015', 'Mg_b'] self.result = {} self.uncert = {} for line in lines: ab, ab_uncert = absorption(line, lambdaq, stellar_spec, noise=gal_noise_log, unc_lam=unc_lam, unc_spec=unc_spec, conv_spec=conv_spec)#, #lick=True) if method == 'Ogando': # Aperture correction ab_file = '%s/Documents/useful_files/ab_linelist.dat' % (cc.home_dir) i1, i2, b1, b2, r1, r2, units = np.genfromtxt(ab_file, unpack=True, usecols=(1,2,3,4,5,6,7), skip_header=2, skip_footer=2) ls = np.genfromtxt(ab_file, unpack=True, dtype=str, usecols=(8), skip_header=2, skip_footer=2) l = np.where(ls == line)[0][0] index = [i1[l], i2[l]] # Convert to mag ab = -2.5 * np.log(1 - ab/(index[1]-index[0])) # Aperture Correction: beta values taken from paper beta = {'H_beta':0.002, 'Fe5015':-0.012, 'Mg_b':-0.031} # If line is not observed by Ogando if line not in beta.keys(): self.result[line] = np.nan self.uncert[line] = np.nan continue H = 70.0 # value used by Bolonga group. r_ab = np.degrees(1.19/1000 * H/(c * data.z)) * 60 * 60 # 1.19 kpc -> arcsec # Correction is def in eq (9) with r_ab and r_norm def in eq (1) - not # 100% sure if I've got them correct. ab = ab - beta[line] * np.log(1.025 * np.sqrt(slit_w*r_ab/np.pi)/slit_h) # Back to Angstroms ab = (index[1]-index[0]) * (1 - np.exp(ab/-2.5)) elif 'Rampazzo' in method: self.r = data.r self.result[line] = ab self.uncert[line] = ab_uncert if debug: for line in lines: print '%s: %.3f +/- %.3f' % (line, self.result[line], self.uncert[line])
def ppxf_two_components_example(): hdu = fits.open('miles_models/Mun1.30Zp0.00T12.5893.fits') # Solar metallicitly, Age=12.59 Gyr gal_lin = hdu[0].data h1 = hdu[0].header lamRange1 = h1['CRVAL1'] + np.array([0., h1['CDELT1']*(h1['NAXIS1']-1)]) c = 299792.458 # speed of light in km/s velscale = c*h1['CDELT1']/max(lamRange1) # Do not degrade original velocity sampling model1, logLam1, velscale = util.log_rebin(lamRange1, gal_lin, velscale=velscale) model1 /= np.median(model1) hdu = fits.open('miles_models/Mun1.30Zp0.00T01.0000.fits') # Solar metallicitly, Age=1.00 Gyr gal_lin = hdu[0].data model2, logLam1, velscale = util.log_rebin(lamRange1, gal_lin, velscale=velscale) model2 /= np.median(model2) model = np.column_stack([model1, model2]) galaxy = np.empty_like(model) # These are the input values in spectral pixels # for the (V,sigma) of the two kinematic components # vel = np.array([0., 250.])/velscale sigma = np.array([200., 100.])/velscale # The synthetic galaxy model consists of the sum of two # SSP spectra with age of 1Gyr and 13Gyr respectively # with different velocity and dispersion # for j in range(len(vel)): dx = int(abs(vel[j]) + 4.*sigma[j]) # Sample the Gaussian at least to vel+4*sigma v = np.linspace(-dx, dx, 2*dx + 1) losvd = np.exp(-0.5*((v - vel[j])/sigma[j])**2) # Gaussian LOSVD losvd /= np.sum(losvd) # normaize LOSVD galaxy[:, j] = signal.fftconvolve(model[:, j], losvd, mode="same") galaxy[:, j] /= np.median(model[:, j]) galaxy = np.sum(galaxy, axis=1) sn = 100. np.random.seed(2) # Ensure reproducible results noise = galaxy/sn galaxy = np.random.normal(galaxy, noise) # add noise to galaxy # Adopts two templates per kinematic component # templates = np.column_stack([model1, model2, model1, model2]) # Start both kinematic components from the same guess. # With multiple stellar kinematic components # a good starting guess is essential # start = [np.mean(vel)*velscale, np.mean(sigma)*velscale] start = [start, start] goodPixels = np.arange(20, 6000) t = clock() plt.clf() plt.subplot(211) plt.title("Two components pPXF fit") print("+++++++++++++++++++++++++++++++++++++++++++++") pp = ppxf(templates, galaxy, noise, velscale, start, goodpixels=goodPixels, plot=True, degree=4, moments=[2, 2], component=[0, 0, 1, 1]) plt.subplot(212) plt.title("Single component pPXF fit") print("---------------------------------------------") start = start[0] pp = ppxf(templates, galaxy, noise, velscale, start, goodpixels=goodPixels, plot=True, degree=4, moments=2) plt.tight_layout() plt.pause(0.01) print("=============================================") print("Total elapsed time %.2f s" % (clock() - t))
for ii in numpy.arange(len(filenames)): print ii, "/", len(filenames) wave, gal_lin, variance_lin, pix = numpy.genfromtxt(filenames[ii], unpack = True, skiprows = 1) Spaxel_coordinates = findCoords_from_string(filenames[ii]) # text = "Blue_Spec_"+str(int(Spaxel_coordinates[0]))+"_"+str(int(Spaxel_coordinates[1])) # noise_lin = numpy.sqrt(variance_lin) out_range = numpy.where(numpy.logical_or(wave< 4020., wave > 5550.)) wave_sel = numpy.delete(wave, out_range) gal_lin_sel = numpy.delete(gal_lin, out_range) noise_lin_sel = numpy.delete(noise_lin, out_range) lamRange1 = numpy.array([wave_sel[0], wave_sel[-1]]) # galaxy, logLam1, velscale = util.log_rebin(lamRange1, gal_lin_sel) fac = numpy.median(galaxy) galaxy = galaxy / fac# Normalize spectrum to avoid numerical issues error, logLam1, velscale = util.log_rebin(lamRange1, noise_lin_sel**2) noise = numpy.sqrt(error) noise = noise / fac # # logLam2, fl_temp = numpy.genfromtxt(directory + 'wise_template.dat', unpack = True, skiprows = 1) wave_temp = numpy.exp(logLam2) lamRange2 = numpy.array([logLam2[0], logLam2[-1]]) # templates = fl_temp # c = 299792.458 dv = (logLam2[0]-logLam1[0])*c # km/s
def ppxf_kinematics_example_sdss(): # 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 = 'spectra/NGC4636_SDSS_DR12.fits' hdu = pyfits.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 = galaxy*0 + 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 # Constant velocity scale in km/s per pixel # If the galaxy is at a significant redshift (z > 0.03), one would need to apply # a large velocity shift in PPXF to match the template to the galaxy spectrum. # This would require a large initial value for the velocity (V > 1e4 km/s) # in the input parameter START = [V,sig]. This can cause PPXF to stop! # The solution consists of bringing the galaxy spectrum roughly to the # rest-frame wavelength, before calling PPXF. In practice there is no # need to modify the spectrum 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('miles_models/Mun1.30Z*.fits') vazdekis.sort() 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 = pyfits.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, logLam2, velscale = util.log_rebin(lamRange_temp, ssp, velscale=velscale) 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 = pyfits.open(fname) ssp = hdu[0].data ssp = util.gaussian_filter1d(ssp, sigma) # perform convolution with variable sigma sspNew, logLam2, velscale = util.log_rebin(lamRange_temp, ssp, velscale=velscale) 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) # Initial estimate of the galaxy velocity in km/s 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) 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))
def ppxf_example_sky_and_symmetric_losvd(): file_dir = path.dirname(path.realpath(__file__)) # path of this procedure # Solar metallicity, Age=12.59 Gyr hdu = fits.open(file_dir + '/miles_models/Mun1.30Zp0.00T12.5893.fits') ssp = hdu[0].data h = hdu[0].header lamRange = h['CRVAL1'] + np.array([0., h['CDELT1'] * (h['NAXIS1'] - 1)]) velscale = 70. # km/s star, logLam, velscale = util.log_rebin(lamRange, ssp, velscale=velscale) star /= np.mean(star) # Adopted input parameters ================================================= vel = 200. / velscale # Velocity of 1st spectrum in pixels (2nd has -vel) sigma = 300. / velscale # Dispersion of both spectra in pixels h3 = 0.1 # h3 of 1st spectrum (2nd has -h3) h4 = 0.1 sn = 40. moments = 4 deg = 4 vshift = 10 # Adopted systemic velocity in pixels vsyst = vshift * velscale # Adopted systemic velocity in km/s # Generate input Sky ======================================================= # For illustration, the sky is modelled as two Gaussian emission lines n = star.size x = np.arange(n) sky1 = np.exp(-0.5 * (x - 1000)**2 / 100) sky2 = np.exp(-0.5 * (x - 2000)**2 / 100) # Generate input LOSVD ===================================================== dx = int(abs(vel) + 5 * sigma) v = np.linspace(-dx, dx, 2 * dx + 1) w = (v - vel) / sigma w2 = w**2 gauss = np.exp(-0.5 * w2) gauss /= np.sum(gauss) h3poly = w * (2 * w2 - 3) / np.sqrt(3) h4poly = (w2 * (4 * w2 - 12) + 3) / np.sqrt(24) losvd = gauss * (1 + h3 * h3poly + h4 * h4poly) # Generate first synthetic spectrum ======================================== # The template is convolved with the LOSVD x = np.linspace(-1, 1, n) galaxy1 = signal.fftconvolve(star, losvd, mode="same") galaxy1 = np.roll(galaxy1, vshift) # Mimic nonzero systemic velocity galaxy1 *= legendre.legval( x, np.append(1, np.random.uniform(-0.1, 0.1, deg - 1))) # Multiplicative polynomials galaxy1 += legendre.legval(x, np.random.uniform(-0.1, 0.1, deg)) # Additive polynomials galaxy1 += sky1 + 2 * sky2 # Add two sky lines galaxy1 = np.random.normal(galaxy1, 1 / sn) # Add noise # Generate symmetric synthetic spectrum ==================================== # The same template is convolved with a reversed LOSVD # and different polynomials and sky lines are included galaxy2 = signal.fftconvolve(star, np.flip(losvd, 0), mode="same") galaxy2 = np.roll(galaxy2, vshift) # Mimic nonzero systemic velocity galaxy2 *= legendre.legval( x, np.append(1, np.random.uniform(-0.1, 0.1, deg - 1))) # Multiplicative polynomials galaxy2 += legendre.legval(x, np.random.uniform(-0.1, 0.1, deg)) # Additive polynomials galaxy2 += 2 * sky1 + sky2 # Add two sky lines galaxy2 = np.random.normal(galaxy2, 1 / sn) # Add noise # Load spectral templates ================================================== vazdekis = glob.glob(file_dir + '/miles_models/Mun1.30Z*.fits') templates = np.empty((n, len(vazdekis))) for j, file in enumerate(vazdekis): hdu = fits.open(file) ssp = hdu[0].data sspNew, logLam2, velscale = util.log_rebin(lamRange, ssp, velscale=velscale) templates[:, j] = sspNew / np.median(sspNew) # Normalize templates # Do the fit =============================================================== # Input both galaxy spectra simultaneously to pPXF galaxy = np.column_stack([galaxy1, galaxy2]) # Use two sky templates for each galaxy spectrum sky = np.column_stack([sky1, sky2]) # Randomized starting guess vel0 = vel + np.random.uniform(-1, 1) sigma0 = sigma * np.random.uniform(0.8, 1.2) start = np.array([vel0, sigma0]) * velscale # Convert to km/s goodpixels = np.arange(50, n - 50) print( "\nThe input values are: Vel=%0.0f, sigma=%0.0f, h3=%0.1f, h4=%0.1f\n" % (vel * velscale, sigma * velscale, h3, h4)) t = clock() pp = ppxf(templates, galaxy, np.full_like(galaxy, 1 / sn), velscale, start, goodpixels=goodpixels, plot=1, moments=moments, vsyst=vsyst, mdegree=deg, degree=deg, sky=sky) print('Elapsed time in pPXF: %.2f s' % (clock() - t)) plt.pause(1)
def ppxf_kinematics_example_sauron(): # Read a galaxy spectrum and define the wavelength range # file = 'spectra/NGC4550_SAURON.fits' hdu = fits.open(file) gal_lin = hdu[0].data h1 = hdu[0].header lamRange1 = h1['CRVAL1'] + np.array( [0., h1['CDELT1'] * (h1['NAXIS1'] - 1)]) FWHM_gal = 4.2 # SAURON has an instrumental resolution FWHM of 4.2A. # If the galaxy is at a significant redshift (z > 0.03), one would need to apply # a large velocity shift in PPXF to match the template to the galaxy spectrum. # This would require a large initial value for the velocity (V > 1e4 km/s) # in the input parameter START = [V,sig]. This can cause PPXF to stop! # The solution consists of bringing the galaxy spectrum roughly to the # rest-frame wavelength, before calling PPXF. In practice there is no # need to modify the spectrum before the usual LOG_REBIN, given that a # red shift corresponds to a linear shift of the log-rebinned spectrum. # One just needs to compute the wavelength range in the rest-frame # and adjust the instrumental resolution of the galaxy observations. # This is done with the following three commented lines: # # z = 1.23 # Initial estimate of the galaxy redshift # lamRange1 = lamRange1/(1+z) # Compute approximate restframe wavelength range # FWHM_gal = FWHM_gal/(1+z) # Adjust resolution in Angstrom galaxy, logLam1, velscale = util.log_rebin(lamRange1, gal_lin) galaxy = galaxy / np.median( galaxy) # Normalize spectrum to avoid numerical issues noise = galaxy * 0 + 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('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 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 = util.log_rebin(lamRange2, ssp, velscale=velscale) 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 = util.log_rebin(lamRange2, ssp, velscale=velscale) 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 = (logLam2[0] - logLam1[0]) * c # km/s vel = 450. # Initial estimate of the galaxy velocity in km/s z = np.exp(vel / c) - 1 # Relation between velocity and redshift in pPXF goodPixels = util.determine_goodpixels(logLam1, lamRange2, z) # Here the actual fit starts. The best fit is plotted on the screen. # Gas emission lines are excluded from the pPXF fit using the GOODPIXELS keyword. # start = [vel, 180., 0, 0] # (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) 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))
def run_ppxf(spectra, velscale, ncomp=2, has_emission=True, mdegree=-1, degree=20, pkls=None, plot=False, data_sky=None): """ Run pPXF in a list of spectra""" if isinstance(spectra, str): spectra = [spectra] if isinstance(pkls, str): pkls = [pkls] if pkls == None: pkls = [x.replace(".fits", ".pkl") for x in spectra] ########################################################################## # Load templates for both stars and gas star_templates, logLam2, delta, miles = stellar_templates(velscale) gas_templates, logLam_gas, delta_gas, gas_files = emission_templates( velscale) ########################################################################## # Join templates in case emission lines are used. if has_emission: templates = np.column_stack((star_templates, gas_templates)) else: templates = star_templates ########################################################################## if ncomp == 1: components = 0 moments = [4] templates_names = miles elif ncomp == 2: components = np.hstack( (np.zeros(len(star_templates[0])), np.ones(len(gas_templates[0])))) moments = [4, 2] templates_names = np.hstack((miles, gas_files)) else: raise Exception("ncomp has to be 1 or 2.") for i, spec in enumerate(spectra): print "pPXF run of spectrum {0} ({1} of {2})".format( spec, i + 1, len(spectra)) pkl = pkls[i] ###################################################################### # Read one galaxy spectrum and define the wavelength range specfile = os.path.join(data_dir, spec) hdu = pf.open(specfile) spec_lin = hdu[0].data h1 = pf.getheader(specfile) lamRange1 = h1['CRVAL1'] + np.array( [0., h1['CDELT1'] * (h1['NAXIS1'] - 1)]) ###################################################################### # Degrade observed spectra to match template resolution FWHM_dif = np.sqrt(FWHM_tem**2 - FWHM_spec**2) sigma = FWHM_dif / 2.355 / delta # Sigma difference in pixels spec_lin = ndimage.gaussian_filter1d(spec_lin, sigma) ###################################################################### # Rebin to log scale galaxy, logLam1, velscale = util.log_rebin(lamRange1, spec_lin, velscale=velscale) ###################################################################### # First guess for the noise noise = np.ones_like(galaxy) * np.std(galaxy - medfilt(galaxy, 5)) ###################################################################### # Calculate difference of velocity between spectrum and templates # due to different initial wavelength dv = (logLam2[0] - logLam1[0]) * c ###################################################################### # Set first guess from setup files start, goodPixels = read_setup_file(spec, logLam1, mask_emline=False) ###################################################################### # Expand start variable to include multiple components if ncomp > 1: start = [start, [start[0], 30]] ###################################################################### # Read sky in needed if data_sky == None: sky = None else: sky_lin = pf.getdata(data_sky[i]) sky_lin = ndimage.gaussian_filter1d(sky_lin, sigma) sky, logLam1, velscale = util.log_rebin(lamRange1, sky_lin, velscale=velscale) sky = sky.reshape(-1, 1) ###################################################################### # First pPXF interaction if os.path.exists(spec.replace(".fits", ".pkl")): pp0 = pPXF(spec, velscale, pklfile=spec.replace(".fits", ".pkl")) noise0 = pp0.noise else: pp0 = ppxf(templates, galaxy, noise, velscale, start, goodpixels=goodPixels, plot=False, moments=moments, degree=12, mdegree=-1, vsyst=dv, component=components, sky=sky) rms0 = galaxy[goodPixels] - pp0.bestfit[goodPixels] noise0 = 1.4826 * np.median(np.abs(rms0 - np.median(rms0))) noise0 = np.zeros_like(galaxy) + noise0 # Second pPXF interaction, realistic noise estimation pp = ppxf(templates, galaxy, noise0, velscale, start, goodpixels=goodPixels, plot=plot, moments=moments, degree=degree, mdegree=mdegree, vsyst=dv, component=components, sky=sky) # pp.template_files = templates_names # pp.has_emission = has_emission ###################################################################### # Save to output file to keep session with open(pkl, "w") as f: pickle.dump(pp, f) ###################################################################### return
def VIMOS_ppxf(l1, l2, start, plot=True, nsimulations=False): spec = getdata(result['ORIGINALFILE'].loc[0], 1) header = getheader(result['ORIGINALFILE'].loc[0], 1) quadrant = header['ESO OCS CON QUAD'] lamRange = header['CRVAL1'] + np.array( [0., header['CD1_1'] * (header['NAXIS1'] - 1)]) wavelength = np.linspace(lamRange[0], lamRange[1], header['NAXIS1']) ix_start = np.where(wavelength > l1)[0][0] ix_end = np.where(wavelength < l2)[0][-1] w_start = wavelength[ix_start] w_end = wavelength[ix_end] lamRange = [w_start, w_end] galaxy, logLam1, velscale = util.log_rebin(lamRange, spec) wavelength_log = np.exp(logLam1) logLam2, templates = utilities.get_templates(velscale, l1, l2) dv = (logLam2[0] - logLam1[0]) * c df_columns = [ 'slit_id', 'rv_ppxf', 'rv_ppxf_err', 'sigma_ppxf', 'sigma_ppxf_err', 'seeing', 'vhelio' ] final_frame = pd.DataFrame(columns=df_columns) # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # for i in tqdm(range(0, len(result))): single = result.iloc[i] hdu = fits.open(single['ORIGINALFILE']) header = hdu[0].header galaxy, _, _ = util.log_rebin(lamRange, hdu[1].data[ix_start:ix_end], velscale=velscale) noise, _, _ = util.log_rebin(lamRange, hdu[2].data[ix_start:ix_end], velscale=velscale) noise = noise + 0.001 # Some spectra have noise = 0 twoD = hdu[3].data thumbnail = hdu[4].data hdu.close() seeing = header[ 'ESO TEL IA FWHMLINOBS'] # Delivered seeing on IA detector slit_id = single['ID'] quadrant = slit_id[2] rv_fxcor = single['VREL'] sg = header['SG_NORM'] sn1 = header['SN1'] sn2 = header['SN2'] RA_g = single['RA_g'] DEC_g = single['DEC_g'] vhelio = utilities.get_vhelio( header) # Calculate heliocentric velocity start = [rv_fxcor, 10] if (quadrant == '1') | (quadrant == '3'): pixelmask = pixelmask_Q1Q3 else: pixelmask = pixelmask_normal goodpixels = utilities.make_goodpixels(pixelmask, logLam1, logLam2) rv, sigma, error_rv, error_sigma, chi2, bestfit, residuals, zp1, output_wavelengths = run_ppxf( wavelength_log, templates, galaxy, noise, velscale, start, goodpixels, dv) if plot: kwargs = { 'output_wavelengths': output_wavelengths, 'ix_start': ix_start, 'ix_end': ix_end, 'slit_id': slit_id, 'rv': rv, 'sigma': sigma, 'chi2': chi2, 'pixelmask': pixelmask, 'zp1': zp1, 'rv_fxcor': rv_fxcor, 'sg': sg, 'sn1': sn1, 'sn2': sn2, 'RA_g': RA_g, 'DEC_g': DEC_g } figure = ppplot.ppxfplot(twoD, galaxy, noise, bestfit, residuals, thumbnail, **kwargs) figure.savefig( '/Volumes/VINCE/OAC/ppxf/figures/{}.png'.format(slit_id)) plt.close(figure) df_tmp = pd.DataFrame( [[slit_id, rv, error_rv, sigma, error_sigma, seeing, vhelio]], columns=df_columns) final_frame = final_frame.append(df_tmp, ignore_index=True) final_frame.to_csv('ppxf_results.csv', index=False) if nsimulations: samples = [] rv_mc = [] for j in xrange(nsimulations): noise_mc = noise * np.random.normal(size=noise.size) galaxy_mc = bestfit + noise_mc samples.append((wavelength_log, templates, galaxy_mc, noise, velscale, start, goodpixels, dv)) rv, sigma, chi2, error_rv, error_sigma, bestfit, residuals, zp1, output_wavelengths = run_ppxf( wavelength_log, templates, galaxy_mc, noise, velscale, start, goodpixels, dv) rv_mc.append(rv) #workers = min(multiprocessing.cpu_count(), 2) #pool = multiprocessing.Pool(processes=workers) #print 'Using', workers, 'workers' #sample_results = pool.map(_boot_VIMOS_ppxf, samples) #print 'past sample_results' return final_frame
def ppxf_nifs_kinematics(): file_dir = path.dirname(path.realpath(__file__)) # path of this procedure # Read a galaxy spectrum and define the wavelength range # file = file_dir + '/spectra/pgc12557_combined.fits' # '/spectra/NGC4550_SAURON.fits' # my current file location hdu = fits.open(file) gal_lin = hdu[1].data # gal_lin = hdu[0].data # h1 = hdu[0].header h1 = hdu[ 1].header # I need to use 1st extension header (0=general, 1=science, 2=variance, 3=data quality flags) ''' print(h1) BITPIX = -32 / array data type NAXIS = 3 / number of array dimensions NAXIS1 = 71 NAXIS2 = 69 NAXIS3 = 2040 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups EXTNAME = 'SCI ' / Extension name EXTVER = 1 / Extension version INHERIT = F / Inherits global header ORIGIN = 'NOAO-IRAF FITS Image Kernel July 2003' / FITS file originator OBJECT = 'PGC12557' / Name of the object observed DISPAXIS= 3 / Dispersion axis PIXSCALE= 0.05 / Pixel scale in arcsec/pixel CTYPE1 = 'LINEAR ' / coordinate type for the dispersion axis CTYPE2 = 'LINEAR ' / coordinate type for the spatial axis DC-FLAG = 0 / CD1_1 = 0.05 / CD2_2 = 0.05 / DCLOG1 = 'Transform' WCSDIM = 3 / CRVAL1 = 2.987 CRPIX1 = 68. CRPIX2 = 2. WAT1_001= 'wtype=linear axtype=xi' / WAT2_001= 'wtype=linear axtype=eta' / CTYPE3 = 'LINEAR ' / WAT3_001= 'wtype=linear axtype=wave' / CRVAL3 = 19971.869140625 / CD3_3 = 2.1321439743 / AIRMASS = 1.374 CRPIX3 = 1. LTM1_1 = 1. LTM2_2 = 1. LTM3_3 = 1. WAT0_001= 'system=world' END ''' ''' # NOTE: Copied from util.log_rebin(): lamRange: two elements vector containing the central wavelength of the first and last pixels in the spectrum, which is assumed to have constant wavelength scale! E.g. from the values in the standard FITS keywords: LAMRANGE = CRVAL1 + [0, CDELT1*(NAXIS1 - 1)]. It must be LAMRANGE[0] < LAMRANGE[1]. ''' # lamRange1 = h1['CRVAL1'] + np.array([0., h1['CDELT1']*(h1['NAXIS1'] - 1)]) # original # lamRange1 = h1['CRVAL3'] + np.array([0., h1['CD3_3']*(h1['CRPIX3'])]) # [ 19971.86914062 19974.0012846 ] # if I use CRPIX3, probably don't want CRPIX3 - 1 because CRPIX3 = 1., and need lamRange1[0] < lamRange1[1] lamRange1 = h1['CRVAL3'] + np.array( [0., h1['CD3_3'] * (h1['NAXIS3'] - 1)]) # [ 19971.86914062 24319.31070422] print(lamRange1, 'l1') # print(gal_lin[0][20]) all 0s # print((gal_lin[300][35])) NOT all 0s! # print(len(lamRange1)) # len(lamRange1) = 2, # len(gal_lin) = 2040, len(gal_lin[0]) = 69, len(gal_lin[0][1]) = 71 # 2040 --> NAXIS 3, 69 --> NAXIS2, 71 --> NAXIS1 # HMM: want gal_lin to be spectrum itself, which should just be 1d array # There's a len 2040 spectrum at each gal_lin[:,x,y] # CUT SPECTRUM TO WAVELENGTH RANGE 2.26 - 2.42 # SO: gal_lin is an array len(2040) starting at lamRange1[0] and finishing at lamRange1[1], with each pixel in # between separated by h1['CD3_3']. So find [22600 - lamRange1[0]] / CD3_3, and that should give the number of # pixels between pixel 1 and the pixel corresponding roughly to 2.26 microns. Then find [24200 - lamRange1[1]] / # CD3_3, which should give the number of pixels between pixel 1 and the pixel corresponding to 2.42 microns. start = 22600. stop = 24200. cut1 = (start - lamRange1[0]) / h1['CD3_3'] cut2 = (stop - lamRange1[0]) / h1['CD3_3'] # print(cut1, cut2, 'cuts') # 1232.62354281, 1983.04191009 gal_lin = gal_lin[int(cut1):int(cut2)] # [1233:1983] # start1 = h1['CRVAL3'] + h1['CD3_3'] * int(cut1) # stop1 = h1['CRVAL3'] + h1['CD3_3'] * int(cut2) # print(start1, stop1, 'me') lamRange1 = [ h1['CRVAL3'] + h1['CD3_3'] * int(cut1), h1['CRVAL3'] + h1['CD3_3'] * int(cut2) ] print(lamRange1, 'l1, cut') # len(gal_lin) is now NOT 2040 but 1983 - 1233 = 750 FWHM_gal = 4.2 # SAURON has an instrumental resolution FWHM of 4.2A. # BUCKET: do I need this? If so what for? # 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 # There's a len 2040 spectrum at each gal_lin[:,x,y] x = 33 y = 35 galaxy, logLam1, velscale = util.log_rebin( lamRange1, gal_lin[:, x, y]) # no input velscale --> function returns print(len(galaxy), 'len gal') # 750 now because of cut to gal_lin! galaxy = galaxy / np.median( galaxy) # Normalize spectrum to avoid numerical issues # print(galaxy) # BUCKET: constant noise/pix good assumption for me? If so what value do I use? TRY our noise! Maybe trust more?! # basically bootstrap the noise! # Do one fit with flat noise, then save the best fit spectrum and the residuals. # Then, iterate ~200 times. For these iterations, set bias = 0.0. Each iteration, for each pixel, use the spectrum # value as the center of a gaussian and use the residuals at that pixel value as the width of the gaussian. Draw # from the resultant distribution to make the new noise. For each iteration, save the output V, sigma, h3, h4, and # print each spectrum so that we can see it evolving (it should look more like a real spectrum, rather than smooth # curve without lines) noise = np.full_like(galaxy, 0.0047) # Assume constant noise per pixel here # MEANTIME: why is output velocity close to systemic insted of close to 0, if I'm dealing with redshift already? # SET bias = 0 # # print(noise.shape) # 751, # print(galaxy.shape) # 751, # 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( file_dir + '/veltemps/*.fits') # '/miles_models/Mun1.30Z*.fits') # my new # BUCKET: what are FWHM of spectra in the veltemps library?? FWHM_tem = 2.51 # Vazdekis+10 spectra have a constant resolution FWHM of 2.51A. # BUCKET: what spectral sampling compared to galaxy do we want for templates?? # velscale_ratio = 2 # 1.23 # 2 # adopts 2x higher spectral sampling for templates than for galaxy # PROBLEM!! If velscale_ratio is not integer, we get issue later because we slice a list with velscale_ratio # so need to change velscale? But it's set by util.log_rebin originally! Only need this if oversampling the # templates, which I'm not doing # 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. # print(vazdekis[0], 'template name') hdu = fits.open( vazdekis[0] ) # do for just one template to determine size needed for array containing all templates ssp = hdu[ 1].data # was hdu[0], but that's generic header rather than science header h2 = hdu[1].header # was hdu[0] ''' for line in h2: print(line, h2[line]) XTENSION IMAGE BITPIX -32 NAXIS 1 NAXIS1 1721 PCOUNT 0 GCOUNT 1 EXTNAME SCI EXTVER 1 INHERIT False ORIGIN NOAO-IRAF FITS Image Kernel July 2003 OBJECT BD-01 3097 DATE 2015-02-15T13:32:06 IRAF-TLM 2015-02-15T13:32:30 NFPAD 2015-02-14T14:40:41 GEM-TLM 2015-02-14T14:40:41 DISPAXIS 1 PIXSCALE 0.043 NSCUTSEC [1:2040,1145:1213] NSCUTSPC 13 FIXPIX Feb 14 8:44 Bad pixel file is tmpdq20234_650 CTYPE1 LINEAR CD1_1 2.13 CTYPE2 LINEAR CD2_2 0.04570113 DCLOG1 Transform DC-FLAG 0 WCSDIM 3 CRVAL1 20628.29 CRPIX1 1.0 CRPIX2 -28.0 WAXMAP01 1 0 0 29 0 0 WAT1_001 wtype=linear axtype=wave WAT2_001 wtype=linear axtype=eta CTYPE3 LINEAR WAT3_001 wtype=linear axtype=xi CRVAL3 1.751 CD3_3 0.103 LTV2 -29.0 LTM1_1 1.0 LTM2_2 1.0 LTM3_3 1.0 WAT0_001 system=image EXPTIME 25.0 IMCMB001 xatfbrsnN20070508S0187.fits[SCI] IMCMB002 xatfbrsnN20070508S0190.fits[SCI] IMCMB003 xatfbrsnN20070508S0191.fits[SCI] IMCMB004 xatfbrsnN20070508S0194.fits[SCI] NCOMBINE 4 GAINORIG 1.0 RONORIG 0.0 GAIN 2.666667 RDNOISE 0.0 ''' # lamRange2 = h2['CRVAL1'] + np.array([0., h2['CDELT1']*(h2['NAXIS1'] - 1)]) # original lamRange2 = h2['CRVAL1'] + np.array([0., h2['CD1_1'] * (h2['NAXIS1'] - 1) ]) # BUCKET want NAXIS - 1? # lamRange2 = h2['CRVAL1'] + np.array([0., h2['CD1_1']*(h2['CRPIX1'])]) # or use CRPIX1?? print(lamRange2, 'l2') # [ 20628.29 24291.89] # print((lamRange2[1] - lamRange2[0])/h2['CD1_1'], 'num of steps in lam2') # 1720. # print(len(ssp), 'len ssp') # 1721 sspNew, logLam2, velscale_temp = util.log_rebin( lamRange2, ssp, velscale=velscale) # /velscale_ratio) # print(len(sspNew), 'len sspnew') # 622 hmmmm NEED THIS TO BE >= (len(galaxy)=750) # FIXED, now 1791 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 sigma = FWHM_dif / 2.355 / h2['CD1_1'] # Sigma difference in pixels for j, file in enumerate( vazdekis ): # now for each template file; so why do the thing above with just 1?? hdu = fits.open(file) # ssp = hdu[0].data ssp = hdu[1].data ssp = ndimage.gaussian_filter1d(ssp, sigma) # ndimage.gaussian_filter takes input array (ssp) and filters it. Sigma = standard deviation of Gaussian kernel # used to filter the array # note: discrete convolution is defined (for any 2 arrays a, v): (a*v)[n] == sum m(-inf to inf) of a[m]*v[n-m] # where a is the original curve and v is the gaussian # print(len(ssp)) # 1721 --> currently by default being resampled to be 116, want it to be resampled to be 71 sspNew, logLam2, velscale_temp = util.log_rebin( lamRange2, ssp, velscale=velscale) # /velscale_ratio) # print(velscale_temp, 'vt') # 78.82967746 # print(velscale, 'v') # 78.82967746 # print(len(sspNew)) # need this to be >= len(galaxy) # now 1791 templates[:, j] = sspNew / np.median(sspNew) # Normalizes templates # print(len(templates[0])) # len(templates)=29, len(templates[0] = 19) # 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 = (logLam2[0] - logLam1[0]) * c # km/s ''' 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.016561 # 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.] # [vel, 200.] # (km/s), starting guess for [V, sigma] t = clock() # print(galaxy.shape[0]) # = len(galaxy) = 750 # print(goodPixels) # print(max(noise[:, x, y]), min(noise[:, x, y]), np.median(noise[:, x, y]), np.mean(noise[:, x, y])) # 0.00106575 -2.77079e-05 4.62628e-05 5.89877e-05 pp = ppxf(templates, galaxy, noise, velscale, start, goodpixels=goodPixels, plot=True, moments=4, degree=4, vsyst=dv, velscale_ratio=1) # 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.bestfit, pp.galaxy, pp.sol # output_spectrum, output_noise # for use in noise_in
def run_stellar_templates(velscale): """ Run over stellar templates. """ temp_dir = os.path.join(home, "stellar_templates/MILES_FWHM_3.6") standards_dir = os.path.join(home, "data/standards") table = os.path.join(tables_dir, "lick_standards.txt") ids = np.loadtxt(table, usecols=(0,), dtype=str).tolist() star_pars = np.loadtxt(table, usecols=(26,27,28,)) for night in nights: cdir = os.path.join(standards_dir, night) os.chdir(cdir) standards = sorted([x for x in os.listdir(".") if x.endswith(".fits")]) for standard in standards: name = standard.split(".")[0].upper() if name not in ids: continue print standard idx = ids.index(name) T, logg, FeH = star_pars[idx] tempfile= "MILES_Teff{0:.2f}_Logg{1:.2f}_MH{2:.2f}" \ "_linear_FWHM_3.6.fits".format(T, logg, FeH ) os.chdir(temp_dir) template = pf.getdata(tempfile) htemp = pf.getheader(tempfile) wtemp = htemp["CRVAL1"] + htemp["CDELT1"] * \ (np.arange(htemp["NAXIS1"]) + 1 - htemp["CRPIX1"]) os.chdir(cdir) dsigma = np.sqrt((3.7**2 - 3.6**2))/2.335/(wtemp[1]-wtemp[0]) template = broad2hydra(wtemp, template, 3.6) data = pf.getdata(standard) w = wavelength_array(standard) lamRange1 = np.array([w[0], w[-1]]) lamRange2 = np.array([wtemp[0], wtemp[-1]]) # Rebin to log scale star, logLam1, velscale = util.log_rebin(lamRange1, data, velscale=velscale) temp, logLam2, velscale = util.log_rebin(lamRange2, template, velscale=velscale) w = np.exp(logLam1) goodpixels = np.argwhere((w>4700) & (w<6100)).T[0] noise = np.ones_like(star) dv = (logLam2[0]-logLam1[0])*c pp0 = ppxf(temp, star, noise, velscale, [300.,5], plot=False, moments=2, degree=20, mdegree=-1, vsyst=dv, quiet=True, goodpixels=goodpixels) noise = np.ones_like(noise) * np.nanstd(star - pp0.bestfit) pp0 = ppxf(temp, star, noise, velscale, [0.,5], plot=False, moments=2, degree=20, mdegree=-1, vsyst=dv, goodpixels=goodpixels) pp0.w = np.exp(logLam1) pp0.wtemp = np.exp(logLam2) pp0.template_linear = [wtemp, template] pp0.temp = temp pp0.ntemplates = 1 pp0.ngas = 0 pp0.has_emission = False pp0.dv = dv pp0.velscale = velscale pp0.ngas = 0 pp0.nsky = 0 if not os.path.exists("logs"): os.mkdir("logs") ppsave(pp0, "logs/{0}".format(standard.replace(".fits", ""))) pp = ppload("logs/{0}".format(standard.replace(".fits", ""))) pp = pPXF(standard, velscale, pp) pp.plot("logs/{0}".format(standard.replace(".fits", ".png"))) # plt.show() return
def run_ppxf(spectra, velscale, ncomp=2, has_emission=True, mdegree=-1, degree=20, pkls=None, plot=False, data_sky=None): """ Run pPXF in a list of spectra""" if isinstance(spectra, str): spectra = [spectra] if isinstance(pkls, str): pkls = [pkls] if pkls == None: pkls = [x.replace(".fits", ".pkl") for x in spectra] ########################################################################## # Load templates for both stars and gas star_templates, logLam2, delta, miles= stellar_templates(velscale) gas_templates,logLam_gas, delta_gas, gas_files=emission_templates(velscale) ########################################################################## # Join templates in case emission lines are used. if has_emission: templates = np.column_stack((star_templates, gas_templates)) else: templates = star_templates ########################################################################## if ncomp == 1: components = 0 moments = [4] templates_names = miles elif ncomp == 2: components = np.hstack((np.zeros(len(star_templates[0])), np.ones(len(gas_templates[0])))) moments = [4,2] templates_names = np.hstack((miles, gas_files)) else: raise Exception("ncomp has to be 1 or 2.") for i, spec in enumerate(spectra): print "pPXF run of spectrum {0} ({1} of {2})".format(spec, i+1, len(spectra)) pkl = pkls[i] ###################################################################### # Read one galaxy spectrum and define the wavelength range specfile = os.path.join(data_dir, spec) hdu = pf.open(specfile) spec_lin = hdu[0].data h1 = pf.getheader(specfile) lamRange1 = h1['CRVAL1'] + np.array([0.,h1['CDELT1']*(h1['NAXIS1']-1)]) ###################################################################### # Degrade observed spectra to match template resolution FWHM_dif = np.sqrt(FWHM_tem**2 - FWHM_spec**2) sigma = FWHM_dif/2.355/delta # Sigma difference in pixels spec_lin = ndimage.gaussian_filter1d(spec_lin,sigma) ###################################################################### # Rebin to log scale galaxy, logLam1, velscale = util.log_rebin(lamRange1, spec_lin, velscale=velscale) ###################################################################### # First guess for the noise noise = np.ones_like(galaxy) * np.std(galaxy - medfilt(galaxy, 5)) ###################################################################### # Calculate difference of velocity between spectrum and templates # due to different initial wavelength dv = (logLam2[0]-logLam1[0])*c ###################################################################### # Set first guess from setup files start, goodPixels = read_setup_file(spec, logLam1, mask_emline=False) ###################################################################### # Expand start variable to include multiple components if ncomp > 1: start = [start, [start[0], 30]] ###################################################################### # Read sky in needed if data_sky == None: sky = None else: sky_lin = pf.getdata(data_sky[i]) sky_lin = ndimage.gaussian_filter1d(sky_lin,sigma) sky, logLam1, velscale = util.log_rebin(lamRange1, sky_lin, velscale=velscale) sky = sky.reshape(-1,1) ###################################################################### # First pPXF interaction if os.path.exists(spec.replace(".fits", ".pkl")): pp0 = pPXF(spec, velscale, pklfile=spec.replace(".fits", ".pkl")) noise0 = pp0.noise else: pp0 = ppxf(templates, galaxy, noise, velscale, start, goodpixels=goodPixels, plot=False, moments=moments, degree=12, mdegree=-1, vsyst=dv, component=components, sky=sky) rms0 = galaxy[goodPixels] - pp0.bestfit[goodPixels] noise0 = 1.4826 * np.median(np.abs(rms0 - np.median(rms0))) noise0 = np.zeros_like(galaxy) + noise0 # Second pPXF interaction, realistic noise estimation pp = ppxf(templates, galaxy, noise0, velscale, start, goodpixels=goodPixels, plot=plot, moments=moments, degree=degree, mdegree=mdegree, vsyst=dv, component=components, sky=sky) # pp.template_files = templates_names # pp.has_emission = has_emission ###################################################################### # Save to output file to keep session with open(pkl, "w") as f: pickle.dump(pp, f) ###################################################################### return
def ppxf_nifs_kinematics(newgal=None, resid=None, centers=None, coords=[0, 0]): file_dir = path.dirname(path.realpath(__file__)) # path of this procedure # Read a galaxy spectrum and define the wavelength range file = file_dir + '/spectra/pgc12557_combined.fits' # '/spectra/NGC4550_SAURON.fits' # my current file location hdu = fits.open(file) gal_lin = hdu[1].data # gal_lin = hdu[0].data h1 = hdu[ 1].header # I need to use 1st extension header (0=general, 1=science, 2=variance, 3=data quality flags) lamRange1 = h1['CRVAL3'] + np.array( [0., h1['CD3_3'] * (h1['NAXIS3'] - 1)]) # [ 19971.86914062 24319.31070422] print(lamRange1, 'l1') # print(gal_lin[0][20]) all 0s # print((gal_lin[300][35])) NOT all 0s! # len(gal_lin) = 2040, len(gal_lin[0]) = 69, len(gal_lin[0][1]) = 71 # 2040 --> NAXIS 3, 69 --> NAXIS2, 71 --> NAXIS1 # There's a len 2040 spectrum at each gal_lin[:,x,y] --> gal_lin[:, x, y] is an array len(2040) starting at # lamRange1[0] and finishing at lamRange1[1], with each pixel in between separated by h1['CD3_3']. # CUT SPECTRUM TO WAVELENGTH RANGE 2.26 - 2.42 low_lim = 22600. up_lim = 24200. cut1 = int( (low_lim - lamRange1[0]) / h1['CD3_3'] ) # num pixels between pix 1 & pix corresponding to 2.26 microns cut2 = int( (up_lim - lamRange1[0]) / h1['CD3_3'] ) # num pixels between pix 1 & pix corresponding to 2.42 microns # print(cut1, cut2, 'cuts') # 1232.62354281, 1983.04191009 --> int(cut1) = 1232, int(cut2) = 1983 gal_lin = gal_lin[cut1: cut2] # cut gal_lin spectrum to new wavelength range start = h1['CRVAL3'] + h1['CD3_3'] * cut1 stop = h1['CRVAL3'] + h1['CD3_3'] * cut2 lamRange1 = [start, stop ] # redefine lamRange1 to correspond to new wavelength range print(lamRange1, 'l1, cut') # len(gal_lin) is now NOT 2040 but 1983 - 1233 = 750 FWHM_gal = 4.2 # SAURON has an instrumental resolution FWHM of 4.2A. # BUCKET: do I need this? If so what for? # 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 # There's a len 2040 spectrum at each gal_lin[:,x,y] x = coords[0] y = coords[1] # if newgal is None: galaxy, logLam1, velscale = util.log_rebin( lamRange1, gal_lin[:, x, y]) # no input velscale --> fcn returns it print(len(galaxy), 'len gal') # 750 now because of cut to gal_lin! print(np.median(galaxy)) galaxy = galaxy / np.median( galaxy) # Normalize spectrum to avoid numerical issues # else: # galaxy, logLam1, velscale = util.log_rebin(lamRange1, newgal) # newgal is the spectrum at coords x, y # basically bootstrap the noise! # Do one fit with flat noise, then save the best fit spectrum and the residuals. # Then, iterate ~200 times. For these iterations, set bias = 0.0. Each iteration, for each pixel, use the spectrum # value as the center of a gaussian and use the residuals at that pixel value as the width of the gaussian. Draw # from the resultant distribution to make the new noise. For each iteration, save the output V, sigma, h3, h4, and # print each spectrum so that we can see it evolving (it should look more like a real spectrum, rather than smooth # curve without lines) noise = np.full_like(galaxy, 0.0047) # Assume constant noise per pixel here # MEANTIME: why is output velocity close to systemic insted of close to 0, if I'm dealing with redshift already? # SET bias = 0 # print('shape', noise.shape) # 751, # print(galaxy.shape) # 751, # 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( file_dir + '/veltemps/*.fits') # '/miles_models/Mun1.30Z*.fits') # my new # BUCKET: what are FWHM of spectra in the veltemps library?? FWHM_tem = 2.51 # Vazdekis+10 spectra have a constant resolution FWHM of 2.51A. # BUCKET: what spectral sampling compared to galaxy do we want for templates?? # velscale_ratio = 2 # 1.23 # 2 # adopts 2x higher spectral sampling for templates than for galaxy # PROBLEM!! If velscale_ratio is not integer, we get issue later because we slice a list with velscale_ratio # so need to change velscale? But it's set by util.log_rebin originally! Only need this if oversampling the # templates, which I'm not doing # 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. # print(vazdekis[0], 'template name') hdu = fits.open( vazdekis[0] ) # do for just one template to determine size needed for array containing all templates ssp = hdu[ 1].data # was hdu[0], but that's generic header rather than science header h2 = hdu[1].header # was hdu[0] lamRange2 = h2['CRVAL1'] + np.array([0., h2['CD1_1'] * (h2['NAXIS1'] - 1) ]) # BUCKET want NAXIS - 1? print(lamRange2, 'l2') # [ 20628.29 24291.89] # print((lamRange2[1] - lamRange2[0])/h2['CD1_1'], 'num of steps in lam2') # 1720. # print(len(ssp), 'len ssp') # 1721 sspNew, logLam2, velscale_temp = util.log_rebin( lamRange2, ssp, velscale=velscale) # /velscale_ratio) # print(len(sspNew), 'len sspnew') # 622 hmmmm NEED THIS TO BE >= (len(galaxy)=750) # FIXED, now 1791 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 sigma = FWHM_dif / 2.355 / h2['CD1_1'] # Sigma difference in pixels for j, file in enumerate( vazdekis ): # now for each template file; so why do the thing above with just 1?? hdu = fits.open(file) # ssp = hdu[0].data ssp = hdu[1].data ssp = ndimage.gaussian_filter1d(ssp, sigma) # ndimage.gaussian_filter takes input array (ssp) and filters it. Sigma = standard deviation of Gaussian kernel # used to filter the array # note: discrete convolution is defined (for any 2 arrays a, v): (a*v)[n] == sum m(-inf to inf) of a[m]*v[n-m] # where a is the original curve and v is the gaussian # print(len(ssp)) # 1721 --> currently by default being resampled to be 116, want it to be resampled to be 71 sspNew, logLam2, velscale_temp = util.log_rebin( lamRange2, ssp, velscale=velscale) # /velscale_ratio) # print(velscale_temp, 'vt') # 78.82967746 # print(velscale, 'v') # 78.82967746 # print(len(sspNew)) # need this to be >= len(galaxy) # now 1791 templates[:, j] = sspNew / np.median(sspNew) # Normalizes templates # print(len(templates[0])) # len(templates)=29, len(templates[0] = 19) # 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 = (logLam2[0] - logLam1[0]) * c # km/s ''' 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.016561 # 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.] # [vel, 200.] # (km/s), starting guess for [V, sigma] t = clock() # print(galaxy.shape[0]) # = len(galaxy) = 750 # print(goodPixels) # print(max(noise[:, x, y]), min(noise[:, x, y]), np.median(noise[:, x, y]), np.mean(noise[:, x, y])) # 0.00106575 -2.77079e-05 4.62628e-05 5.89877e-05 if newgal is None: pp = ppxf(templates, galaxy, noise, velscale, start, goodpixels=goodPixels, plot=True, moments=4, degree=4, vsyst=dv, velscale_ratio=1, bias=0.) # velscale_ratio=velscale_ratio) stuff = pp.bestfit, pp.galaxy, pp.sol, goodPixels else: pp = ppxf(templates, galaxy, noise, velscale, start, goodpixels=goodPixels, plot=True, moments=4, degree=4, vsyst=dv, velscale_ratio=1, bias=0.) # velscale_ratio=velscale_ratio) pp_new = ppxf(templates, newgal, noise, velscale, start, goodpixels=goodPixels, plot=False, moments=4, degree=4, vsyst=dv, velscale_ratio=1, bias=0.) # velscale_ratio=velscale_ratio) # pp.plot() # Plot best fit and gas lines x_ax = np.arange(galaxy.size) plt.plot(x_ax, pp.galaxy, 'k') plt.plot(x_ax, newgal, 'b') stuff = pp_new.bestfit, pp_new.galaxy, pp_new.sol 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 stuff # output_spectrum, output_noise # for use in noise_in
hdulist = fits.open(name + '.fits') if len(hdulist) > 1: hdu = hdulist[1] else: hdu = hdulist[0] cube = hdu.data.T #just remember to always transpose with fits files hdr_cube = hdu.header xs, ys, naxis = cube.shape hdulist.close() xcube, ycube = np.ogrid[-xc:xs - xc, -yc:ys - yc] rCube = np.sqrt(xcube * xcube + ycube * ycube) lamRange1 = hdr_cube['CRVAL3'] + np.array( [0., hdr_cube['CD3_3'] * (hdr_cube['NAXIS3'] - 1.)]) tg, logLam1, velscale = util.log_rebin(lamRange1, cube[10, 10, :]) # Read the template hdu = fits.open('temp_final_%s.fits' % Z) h_template = hdu[0].header temp_lin = hdu[0].data lamRange2 = h_template['CRVAL1'] + np.array( [0., h_template['CDELT1'] * (h_template['NAXIS1'] - 1.)]) templates, logLam2, velscale = util.log_rebin(lamRange2, temp_lin, velscale=velscale) # Calculate the offset between the template and the galaxy c = 299792.458 dv = (logLam2[0] - logLam1[0]) * c
def setup_spectral_library(velscale, FWHM_gal): # Read the list of filenames from the Single Stellar Population library # by Vazdekis et al. (2010, MNRAS, 404, 1639) http://miles.iac.es/. # # For this example I downloaded from the above website a set of # model spectra with default linear sampling of 0.9A/pix and default # spectral resolution of FWHM=2.51A. I selected a Salpeter IMF # (slope 1.30) and a range of population parameters: # # [M/H] = [-1.71, -1.31, -0.71, -0.40, 0.00, 0.22] # Age = range(1.0, 17.7828, 26, /LOG) # # This leads to a set of 156 model spectra with the file names like # # Mun1.30Zm0.40T03.9811.fits # # IMPORTANT: the selected models form a rectangular grid in [M/H] # and Age: for each Age the spectra sample the same set of [M/H]. # # We assume below that the model spectra have been placed in the # directory "miles_models" under the current directory. # vazdekis = glob.glob('miles_models/Mun1.30*.fits') FWHM_tem = 2.51 # Vazdekis+10 spectra have a resolution FWHM of 2.51A. # 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 = pyfits.open(vazdekis[0]) ssp = hdu[0].data h2 = hdu[0].header lamRange_temp = h2['CRVAL1'] + np.array([0.,h2['CDELT1']*(h2['NAXIS1']-1)]) sspNew, logLam2, velscale = util.log_rebin(lamRange_temp, ssp, velscale=velscale) # Create a three dimensional array to store the # two dimensional grid of model spectra # nAges = 26 nMetal = 6 templates = np.empty((sspNew.size,nAges,nMetal)) # 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 # 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 # metal = ['m1.71', 'm1.31', 'm0.71', 'm0.40', 'p0.00', 'p0.22'] for k, mh in enumerate(metal): files = [s for s in vazdekis if mh in s] for j, filename in enumerate(files): hdu = pyfits.open(filename) ssp = hdu[0].data ssp = ndimage.gaussian_filter1d(ssp,sigma) sspNew, logLam2, velscale = util.log_rebin(lamRange_temp, ssp, velscale=velscale) templates[:,j,k] = sspNew # Templates are *not* normalized here print(np.shape(templates)) return templates, lamRange_temp
def ppxf_simulation_example(): dir = 'spectra/' file = dir + 'Rbi1.30z+0.00t12.59.fits' hdu = pyfits.open(file) ssp = hdu[0].data h = hdu[0].header lamRange = h['CRVAL1'] + np.array([0.,h['CDELT1']*(h['NAXIS1']-1)]) star, logLam, velscale = util.log_rebin(lamRange, ssp) # 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=1) # 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 vel = 0.3 # velocity in *pixels* [=V(km/s)/velScale] h3 = 0.1 # Adopted G-H parameters of the LOSVD h4 = -0.1 sn = 60. # Adopted S/N of the Monte Carlo simulation m = 300 # Number of realizations of the simulation sigmaV = np.linspace(0.8,4,m) # Range of sigma in *pixels* [=sigma(km/s)/velScale] result = np.zeros((m,4)) # This will store the results t = clock() np.random.seed(123) # for reproducible results for j in range(m): sigma = sigmaV[j] dx = int(abs(vel)+4.0*sigma) # Sample the Gaussian and GH at least to vel+4*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)/(np.sqrt(2.*np.pi)*sigma*factor) # 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.random(), sigma*np.random.uniform(0.85,1.15)])*velscale # Convert to km/s pp = ppxf(star, galaxy, noise, velscale, start, goodpixels=np.arange(dx,galaxy.size-dx), plot=False, moments=4, bias=0.5) result[j,:] = pp.sol print('Calculation time: %.2f s' % (clock()-t)) plt.clf() plt.subplot(221) plt.plot(sigmaV*velscale, result[:,0]-vel*velscale, '+k') plt.plot(sigmaV*velscale, sigmaV*velscale*0, '-r') plt.ylim(-40, 40) plt.xlabel('$\sigma_{in}\ (km\ s^{-1})$') plt.ylabel('$V - V_{in}\ (km\ s^{-1}$)') plt.subplot(222) plt.plot(sigmaV*velscale, result[:,1]-sigmaV*velscale, '+k') plt.plot(sigmaV*velscale, sigmaV*velscale*0, '-r') plt.ylim(-40, 40) plt.xlabel('$\sigma_{in}\ (km\ s^{-1})$') plt.ylabel('$\sigma - \sigma_{in}\ (km\ s^{-1}$)') plt.subplot(223) plt.plot(sigmaV*velscale, result[:,2], '+k') plt.plot(sigmaV*velscale, sigmaV*velscale*0+h3, '-r') plt.ylim(-0.2+h3, 0.2+h3) plt.xlabel('$\sigma_{in}\ (km\ s^{-1})$') plt.ylabel('$h_3$') plt.subplot(224) plt.plot(sigmaV*velscale, result[:,3], '+k') plt.plot(sigmaV*velscale, sigmaV*velscale*0+h4, '-r') plt.ylim(-0.2+h4, 0.2+h4) plt.xlabel('$\sigma_{in}\ (km\ s^{-1})$') plt.ylabel('$h_4$') plt.tight_layout() plt.pause(0.01)