def SNR_final_spec(data_path, plot_path, stars, plot=False): """ - Calculate the median SNR of the final used spectra after the 1% noise addition and after "merging" (in merge_obsspec.py), in certain wavelength regions - Plot the SNR of the final used spectra if requested Parameters ---------- data_path : string Path to the data files plot_path : string Path to save the plots stars : list of strings List of stars for which to calculate (and plot) the SNR plot : boolean [default=False] Whether or not to plot the SNR vs. wavelength for every star Returns ------- - Median SNRs in certain wavelength regions - Plots of the SNR vs. wavelength (if requested) """ meds = np.zeros((3, len(stars))) for j, star in enumerate(stars): # obtain the flux values and uncertainties starobs = StarData( "%s.dat" % star.lower(), path=data_path, use_corfac=True, ) waves, fluxes, uncs = starobs.get_flat_data_arrays(["SpeX_SXD", "SpeX_LXD"]) # calculate the median SNR in certain wavelength regions ranges = [ (0.79, 2.54), (2.85, 4.05), (4.55, 5.5), ] SNR = fluxes / uncs for i, range in enumerate(ranges): mask = (waves > range[0]) & (waves < range[1]) meds[i][j] = np.median(SNR[mask]) # plot SNR vs. wavelength if requested if plot: fig, ax = plt.subplots() ax.scatter(waves, fluxes / uncs, s=1) plt.savefig(plot_path + star + "_SNR.pdf") print(ranges[0], np.nanmin(meds[0]), np.nanmax(meds[0])) print(ranges[1], np.nanmin(meds[1]), np.nanmax(meds[1])) print(ranges[2], np.nanmin(meds[2]), np.nanmax(meds[2]))
def measure_SNR(spex_path, data_path, plot_path, star, ranges): """ Measure the SNR of a spectrum, by fitting straight lines to pieces of the spectrum """ # plot the spectrum to define regions without spectral lines fig, ax = plot_spectrum(star, data_path, range=[0.75, 5.6], log=True) # read in all bands and spectra for this star starobs = StarData("%s.dat" % star.lower(), path=data_path, use_corfac=True) # obtain flux values at a few wavelengths and fit a straight line through the data waves, fluxes, uncs = starobs.get_flat_data_arrays(["SpeX_SXD", "SpeX_LXD"]) print(star) for range in ranges: min_indx = np.abs(waves - range[0]).argmin() max_indx = np.abs(waves - range[1]).argmin() func = Linear1D() fit = LinearLSQFitter() fit_result = fit(func, waves[min_indx:max_indx], fluxes[min_indx:max_indx]) residu = fluxes[min_indx:max_indx] - fit_result(waves[min_indx:max_indx]) # calculate the SNR from the data data_sxd = Table.read( spex_path + star + "_sxd.txt", format="ascii", ) data_lxd = Table.read( spex_path + star + "_lxd.txt", format="ascii", ) data = vstack([data_sxd, data_lxd]) data.sort("col1") print("wave_range", range) min_indx2 = np.abs(data["col1"] - range[0]).argmin() max_indx2 = np.abs(data["col1"] - range[1]).argmin() SNR_data = np.nanmedian((data["col2"] / data["col3"])[min_indx2:max_indx2]) print("SNR from data", SNR_data) # calculate the SNR from the noise around the linear fit mean, median, stddev = sigma_clipped_stats(residu) SNR_fit = np.median(fluxes[min_indx:max_indx] / stddev) print("SNR from fit", SNR_fit) # plot the fitted lines on top of the spectrum ax.plot( waves[min_indx:max_indx], fit_result(waves[min_indx:max_indx]), lw=2, alpha=0.8, color="k", ) fig.savefig(plot_path + star + "_SNR_measure.pdf")
def plot_multi_spectra( starlist, path, mlam4=False, HI_lines=False, range=None, norm_range=None, spread=False, exclude=[], log=False, class_offset=True, text_offsets=[], text_angles=[], pdf=False, outname="all_spec.pdf", ): """ Plot the observed band and spectral data of multiple stars in the same plot Parameters ---------- starlist : list of strings List of stars for which to plot the spectrum path : string Path to the data files mlam4 : boolean [default=False] Whether or not to multiply the flux F(lambda) by lambda^4 to remove the Rayleigh-Jeans slope HI_lines : boolean [default=False] Whether or not to indicate the HI-lines in the plot range : list of 2 floats [default=None] Wavelength range to be plotted (in micron) - [min,max] norm_range : list of 2 floats [default=None] Wavelength range to use to normalize the data (in micron)- [min,max] spread : boolean [default=False] Whether or not to spread the spectra out by adding a vertical offset to each spectrum exclude : list of strings [default=[]] List of data type(s) to exclude from the plot (e.g., IRS) log : boolean [default=False] Whether or not to plot the wavelengths on a log-scale class_offset : boolean [default=True] Whether or not to add an extra offset between main sequence and giant stars (only relevant when spread=True; this only works when the stars are sorted by spectral class, i.e. first the main sequence and then the giant stars) text_offsets : list of floats [default=[]] List of the same length as starlist with offsets for the annotated text text_angles : list of integers [default=[]] List of the same length as starlist with rotation angles for the annotated text pdf : boolean [default=False] Whether or not to save the figure as a pdf file outname : string [default="all_spec.pdf"] Name for the output pdf file Returns ------- Figure with band data points and spectra of multiple stars """ # plotting setup for easier to read plots fontsize = 18 font = {"size": fontsize} plt.rc("font", **font) plt.rc("lines", linewidth=1) plt.rc("axes", linewidth=2) plt.rc("xtick.major", width=2) plt.rc("xtick.minor", width=2) plt.rc("ytick.major", width=2) plt.rc("ytick.minor", width=2) # create the plot fig, ax = plt.subplots(figsize=(15, len(starlist) * 1.25)) colors = plt.get_cmap("tab10") if norm_range is not None: norm_range = norm_range * u.micron # set default text offsets and angles if text_offsets == []: text_offsets = np.full(len(starlist), 0.2) if text_angles == []: text_angles = np.full(len(starlist), 10) for i, star in enumerate(starlist): # read in all bands and spectra for this star starobs = StarData("%s.dat" % star.lower(), path=path, use_corfac=True) # spread out the spectra if requested # add extra whitespace when the luminosity class changes from main sequence to giant if spread: extra_off = 0 if "V" not in starobs.sptype and class_offset: extra_off = 1 yoffset = extra_off + 0.5 * i else: yoffset = 0 # determine where to add the name of the star and its spectral type # find the shortest plotted wavelength, and give preference to spectral data when available exclude2 = [] if "BAND" in starobs.data.keys() and len(starobs.data.keys()) > 1: exclude2 = ["BAND"] (waves, fluxes, flux_uncs) = starobs.get_flat_data_arrays(starobs.data.keys() - (exclude + exclude2)) if range is not None: waves = waves[waves >= range[0]] min_wave = waves[0] # find out which data type corresponds with this wavelength for data_type in starobs.data.keys(): if data_type in exclude: continue used_waves = starobs.data[data_type].waves[ starobs.data[data_type].npts > 0] if min_wave in used_waves.value: ann_key = data_type ann_range = [min_wave, min_wave] * u.micron # plot the spectrum starobs.plot( ax, pcolor=colors(i % 10), norm_wave_range=norm_range, mlam4=mlam4, exclude=exclude, yoffset=yoffset, yoffset_type="add", annotate_key=ann_key, annotate_wave_range=ann_range, annotate_text=star.upper() + " " + starobs.sptype, annotate_yoffset=text_offsets[i], annotate_rotation=text_angles[i], annotate_color=colors(i % 10), ) # plot HI-lines if requested if HI_lines: plot_HI(path, ax) # zoom in on a specific region if requested if range is not None: zoom(ax, range) outname = outname.replace(".pdf", "_zoom.pdf") # finish configuring the plot if not spread: ax.set_yscale("log") if log: ax.set_xscale("log") ax.set_xlabel(r"$\lambda$ [$\mu m$]", fontsize=1.5 * fontsize) ylabel = r"$F(\lambda)$" if norm_range is not None: if norm_range[0].unit == "micron": units = r"$\mu m$" else: units = norm_range[0].unit ylabel += "/$F$(" + str(int(np.mean(norm_range).value)) + units + ")" else: ylabel += r" [$ergs\ cm^{-2}\ s^{-1}\ \AA^{-1}$]" if mlam4: ylabel = r"$\lambda^4$" + ylabel.replace("]", r" $\mu m^4$]") outname = outname.replace("spec", "spec_mlam4") if spread: ylabel += " + offset" ax.set_ylabel(ylabel, fontsize=1.5 * fontsize) ax.tick_params("both", length=10, width=2, which="major") ax.tick_params("both", length=5, width=1, which="minor") # show the figure or save it to a pdf file if pdf: fig.savefig(path + outname, bbox_inches="tight") else: plt.show() # return the figure and axes for additional manipulations return fig, ax