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")
Exemple #3
0
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