Example #1
0
def test_get_fitdata():

    data_path = pkg_resources.resource_filename("measure_extinction", "data/")

    # read in the observed data of the stars
    redstar = StarData("hd229238.dat", path=data_path)
    compstar = StarData("hd204172.dat", path=data_path)

    # calculate the extinction curve
    ext = ExtData()
    ext.calc_elx(redstar, compstar)

    # once wavelenth units saved, update FITS file and use this line instead
    # of the 4 lines above

    # ext = ExtData(filename=data_path + "hd283809_hd064802_ext.fits")

    wave, y, unc = ext.get_fitdata(
        ["BAND", "IUE"], remove_uvwind_region=True, remove_lya_region=True
    )

    # fitting routines often cannot handle units, make sure none are present
    for cursrc in ext.waves.keys():
        assert isinstance(wave, u.Quantity)
        assert not isinstance(y, u.Quantity)
        assert not isinstance(unc, u.Quantity)
Example #2
0
    def SED_to_StarData(self, sed):
        """
        Convert the model created SED into a StarData object.
        Needed to plug into generating an ExtData object.

        Parameters
        ----------
        sed : object
            SED of each component
        """
        sd = StarData(None)

        for cspec in sed.keys():
            if cspec == "BAND":
                # populate the BAND info
                sd.data["BAND"] = BandData("BAND")
                for k, cband in enumerate(self.band_names):
                    sd.data["BAND"].band_fluxes[cband] = (sed["BAND"][k], 0.0)
                sd.data["BAND"].get_band_mags_from_fluxes()
            else:
                # populate the spectral info
                sd.data[cspec] = SpecData(cspec)
                sd.data[cspec].waves = self.waves[cspec]
                sd.data[cspec].n_waves = len(sd.data[cspec].waves)
                sd.data[cspec].fluxes = sed[cspec] * (
                    u.erg / ((u.cm ** 2) * u.s * u.angstrom)
                )
                sd.data[cspec].uncs = 0.0 * sd.data[cspec].fluxes
                sd.data[cspec].npts = np.full((sd.data[cspec].n_waves), 1.0)
                print(sd.data[cspec].fluxes)

        return sd
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]))
Example #4
0
def calc_extinction(redstarname, compstarname, path):
    # read in the observed data for both stars
    redstarobs = StarData("%s.dat" % redstarname.lower(), path=path)
    compstarobs = StarData("%s.dat" % compstarname.lower(), path=path)

    # calculate the extinction curve
    extdata = ExtData()
    extdata.calc_elx(redstarobs, compstarobs)

    extdata.save(path + "%s_%s_ext.fits" %
                 (redstarname.lower(), compstarname.lower()))
Example #5
0
def calc_save_corfac_spex(starname, path):
    # read in the data file (do not use the correction factors at this point)
    star_data = StarData("%s.dat" % starname.lower(),
                         path=path,
                         use_corfac=False)
    star_phot = star_data.data["BAND"]

    # if the LXD scaling factor has been set manually, warn the user and do not recalculate the scaling factors.
    if star_data.LXD_man:
        print(
            "The LXD scaling factor has been set manually and the scaling factors (SXD and LXD) will not be recalculated!"
        )
        return

    # check which spectra are available,
    # and calculate the correction factors for the spectra (if they have not been set manually)
    if "SpeX_SXD" in star_data.data.keys():
        star_spec_SXD = star_data.data["SpeX_SXD"]
        corfac_SXD = calc_corfac(star_phot, star_spec_SXD, ["J", "H", "K"])
    else:
        corfac_SXD = None
    if "SpeX_LXD" in star_data.data.keys():
        star_spec_LXD = star_data.data["SpeX_LXD"]
        corfac_LXD = calc_corfac(
            star_phot, star_spec_LXD,
            ["IRAC1", "IRAC2", "WISE1", "WISE2", "L", "M"])
    else:
        corfac_LXD = None

    # add the correction factors to the data file if not already in there,
    # otherwise overwrite the existing correction factors
    if "SpeX_SXD" in star_data.corfac.keys():
        datafile = open(path + "%s.dat" % starname.lower(), "w")
        for ln, line in enumerate(star_data.datfile_lines):
            if "corfac_spex_SXD" in line:
                star_data.datfile_lines[ln] = ("corfac_spex_SXD = " +
                                               str(corfac_SXD) + "\n")
            datafile.write(star_data.datfile_lines[ln])
        datafile.close()
    else:
        with open(path + "%s.dat" % starname.lower(), "a") as datafile:
            datafile.write("corfac_spex_SXD = " + str(corfac_SXD) + "\n")
    if "SpeX_LXD" in star_data.corfac.keys():
        datafile = open(path + "%s.dat" % starname.lower(), "w")
        for ln, line in enumerate(star_data.datfile_lines):
            if "corfac_spex_LXD" in line:
                star_data.datfile_lines[ln] = ("corfac_spex_LXD = " +
                                               str(corfac_LXD) + "\n")
            datafile.write(star_data.datfile_lines[ln])
        datafile.close()
    else:
        with open(path + "%s.dat" % starname.lower(), "a") as datafile:
            datafile.write("corfac_spex_LXD = " + str(corfac_LXD) + "\n")
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 get_colors(starnames):

    # standard stars
    n_stars = len(starnames)
    xvals = np.zeros((n_stars, 2))
    yvals = np.zeros((n_stars, 2))
    for i in range(n_stars):
        stardata = StarData(subpath + starnames[i] + ".dat",
                            path=path,
                            use_corfac=True)

        b1vals = stardata.data["BAND"].get_band_mag(xbands[0])
        b2vals = stardata.data["BAND"].get_band_mag(xbands[1])
        if (b1vals is not None) and (b2vals is not None):
            xvals[i, 0] = b1vals[0] - b2vals[0]
            xvals[i, 1] = np.sqrt((b1vals[1]**2) + (b2vals[1]**2))

        b1vals = stardata.data["BAND"].get_band_mag(ybands[0])
        b2vals = stardata.data["BAND"].get_band_mag(ybands[1])

        if (b1vals is not None) and (b2vals is not None):
            yvals[i, 0] = b1vals[0] - b2vals[0]
            yvals[i, 1] = np.sqrt((b1vals[1]**2) + (b2vals[1]**2))

    return (xvals, yvals)
Example #8
0
def test_load_stardata():
    # get the location of the data files
    data_path = pkg_resources.resource_filename("measure_extinction", "data/")

    # read in the observed data on the star
    star = StarData("hd229238.dat", path=data_path)

    assert "BAND" in star.data.keys()
    assert "IUE" in star.data.keys()
Example #9
0
def test_calc_AV_RV():
    # get the location of the data files
    data_path = pkg_resources.resource_filename("measure_extinction", "data/")

    # read in the observed data of the stars
    redstar = StarData("hd229238.dat", path=data_path)
    compstar = StarData("hd204172.dat", path=data_path)

    # calculate the extinction curve
    ext = ExtData()
    ext.calc_elx(redstar, compstar)

    # calculate A(V)
    ext.calc_AV()
    np.testing.assert_almost_equal(ext.columns["AV"], 2.5626900237367805)

    # calculate R(V)
    ext.calc_RV()
    np.testing.assert_almost_equal(ext.columns["RV"], 2.614989769244703)
Example #10
0
def test_calc_ext():
    # get the location of the data files
    data_path = pkg_resources.resource_filename("measure_extinction", "data/")

    # read in the observed data of the stars
    redstar = StarData("hd229238.dat", path=data_path)
    compstar = StarData("hd204172.dat", path=data_path)

    # calculate the extinction curve
    ext = ExtData()
    ext.calc_elx(redstar, compstar)

    # test that the quantities have units (or not as appropriate)
    for cursrc in ext.waves.keys():
        assert isinstance(ext.waves[cursrc], u.Quantity)
        assert not isinstance(ext.exts[cursrc], u.Quantity)
        assert not isinstance(ext.uncs[cursrc], u.Quantity)
        assert not isinstance(ext.npts[cursrc], u.Quantity)

    # check that the wavelengths can be converted to microns
    for cursrc in ext.waves.keys():
        twave = ext.waves[cursrc].to(u.micron)
        assert twave.unit == u.micron
Example #11
0
def plot_mir_set(
    ax,
    starnames,
    extra_off_val=0.0,
    plam4=True,
    norm_wave_range=[6.0, 10.0] * u.micron,
    col_vals=["b", "g", "r", "m", "c", "y"],
    ann_xvals=[35.0, 42.0] * u.micron,
    ann_wave_range=[9.0, 15.0] * u.micron,
    ann_rot=5.0,
    ann_offset=0.2,
    fontsize=12,
    path="/home/kgordon/Python_git/extstar_data/",
    subpath="DAT_files/",
):
    """
    Plot a set of spectra
    """
    n_col = len(col_vals)
    for i in range(len(starnames)):
        stardata = StarData(subpath + starnames[i] + ".dat", path=path, use_corfac=True)

        stardata.plot(
            ax,
            mlam4=True,
            norm_wave_range=norm_wave_range,
            yoffset=extra_off_val + 0.5 * i,
            yoffset_type="add",
            pcolor=col_vals[i % n_col],
            annotate_key="IRS",
            annotate_wave_range=ann_wave_range,
            annotate_text=starnames[i] + " " + stardata.sptype,
            fontsize=fontsize,
            annotate_rotation=ann_rot,
            annotate_yoffset=ann_offset,
        )
Example #12
0
    def read_tlusty_models(self, filebase, path):
        """
        Read in the TLusty stellar model atmosphere predictions.
        All are assumed to be in the measure_extinction data format.
        """
        self.filebase = filebase

        # read in the model data
        model_files = glob.glob("%s/%s" % (path, filebase))

        # basic stellar atmosphere data
        self.n_models = len(model_files)
        self.temps = np.zeros(self.n_models)
        self.gravs = np.zeros(self.n_models)
        self.mets = np.zeros(self.n_models)

        # read in the stellar atmosphere models
        self.model_spectra = []
        for i, file in enumerate(model_files):
            # decode the filename to get the stellar parameters
            spos = file.rfind('/')
            Tpos = file.find('T', spos)
            gpos = file.find('g', spos)
            vpos = file.find('v', spos)
            zpos = file.find('z', spos)
            dpos = file.find('.dat', spos)
            self.temps[i] = np.log10(float(file[Tpos + 1:gpos]))
            self.gravs[i] = float(file[gpos + 1:vpos]) * 1e-2
            self.mets[i] = np.log10(float(file[zpos + 1:dpos]))

            # get the data
            self.model_spectra.append(StarData(file), path='../')

        # provide the width in model space for each parameter
        # used in calculating the nearest neighbors
        self.temp_min = min(self.temps)
        self.temp_max = max(self.temps)
        self.temp_width2 = (self.temp_max - self.temp_min)**2
        self.temp_width2 = 1.0

        self.grav_min = min(self.gravs)
        self.grav_max = max(self.gravs)
        self.grav_width2 = (self.grav_max - self.grav_min)**2

        self.met_min = min(self.mets)
        self.met_max = max(self.mets)
        self.met_width2 = (self.met_max - self.met_min)**2
        self.met_width2 *= 4.0
Example #13
0
def test_units_stardata():
    # get the location of the data files
    data_path = pkg_resources.resource_filename("measure_extinction", "data/")

    # read in the observed data of the star
    star = StarData("hd229238.dat", path=data_path)

    # test that the quantities have units
    for cursrc in star.data.keys():
        assert isinstance(star.data[cursrc].waves, u.Quantity)
        assert isinstance(star.data[cursrc].wave_range, u.Quantity)
        assert isinstance(star.data[cursrc].fluxes, u.Quantity)
        assert isinstance(star.data[cursrc].uncs, u.Quantity)

    fluxunit = u.erg / ((u.cm**2) * u.s * u.angstrom)
    # check that the wavelengths can be converted to microns and the
    # flux units can be converted to spectral density
    for cursrc in star.data.keys():
        twave = star.data[cursrc].waves.to(u.micron)
        assert twave.unit == u.micron

        tflux = star.data[cursrc].fluxes.to(
            fluxunit, equivalencies=u.spectral_density(twave))
        assert tflux.unit == fluxunit
Example #14
0
def plot_spectrum(
    star,
    path,
    mlam4=False,
    HI_lines=False,
    range=None,
    norm_range=None,
    exclude=[],
    pdf=False,
):
    """
    Plot the observed band and spectral data of a star

    Parameters
    ----------
    star : string
        Name of the star 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]

    exclude : list of strings [default=[]]
        List of data type(s) to exclude from the plot (e.g., IRS)

    pdf : boolean [default=False]
        Whether or not to save the figure as a pdf file

    Returns
    -------
    Figure with band data points and spectrum
    """
    # 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=(13, 10))

    # read in and plot all bands and spectra for this star
    starobs = StarData("%s.dat" % star.lower(), path=path, use_corfac=True)
    if norm_range is not None:
        norm_range = norm_range * u.micron
    starobs.plot(ax, norm_wave_range=norm_range, mlam4=mlam4, exclude=exclude)

    # plot HI-lines if requested
    if HI_lines:
        plot_HI(path, ax)

    # define the output name
    outname = star.lower() + "_spec.pdf"

    # 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
    ax.set_xscale("log")
    ax.set_yscale("log")
    ax.set_title(star.upper(), fontsize=50)
    ax.set_xlabel(r"$\lambda$ [$\mu m$]", fontsize=1.5 * fontsize)
    if mlam4:
        ax.set_ylabel(
            r"$F(\lambda)\ \lambda^4$ [$ergs\ cm^{-2}\ s^{-1}\ \AA^{-1}\ \mu m^4$]",
            fontsize=1.5 * fontsize,
        )
        outname = outname.replace("spec", "spec_mlam4")
    else:
        ax.set_ylabel(
            r"$F(\lambda)$ [$ergs\ cm^{-2}\ s^{-1}\ \AA^{-1}$]",
            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")
        plt.close()
    else:
        plt.show()
    parser.add_argument("--pdf",
                        help="save figure as a pdf file",
                        action="store_true")
    args = parser.parse_args()

    filename = args.filelist

    f = open(filename, 'r')
    file_lines = list(f)
    starnames = []
    stardata = []
    for line in file_lines:
        if (line.find('#') != 0) & (len(line) > 0):
            name = line.rstrip()
            starnames.append(name)
            tstar = StarData('DAT_files/' + name + '.dat',
                             path='/home/kgordon/Dust/Ext/')
            stardata.append(tstar)

    fontsize = 14

    font = {'size': fontsize}
    mpl.rc('font', **font)
    mpl.rc('lines', linewidth=1)
    mpl.rc('axes', linewidth=2)
    mpl.rc('xtick.major', width=2)
    mpl.rc('xtick.minor', width=2)
    mpl.rc('ytick.major', width=2)
    mpl.rc('ytick.minor', width=2)

    fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(16, 6))
Example #16
0
def plot_uv_set(ax,
                starnames,
                extra_off_val=0.0,
                norm_wave_range=[0.2, 0.3],
                col_vals=['b', 'g', 'r', 'm', 'c', 'y'],
                ann_xvals=[0.25],
                ann_wave_range=[0.2, 0.3],
                fontsize=12):
    """
    Plot a set of spectra
    """

    spec_name = 'STIS'
    for i in range(len(starnames)):
        stardata = StarData('DAT_files/' + starnames[i] + '.dat',
                            path='/home/kgordon/Python_git/extstar_data/',
                            use_corfac=True)

        ymult = np.full((len(stardata.data[spec_name].waves)), 1.0)

        # get the value to use for normalization and offset
        norm_indxs = np.where(
            (stardata.data[spec_name].waves >= norm_wave_range[0])
            & (stardata.data[spec_name].waves <= norm_wave_range[1]))
        norm_val = 1.0 / np.average(
            stardata.data[spec_name].fluxes[norm_indxs] * ymult[norm_indxs])
        off_val = extra_off_val + 10.0 * (i) + 1.0
        print(off_val)

        # plot the spectroscopic data
        gindxs = np.where(stardata.data[spec_name].npts > 0)
        # max_gwave = max(stardata.data[spec_name].waves[gindxs])
        bnpts = 11
        xvals = smooth(stardata.data[spec_name].waves[gindxs], bnpts)
        yvals = smooth(stardata.data[spec_name].fluxes[gindxs], bnpts)
        ax.plot(xvals[0:-5],
                (yvals[0:-5] * ymult[gindxs][0:-5] * norm_val * off_val),
                col_vals[i % 6] + '-')

        # annotate the spectra
        # ann_wave_range = np.array([max_gwave-5.0, max_gwave-1.0])
        ann_indxs = np.where(
            (stardata.data[spec_name].waves >= ann_wave_range[0])
            & (stardata.data[spec_name].waves <= ann_wave_range[1])
            & (stardata.data[spec_name].npts > 0))
        ann_val = np.median(stardata.data[spec_name].fluxes[ann_indxs] *
                            ymult[ann_indxs])
        ann_val *= norm_val
        ann_val += off_val + 2.0
        # ax.annotate(starnames[i]+' '+stardata.sptype, xy=(ann_xvals[0],
        #                                                   ann_val),
        #             xytext=(ann_xvals[1], ann_val),
        #             verticalalignment="center",
        #             arrowprops=dict(facecolor=col_vals[i % 6], shrink=0.1),
        #             fontsize=0.85*fontsize, rotation=-0.)

        # plot the band fluxes
        ymult = np.full((len(stardata.data['BAND'].waves)), 1.0)
        ax.plot(stardata.data['BAND'].waves,
                stardata.data['BAND'].fluxes * ymult * norm_val * off_val,
                col_vals[i % 6] + 'o')
Example #17
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
Example #18
0
    def __init__(
        self,
        modelfiles,
        path="./",
        band_names=["U", "B", "V", "J", "H", "K"],
        spectra_names=["BAND", "STIS"],
    ):

        self.n_models = len(modelfiles)
        self.model_files = np.array(modelfiles)

        # physical parameters of models
        self.temps = np.zeros(self.n_models)
        self.gravs = np.zeros(self.n_models)
        self.mets = np.zeros(self.n_models)
        self.vturb = np.zeros(self.n_models)

        # photometric band data
        self.n_bands = len(band_names)
        self.band_names = band_names

        # photometric and spectroscopic data
        self.n_spectra = len(spectra_names) + 1
        self.spectra_names = spectra_names
        self.waves = {}
        self.fluxes = {}
        self.flux_uncs = {}

        for cspec in self.spectra_names:
            self.fluxes[cspec] = None
            self.flux_uncs[cspec] = None

        # initialize the BAND dictonary entry as the number of elements
        # is set by the desired bands, not the bands in the files
        self.waves["BAND"] = np.zeros((self.n_bands))
        self.fluxes["BAND"] = np.zeros((self.n_models, self.n_bands))
        self.flux_uncs["BAND"] = np.zeros((self.n_models, self.n_bands))

        # read and store the model data
        for k, cfile in enumerate(modelfiles):
            moddata = StarData(cfile, path=path)

            # model parameters
            self.temps[k] = np.log10(float(moddata.model_params["Teff"]))
            self.gravs[k] = float(moddata.model_params["logg"])
            self.mets[k] = np.log10(float(moddata.model_params["Z"]))
            self.vturb[k] = float(moddata.model_params["vturb"])

            # spectra
            for cspec in self.spectra_names:
                # initialize the spectra vectors
                if self.fluxes[cspec] is None:
                    self.waves[cspec] = moddata.data[cspec].waves
                    self.fluxes[cspec] = np.zeros(
                        (self.n_models, len(moddata.data[cspec].fluxes))
                    )
                    self.flux_uncs[cspec] = np.zeros(
                        (self.n_models, len(moddata.data[cspec].fluxes))
                    )

                # photometric bands
                if cspec == "BAND":
                    for i, cband in enumerate(self.band_names):
                        band_flux = moddata.data["BAND"].get_band_flux(cband)
                        self.waves[cspec][i] = band_flux[2]
                        self.fluxes[cspec][k, i] = band_flux[0]
                        self.flux_uncs[cspec][k, i] = band_flux[1]
                else:
                    # get the spectral data
                    self.fluxes[cspec][k, :] = moddata.data[cspec].fluxes
                    self.flux_uncs[cspec][k, :] = moddata.data[cspec].uncs

        # add units
        self.waves["BAND"] = self.waves["BAND"] * u.micron

        # provide the width in model space for each parameter
        #   used in calculating the nearest neighbors
        self.n_nearest = 11

        self.temps_min = min(self.temps)
        self.temps_max = max(self.temps)
        self.temps_width2 = (self.temps_max - self.temps_min) ** 2
        # self.temp_width2 = 1.0

        self.gravs_min = min(self.gravs)
        self.gravs_max = max(self.gravs)
        self.gravs_width2 = (self.gravs_max - self.gravs_min) ** 2

        self.mets_min = min(self.mets)
        self.mets_max = max(self.mets)
        self.mets_width2 = (self.mets_max - self.mets_min) ** 2
Example #19
0
    mpl.rc("axes", linewidth=2)
    mpl.rc("xtick.major", width=2)
    mpl.rc("xtick.minor", width=2)
    mpl.rc("ytick.major", width=2)
    mpl.rc("ytick.minor", width=2)

    # setup the plot
    fig, ax = plt.subplots(ncols=2, figsize=(13, 8))

    # plot the spectra in two columns
    half_num = len(starnames) // 2 + 1
    col_vals = ["b", "g", "c"]
    n_cols = len(col_vals)
    for k, cstarname in enumerate(starnames):
        fstarname, file_path = get_full_starfile(cstarname)
        starobs = StarData(fstarname, path=file_path)
        if k // half_num > 0:
            yoff = 2.5**(k - half_num)
        else:
            yoff = 2.5**k
        starobs.plot(
            ax[k // half_num],
            norm_wave_range=[0.2, 0.3] * u.micron,
            yoffset=yoff,
            pcolor=col_vals[k % n_cols],
            annotate_key="IUE",
            annotate_wave_range=[0.25, 0.27] * u.micron,
            annotate_text=cstarname,
            annotate_rotation=-10.,
            annotate_yoffset=0.0,
            fontsize=12,
    parser.add_argument("--path", help="path to star files", default="./")
    parser.add_argument("--png", help="save figure as a png file", action="store_true")
    parser.add_argument("--eps", help="save figure as an eps file", action="store_true")
    parser.add_argument("--pdf", help="save figure as a pdf file", action="store_true")
    return parser


if __name__ == "__main__":

    # commandline parser
    parser = plot_spec_parser()
    args = parser.parse_args()

    # read in the observed data on the star
    fstarname, file_path = get_full_starfile("m33_j013334.26+303327")
    starobs = StarData(fstarname, path=file_path)
    band_names = starobs.data["BAND"].get_band_names()

    # get the model filenames
    print("reading in the model spectra")
    file_path = "/home/kgordon/Python_git/extstar_data/"
    tlusty_models_fullpath = glob.glob("{}/Models/tlusty_*v10.dat".format(file_path))
    tlusty_models_fullpath = tlusty_models_fullpath[0:10]
    tlusty_models = [
        tfile[tfile.rfind("/") + 1 : len(tfile)] for tfile in tlusty_models_fullpath
    ]

    spectra_names = ["BAND", "STIS"]
    # band_names = ['ACS_F814W', 'V', 'WFC3_F336W', 'WFC3_F160W',
    #               'ACS_F475W', 'WFC3_F110W', 'WFC3_F275W']
    # get the models with just the reddened star band data and spectra
Example #21
0
                      gt['G_mag'][ri[0]]*gt['G_flux_error'][ri[0]]
                      / gt['G_flux'][ri[0]])
            compplx = (gt['parallax'][ci[0]], gt['parallax_error'][ci[0]])
            compmag = (gt['G_mag'][ci[0]],
                       gt['G_mag'][ci[0]]*gt['G_flux_error'][ci[0]]
                       / gt['G_flux'][ci[0]])
            absext = compute_absext(redplx, compplx, redmag, compmag)
            if absext[0] is not None:
                gext.append(absext[0])
                gext_unc.append(absext[1])

                # calculate the V band abs extinction
                redname = '/home/kgordon/Python_git/measured_extcurves/data/DAT_files/%s.dat' % (rname)
                compname = '/home/kgordon/Python_git/measured_extcurves/data/DAT_files/%s.dat' % (cname)
                if os.path.isfile(redname) and os.path.isfile(compname):
                    redstar = StarData(redname, photonly=True)
                    compstar = StarData(compname, photonly=True)
                    v_absext = compute_absext(redplx, compplx,
                                              redstar.data['BAND'].bands['V'],
                                              compstar.data['BAND'].bands['V'])
                    abs_av.append(v_absext[0])
                    abs_av_unc.append(v_absext[1])
                else:
                    abs_av.append(-1.0)
                    abs_av_unc.append(-1.0)

                # get the extrapolated derived value from FITS extinction curve
                fname = "%s/%s_%s/%s_%s_ext_bin.fits" \
                    % (fitspath, rname.lower(), cname.lower(),
                       rname.lower(), cname.lower())
                if os.path.isfile(fname):
Example #22
0
                     zip(*np.percentile(new_samples, [16, 50, 84],
                                        axis=0)))

    return per_params


if __name__ == '__main__':
    # commandline parser
    parser = fit_model_parser()
    args = parser.parse_args()

    # get the full starfilename and path
    fstarname, file_path = get_full_starfile(args.starname)

    # get the observed reddened star data
    reddened_star = StarData(fstarname, path=file_path)
    band_names = reddened_star.data['BAND'].get_band_names()
    spectra_names = reddened_star.data.keys()

    # override for now
    print('possible', spectra_names)
    # spectra_names = ['BAND', 'STIS_Opt']
    # spectra_names = ['BAND', 'STIS_Opt']
    print('only using', spectra_names)

    # override for now
    # band_names = ['U', 'B', 'V']
    # band_names = ['U', 'B', 'V', 'J', 'H', 'K']
    print(band_names)

    # get just the filenames
def fit_features_spec(star, path):
    """
    Fit the features directly from the spectrum with different profiles

    Parameters
    ----------
    star : string
        Name of the reddened star for which to fit the features in the spectrum

    path : string
        Path to the data files

    Returns
    -------
    waves : np.ndarray
        Numpy array with wavelengths

    flux_sub : np.ndarray
        Numpy array with continuum subtracted fluxes

    results : list
        List with the fitted models for different profiles
    """
    # obtain the spectrum of the reddened star
    stardata = StarData(star + ".dat", path)
    npts = stardata.data["SpeX_LXD"].npts
    waves = stardata.data["SpeX_LXD"].waves.value
    flux_unc = stardata.data["SpeX_LXD"].uncs

    # "manually" obtain the continuum from the spectrum (i.e. read the flux at 2.4 and 3.6 micron)
    plot_spectrum(
        star,
        path,
        mlam4=True,
        range=[2, 4.5],
        exclude=["IRS", "STIS_Opt"],
    )

    # fit the continuum reference points with a straight line
    ref_waves = [2.4, 3.6]
    fluxes = [3.33268e-12, 4.053e-12]
    func = Linear1D()
    fit = LinearLSQFitter()
    fit_result = fit(func, ref_waves, fluxes)

    # subtract the continuum from the fluxes
    fluxes = stardata.data["SpeX_LXD"].fluxes.value * waves ** 4 - fit_result(waves)

    # define different profiles
    # 2 Gaussians (stddev=FWHM/(2sqrt(2ln2)))
    gauss = Gaussian1D(mean=3, stddev=0.13) + Gaussian1D(
        mean=3.4, stddev=0.06, fixed={"mean": True}
    )

    # 2 Drudes
    drude = Drude1D(x_0=3, fwhm=0.3) + Drude1D(x_0=3.4, fwhm=0.15, fixed={"x_0": True})

    # 2 Lorentzians
    lorentz = Lorentz1D(x_0=3, fwhm=0.3) + Lorentz1D(
        x_0=3.4, fwhm=0.15, fixed={"x_0": True}
    )

    # 2 asymmetric Gaussians
    Gaussian_asym = custom_model(gauss_asymmetric)
    gauss_asym = Gaussian_asym(x_o=3, gamma_o=0.3) + Gaussian_asym(
        x_o=3.4, gamma_o=0.15, fixed={"x_o": True}
    )

    # 2 "asymmetric" Drudes
    Drude_asym = custom_model(drude_asymmetric)
    drude_asym = Drude_asym(x_o=3, gamma_o=0.3) + Drude_asym(
        x_o=3.4, gamma_o=0.15, fixed={"x_o": True}
    )

    # 2 asymmetric Lorentzians
    Lorentzian_asym = custom_model(lorentz_asymmetric)
    lorentz_asym = Lorentzian_asym(x_o=3, gamma_o=0.3) + Lorentzian_asym(
        x_o=3.4, gamma_o=0.15, fixed={"x_o": True}
    )

    # 1 asymmetric Drude
    drude_asym1 = Drude_asym(x_o=3, gamma_o=0.3)

    profiles = [
        gauss,
        drude,
        lorentz,
        gauss_asym,
        drude_asym,
        lorentz_asym,
        drude_asym1,
    ]

    # fit the different profiles
    fit2 = LevMarLSQFitter()
    results = []
    mask1 = (waves > 2.4) & (waves < 3.6)
    mask2 = mask1 * (npts > 0)

    for profile in profiles:
        fit_result = fit2(
            profile,
            waves[mask2],
            fluxes[mask2],
            weights=1 / flux_unc[mask2],
            maxiter=10000,
        )
        results.append(fit_result)
        print(fit_result)
        print(
            "Chi2",
            np.sum(((fluxes[mask2] - fit_result(waves[mask2])) / flux_unc[mask2]) ** 2),
        )

    return waves[mask1], fluxes[mask1], npts[mask1], results
Example #24
0
    mpl.rc("font", **font)
    mpl.rc("lines", linewidth=1)
    mpl.rc("axes", linewidth=2)
    mpl.rc("xtick.major", width=2)
    mpl.rc("xtick.minor", width=2)
    mpl.rc("ytick.major", width=2)
    mpl.rc("ytick.minor", width=2)

    # setup the plot
    fig, ax = plt.subplots(figsize=(13, 10))

    # plot the bands and all spectra for this star
    # plot all the spectra on the same plot
    for k, cstarname in enumerate(starnames):
        fstarname, file_path = get_full_starfile(cstarname)
        starobs = StarData(fstarname, path=file_path)
        starobs.plot(ax, norm_wave_range=[0.2, 0.3] * u.micron, yoffset=2**k)

    # finish configuring the plot
    ax.set_yscale("log")
    ax.set_xscale("log")
    ax.set_xlabel(r"$\lambda$ [$\mu m$]", fontsize=1.3 * fontsize)
    ax.set_ylabel(r"$F(\lambda)$ [$ergs\ cm^{-2}\ s\ \AA$]",
                  fontsize=1.3 * fontsize)
    ax.tick_params("both", length=10, width=2, which="major")
    ax.tick_params("both", length=5, width=1, which="minor")

    # use the whitespace better
    fig.tight_layout()

    # plot or save to a file
Example #25
0
if __name__ == "__main__":

    # commandline parser
    parser = argparse.ArgumentParser()
    parser.add_argument("redstarname", help="name of reddened star")
    parser.add_argument("compstarname", help="name of comparision star")
    parser.add_argument(
        "--path",
        help="base path to observed data",
        default="/home/kgordon/Python_git/extstar_data/",
    )
    args = parser.parse_args()

    # read in the observed data for both stars
    redstarobs = StarData("DAT_files/%s.dat" % args.redstarname,
                          path=args.path)
    compstarobs = StarData("DAT_files/%s.dat" % args.compstarname,
                           path=args.path)

    # output filebase
    filebase = "fits/%s_%s" % (args.redstarname, args.compstarname)

    # calculate the extinction curve
    extdata = ExtData()
    extdata.calc_elx(redstarobs, compstarobs)

    # save the extinction curve
    out_fname = "fits/%s_%s_ext.fits" % (args.redstarname, args.compstarname)
    extdata.save(out_fname)
def print_BV(star):
    path = "/Users/mdecleir/Documents/NIR_ext/Data/"
    star_data = StarData("%s.dat" % star.lower(), path=path, use_corfac=False)
    V = star_data.data["BAND"].get_band_mag("V")
    B = star_data.data["BAND"].get_band_mag("B")
    print(star, ", B:", B, ", V:", V, ", B-V:", B[0] - V[0])
def plot_color_color(stars, bands, div):
    """
    Make a color-color plot

    Parameters
    ----------
    stars : list of strings
        List of stars

    bands : list of strings
        List of bands

    div : float
        Location of division line

    Returns
    -------
    IR color-color plot
    """
    # create the figure
    plt.rc("axes", linewidth=0.8)
    fig, ax = plt.subplots(figsize=(8, 7))

    for i, star in enumerate(stars):
        # categorize the star
        if star in comp_stars:
            color = "black"
            marker = "P"
        elif star in red_stars:
            color = "green"
            marker = "d"
        elif star == "HD014250":
            color = "purple"
            marker = "s"
        else:
            color = "red"
            marker = "o"

        # obtain the photometry
        star_data = StarData("%s.dat" % star.lower(), path=inpath)
        band_data = star_data.data["BAND"]
        mags = np.full(len(bands), np.nan)
        errs = np.full(len(bands), np.nan)
        for j, band in enumerate(bands):
            if band == "K_S":
                band = "K"
            if band in band_data.get_band_names():
                mags[j] = band_data.get_band_mag(band)[0]
                errs[j] = band_data.get_band_mag(band)[1]

        # plot colors
        ax.errorbar(
            mags[0] - mags[1],
            mags[2] - mags[3],
            xerr=np.sqrt((errs[0]**2) + (errs[1]**2)),
            yerr=np.sqrt((errs[2]**2) + (errs[3]**2)),
            marker=marker,
            color=color,
            markersize=6,
            markeredgewidth=0,
            elinewidth=0.8,
            alpha=0.7,
        )

    # finalize and save the plot
    ax.axhline(div, color="grey", ls=":")
    ax.set_xlabel(r"$" + bands[0] + "-" + bands[1] + "$", fontsize=fs)
    ax.set_ylabel(r"$" + bands[2] + "-$" + bands[3], fontsize=fs)
    ax.tick_params(width=1)

    labels = ["comparison", "reddened", "windy", "bad"]
    handle1 = Line2D([], [], lw=1, color="black", marker="P", alpha=0.7)
    handle2 = Line2D([], [], lw=1, color="green", marker="d", alpha=0.7)
    handle3 = Line2D([], [], lw=1, color="red", marker="o", alpha=0.7)
    handle4 = Line2D([], [], lw=1, color="purple", marker="s", alpha=0.7)
    handles = [handle1, handle2, handle3, handle4]
    ax.legend(handles, labels, fontsize=fs * 0.8)
    fig.savefig(outpath + "wind_" + bands[3] + ".pdf", bbox_inches="tight")
Example #28
0
    # commandline parser
    parser = argparse.ArgumentParser()
    parser.add_argument("filelist", help="file with list of stars to use")
    args = parser.parse_args()

    filename = args.filelist

    f = open(filename, "r")
    file_lines = list(f)
    starnames = []
    stardata = []
    bvcol = []
    for line in file_lines:
        if (line.find("#") != 0) & (len(line) > 0):
            name = line.rstrip()
            starnames.append(name)
            tstar = StarData(
                "DAT_files/" + name + ".dat",
                path="/home/kgordon/Python_git/extstar_data/",
            )
            if "IRS_slope" in tstar.corfac.keys():
                print("%s & %.3f & %.3f & %.3f \\\\" % (
                    name.upper(),
                    tstar.corfac["IRS"],
                    tstar.corfac["IRS_slope"],
                    tstar.corfac["IRS_zerowave"],
                ))
            else:
                print("%s & %.3f & \\nodata & \\nodata \\\\" %
                      (name.upper(), tstar.corfac["IRS"]))
def fit_spex_ext(
    starpair,
    path,
    functype="pow",
    dense=False,
    profile="drude_asym",
    exclude=None,
    bootstrap=False,
    fixed=False,
):
    """
    Fit the observed SpeX NIR extinction curve

    Parameters
    ----------
    starpair : string
        Name of the star pair for which to fit the extinction curve, in the format "reddenedstarname_comparisonstarname" (no spaces), or "average" to fit the average extinction curve

    path : string
        Path to the data files

    functype : string [default="pow"]
        Fitting function type ("pow" for powerlaw or "pol" for polynomial)

    dense : boolean [default=False]
        Whether or not to fit the features around 3 and 3.4 micron

    profile : string [default="drude_asym"]
        Profile to use for the features if dense = True (options are "gauss", "drude", "lorentz", "gauss_asym", "drude_asym", "lorentz_asym")

    exclude : list of tuples [default=None]
        list of tuples (min,max) with wavelength regions (in micron) that need to be excluded from the fitting, e.g. [(0.8,1.2),(2.2,5)]

    bootstrap : boolean [default=False]
        Whether or not to do a quick bootstrap fitting to get more realistic uncertainties on the fitting results

    fixed : boolean [default=False]
        Whether or not to add a fixed feature around 3 micron (for diffuse sightlines)

    Returns
    -------
    Updates extdata.model["type", "waves", "exts", "residuals", "chi2", "params"] and extdata.columns["AV"] with the fitting results:
        - type: string with the type of model (e.g. "pow_elx_Drude")
        - waves: np.ndarray with the SpeX wavelengths
        - exts: np.ndarray with the fitted model to the extinction curve at "waves" wavelengths
        - residuals: np.ndarray with the residuals, i.e. data-fit, at "waves" wavelengths
        - chi2 : float with the chi square of the fitting
        - params: list with output Parameter objects
    """
    # retrieve the SpeX data to be fitted, and sort the curve from short to long wavelengths
    filename = "%s%s_ext.fits" % (path, starpair.lower())
    if fixed:
        filename = filename.replace(".", "_ice.")
    extdata = ExtData(filename)
    (waves, exts, exts_unc) = extdata.get_fitdata(["SpeX_SXD", "SpeX_LXD"])
    indx = np.argsort(waves)
    waves = waves[indx].value
    exts = exts[indx]
    exts_unc = exts_unc[indx]

    # exclude wavelength regions if requested
    if exclude:
        mask = np.full_like(waves, False, dtype=bool)
        for region in exclude:
            mask += (waves > region[0]) & (waves < region[1])
        waves = waves[~mask]
        exts = exts[~mask]
        exts_unc = exts_unc[~mask]

    # get a quick estimate of A(V)
    if extdata.type == "elx":
        extdata.calc_AV()
        AV_guess = extdata.columns["AV"]
    else:
        AV_guess = None

    # convert to A(lambda)/A(1 micron)
    # ind1 = np.abs(waves - 1).argmin()
    # exts = exts / exts[ind1]
    # exts_unc = exts_unc / exts[ind1]

    # obtain the function to fit
    if "SpeX_LXD" not in extdata.waves.keys():
        dense = False
        fixed = False
    func = fit_function(
        dattype=extdata.type,
        functype=functype,
        dense=dense,
        profile=profile,
        AV_guess=AV_guess,
        fixed=fixed,
    )

    # for dense sightlines, add more weight to the feature region
    weights = 1 / exts_unc
    if dense:
        mask_ice = (waves > 2.88) & (waves < 3.19)
        mask_tail = (waves > 3.4) & (waves < 4)
        weights[mask_ice + mask_tail] *= 2

    # use the Levenberg-Marquardt algorithm to fit the data with the model
    fit = LevMarLSQFitter()
    fit_result_lev = fit(func, waves, exts, weights=weights, maxiter=10000)

    # set up the backend to save the samples for the emcee runs
    emcee_samples_file = path + "Fitting_results/" + starpair + "_emcee_samples.h5"

    # do the fitting again, with MCMC, using the results from the first fitting as input
    fit2 = EmceeFitter(nsteps=10000, burnfrac=0.1, save_samples=emcee_samples_file)

    # add parameter bounds
    for param in fit_result_lev.param_names:
        if "amplitude" in param:
            getattr(fit_result_lev, param).bounds = (0, 2)
        elif "alpha" in param:
            getattr(fit_result_lev, param).bounds = (0, 4)
        elif "Av" in param:
            getattr(fit_result_lev, param).bounds = (0, 10)

    fit_result_mcmc = fit2(fit_result_lev, waves, exts, weights=weights)

    # create standard MCMC plots
    fit2.plot_emcee_results(
        fit_result_mcmc, filebase=path + "Fitting_results/" + starpair
    )

    # choose the fit result to save
    fit_result = fit_result_mcmc
    # fit_result = fit_result_lev
    print(fit_result)

    # determine the wavelengths at which to evaluate and save the fitted model curve: all SpeX wavelengths, sorted from short to long (to avoid problems with overlap between SXD and LXD), and shortest and longest wavelength should have data
    if "SpeX_LXD" not in extdata.waves.keys():
        full_waves = extdata.waves["SpeX_SXD"].value
        full_npts = extdata.npts["SpeX_SXD"]
    else:
        full_waves = np.concatenate(
            (extdata.waves["SpeX_SXD"].value, extdata.waves["SpeX_LXD"].value)
        )
        full_npts = np.concatenate((extdata.npts["SpeX_SXD"], extdata.npts["SpeX_LXD"]))
    # sort the wavelengths
    indxs_sort = np.argsort(full_waves)
    full_waves = full_waves[indxs_sort]
    full_npts = full_npts[indxs_sort]
    # cut the wavelength region
    indxs = np.logical_and(full_waves >= np.min(waves), full_waves <= np.max(waves))
    full_waves = full_waves[indxs]
    full_npts = full_npts[indxs]

    # calculate the residuals and put them in an array of the same length as "full_waves" for plotting
    residuals = exts - fit_result(waves)
    full_res = np.full_like(full_npts, np.nan)
    if exclude:
        mask = np.full_like(full_waves, False, dtype=bool)
        for region in exclude:
            mask += (full_waves > region[0]) & (full_waves < region[1])
        full_res[(full_npts > 0) * ~mask] = residuals

    else:
        full_res[(full_npts > 0)] = residuals

    # bootstrap to get more realistic uncertainties on the parameter results
    if bootstrap:
        red_star = StarData(extdata.red_file, path=path, use_corfac=True)
        comp_star = StarData(extdata.comp_file, path=path, use_corfac=True)
        red_V_unc = red_star.data["BAND"].get_band_mag("V")[1]
        comp_V_unc = comp_star.data["BAND"].get_band_mag("V")[1]
        unc_V = np.sqrt(red_V_unc ** 2 + comp_V_unc ** 2)
        fit_result_mcmc_low = fit2(fit_result_lev, waves, exts - unc_V, weights=weights)
        fit_result_mcmc_high = fit2(
            fit_result_lev, waves, exts + unc_V, weights=weights
        )

    # save the fitting results to the fits file
    if dense:
        functype += "_" + profile
    extdata.model["type"] = functype + "_" + extdata.type
    extdata.model["waves"] = full_waves
    extdata.model["exts"] = fit_result(full_waves)
    extdata.model["residuals"] = full_res
    extdata.model["chi2"] = np.sum((residuals / exts_unc) ** 2)
    print("Chi2", extdata.model["chi2"])
    extdata.model["params"] = []
    for param in fit_result.param_names:
        # update the uncertainties when bootstrapping
        if bootstrap:
            min_val = min(
                getattr(fit_result_mcmc, param).value,
                getattr(fit_result_mcmc_low, param).value,
                getattr(fit_result_mcmc_high, param).value,
            )
            max_val = max(
                getattr(fit_result_mcmc, param).value,
                getattr(fit_result_mcmc_low, param).value,
                getattr(fit_result_mcmc_high, param).value,
            )
            sys_unc = (max_val - min_val) / 2
            getattr(fit_result, param).unc_minus = np.sqrt(
                getattr(fit_result, param).unc_minus ** 2 + sys_unc ** 2
            )
            getattr(fit_result, param).unc_plus = np.sqrt(
                getattr(fit_result, param).unc_plus ** 2 + sys_unc ** 2
            )

        extdata.model["params"].append(getattr(fit_result, param))

        # save the column information (A(V), E(B-V) and R(V))
        if "Av" in param:
            extdata.columns["AV"] = (
                getattr(fit_result, param).value,
                getattr(fit_result, param).unc_minus,
                getattr(fit_result, param).unc_plus,
            )
            # calculate the distrubtion of R(V) and 1/R(V) from the distributions of A(V) and E(B-V)
            nsamples = getattr(fit_result, param).posterior.n_samples
            av_dist = unc.normal(
                extdata.columns["AV"][0],
                std=(extdata.columns["AV"][1] + extdata.columns["AV"][2]) / 2,
                n_samples=nsamples,
            )
            b_indx = np.abs(extdata.waves["BAND"] - 0.438 * u.micron).argmin()
            ebv_dist = unc.normal(
                extdata.exts["BAND"][b_indx],
                std=extdata.uncs["BAND"][b_indx],
                n_samples=nsamples,
            )
            ebv_per = ebv_dist.pdf_percentiles([16.0, 50.0, 84.0])
            extdata.columns["EBV"] = (
                ebv_per[1],
                ebv_per[1] - ebv_per[0],
                ebv_per[2] - ebv_per[1],
            )
            rv_dist = av_dist / ebv_dist
            rv_per = rv_dist.pdf_percentiles([16.0, 50.0, 84.0])
            extdata.columns["RV"] = (
                rv_per[1],
                rv_per[1] - rv_per[0],
                rv_per[2] - rv_per[1],
            )
            inv_rv_dist = ebv_dist / av_dist
            inv_rv_per = inv_rv_dist.pdf_percentiles([16.0, 50.0, 84.0])
            extdata.columns["IRV"] = (
                inv_rv_per[1],
                inv_rv_per[1] - inv_rv_per[0],
                inv_rv_per[2] - inv_rv_per[1],
            )
            print(extdata.columns)

    # save the fits file
    extdata.save(filename)

    # print information about the ice feature
    if fixed:
        print(
            "Ice feature strength: ",
            extdata.model["params"][3].value,
            extdata.model["params"][3].unc_minus,
            extdata.model["params"][3].unc_plus,
        )
Example #30
0
                        action="store_true")
    parser.add_argument("--pdf",
                        help="save figure as a pdf file",
                        action="store_true")
    return parser


if __name__ == "__main__":

    # commandline parser
    parser = plot_spec_parser()
    args = parser.parse_args()

    # read in the observed data on the star
    fstarname, file_path = get_full_starfile(args.starname)
    starobs = StarData(fstarname, path=file_path)

    # plotting setup for easier to read plots
    fontsize = 18
    font = {'size': fontsize}
    mpl.rc('font', **font)
    mpl.rc('lines', linewidth=1)
    mpl.rc('axes', linewidth=2)
    mpl.rc('xtick.major', width=2)
    mpl.rc('xtick.minor', width=2)
    mpl.rc('ytick.major', width=2)
    mpl.rc('ytick.minor', width=2)

    # setup the plot
    fig, ax = plt.subplots(figsize=(13, 10))