def plotspecabun(fnames, base, geo='eclipse', xlims=(2.28905, 2.28945), oname=False, title=False): """ The function produces a plot of the spectra produced in abundance. All spectra are plotted together to show the difference in line depth. Inputs ------ fnames: list of strings. File names of the spectrum files. [fname1, fname2, fname3, ...] base : string. File name of the spectrum with the line moved. geo : string. Geometry of the produced spectrum. 'transit' or 'eclipse' xlims : tuple. Minimum and maximum X-axis values to be plotted. Default corresponds to the default line used in abundance. (xmin, xmax) oname: string. If oname is False, the default plotting name is used. If it is a string, then that path/to/plotname is used. title : bool If True, plots w/ title. If False, it does not. Outputs ------- PNG file of the portion of the spectrum. Naming convention is i.e. abundance_emission_spectra.png """ # Load and plot spectrum w/ line moved wl, fl = rt.readspectrum(base, 0) plt.plot(wl, fl, label='No line', color=(0, 0, 0)) # Loop over list of files for fn in range(len(fnames)): # Load data wlength, flux = rt.readspectrum(fnames[fn], 0) col = float(fn) / float(len(fnames)) # Plot it, with label and no repeat colors plt.plot(wlength, flux, label=fnames[fn].split('/')[-1].split('_')[1], \ color=(col, 0, 1-col)) # Set axis limits for the plot plt.xlim(xlims[0], xlims[1]) loc = np.where(fl - flux == np.amax(fl - flux))[0][0] plt.ylim(flux[loc] - 5, fl[loc] + 5) # Label rest of plot, save if title: plt.title('Varying Abundance of One Line, \n Optically Thin Regime') plt.ylabel("Flux (erg s$^{-1}$ cm$^{-1}$)", fontsize=14) plt.xlabel(u"Wavelength (\u00b5m)", fontsize=14) plt.legend(loc="best") if not oname: plt.savefig(base.split('/')[0] + '/' + 'abundance_emission_spectra.png', bbox_inches='tight') else: plt.savefig(oname, bbox_inches='tight') plt.clf()
def energycons(spectra, outfile): """ Integrates each spectrum, saves those values as well as if they all match within a specified tolerance. Inputs ------ spectra: list, strings. Path/to/file for each spectrum to be integrated. outfile: string. Path/to/file to save the results. """ # Array to hold integrated values integspec = np.zeros(len(spectra)) # Load each spectrum, integrate it for i in range(len(spectra)): wnums, flux = rt.readspectrum(spectra[i]) integspec[i] = np.trapz(flux, x=wnums) # Cubic spline returns roughly same value #integspec[i] = si.InterpolatedUnivariateSpline(wnums, flux, # k=3).integral(wnums[0], wnums[-1]) # Check if they match diff = integspec / integspec[0] - 1 # Save out result hdr = "# Units are erg/s/cm2" ftr = "# % differences compared to the first spectrum: \n" + \ "# " + str(100*diff) + " %" np.savetxt(outfile, integspec, header=hdr, footer=ftr)
def plot_bestFit_Spectrum(filters, kurucz, tepfile, solution, output, data, uncert, date_dir, fs=15): ''' Plot BART best-model spectrum Parameters ---------- filters : list, strings. Paths to filter files corresponding to data. kurucz : string. Path to Kurucz stellar model file. tepfile : string. Path to Transiting ExoPlanet (TEP) file. solution: string. Observing geometry. 'eclipse' or 'transit'. output : string. Best-fit spectrum output file name. data : 1D array. Eclipse or transit depths. uncert : 1D array. Uncertainties for data values. date_dir: string. Path to directory where the plot will be saved. fs : int. Font size for plots. ''' # get star data R_star, T_star, sma, gstar = get_starData(tepfile) # get surface gravity grav, Rp = mat.get_g(tepfile) # convert Rp to m Rp = Rp * 1000 # ratio planet to star rprs = Rp / R_star # read kurucz file starfl, starwn, tmodel, gmodel = w.readkurucz(kurucz, T_star, gstar) # read best-fit spectrum output file, take wn and spectra values if solution == 'eclipse': specwn, bestspectrum = rt.readspectrum(date_dir + output, wn=True) # print on screen print(" Plotting BART best-fit eclipse spectrum figure.") elif solution == 'transit': specwn, bestspectrum = rt.readspectrum(date_dir + output, wn=True) # print on screen print(" Plotting BART best-fit modulation spectrum figure.") # convert wn to wl specwl = 1e4 / specwn # number of filters nfilters = len(filters) # read and resample the filters: nifilter = [] # Normalized interpolated filter istarfl = [] # interpolated stellar flux wnindices = [] # wavenumber indices used in interpolation meanwn = [] # Filter mean wavenumber for i in np.arange(nfilters): # read filter: filtwaven, filttransm = w.readfilter(filters[i]) meanwn.append(np.sum(filtwaven * filttransm) / sum(filttransm)) # resample filter and stellar spectrum: nifilt, strfl, wnind = w.resample(specwn, filtwaven, filttransm, starwn, starfl) nifilter.append(nifilt) istarfl.append(strfl) wnindices.append(wnind) # convert mean wn to mean wl meanwl = 1e4 / np.asarray(meanwn) # band-integrate the flux-ratio or modulation: bandflux = np.zeros(nfilters, dtype='d') bandmod = np.zeros(nfilters, dtype='d') for i in np.arange(nfilters): fluxrat = (bestspectrum[wnindices[i]] / istarfl[i]) * rprs * rprs bandflux[i] = w.bandintegrate(fluxrat, specwn, nifilter[i], wnindices[i]) bandmod[i] = w.bandintegrate(bestspectrum[wnindices[i]], specwn, nifilter[i], wnindices[i]) # stellar spectrum on specwn: sinterp = si.interp1d(starwn, starfl) sflux = sinterp(specwn) frat = bestspectrum / sflux * rprs * rprs # plot figure plt.rcParams["mathtext.default"] = 'rm' matplotlib.rcParams.update({'mathtext.default': 'rm'}) matplotlib.rcParams.update({'font.size': fs - 2}) plt.figure(3, (8.5, 6)) plt.clf() # depending on solution plot eclipse or modulation spectrum if solution == 'eclipse': gfrat = gaussf(frat, 2) plt.semilogx(specwl, gfrat * 1e3, "b", lw=1.5, label="Best-fit") plt.errorbar(meanwl, data * 1e3, uncert * 1e3, fmt="or", label="data") plt.plot(meanwl, bandflux * 1e3, "ok", label="model", alpha=1.0) plt.ylabel(r"$F_p/F_s$ (10$^{-3}$)", fontsize=fs) elif solution == 'transit': gmodel = gaussf(bestspectrum, 2) plt.semilogx(specwl, gmodel, "b", lw=1.5, label="Best-fit") plt.errorbar(meanwl, data, uncert, fmt="or", label="data") plt.plot(meanwl, bandmod, "ok", label="model", alpha=0.5) plt.ylabel(r"$(R_p/R_s)^2$", fontsize=fs) leg = plt.legend(loc="best") leg.get_frame().set_alpha(0.5) ax = plt.subplot(111) ax.set_xscale('log') plt.xlabel("${\\rm Wavelength\ \ (\u03bcm)}$", fontsize=fs) #plt.xticks(size=fs) #plt.yticks(size=fs) formatter = matplotlib.ticker.FuncFormatter( lambda y, _: '{:.8g}'.format(y)) ax.get_xaxis().set_major_formatter(formatter) ax.get_xaxis().set_minor_formatter(formatter) plt.xlim(min(specwl), max(specwl)) plt.savefig(date_dir + "BART-bestFit-Spectrum.png") plt.close()
def comparison(transit, rhd, geo, atm, outdir=None, titles=False): """ This function produces a plot of Transit and RHD (or other RT code) spectra for the comparison tests (c##). Inputs ------ transit: string. path/to/file for transit spectrum data. rhd : string. path/to/file for RHD (or other RT code) spectrum data. geo : string. Viewing geometry. 'eclipse' or 'transit' atm : string. Atmosphere's PT profile. 'iso', 'inv', or 'noi' outdir : string. path/to/output. Default is execution directory titles : bool. Determines whether to include titles on plots or not. Revisions --------- 2017-10-16 mhimes Initial implementation. 2018-02-03 raechel Improved plotting, added residuals subplot. 2019-04-01 mhimes Merged into BARTTest. """ # Load transit data wlength, flux = rt.readspectrum(transit, 0) # Load RHD data data = open(rhd, "r") lines = data.readlines() if geo == 'eclipse': lines = lines[3:] wlengthb = np.zeros(len(lines), dtype=float) fluxb = np.zeros(len(lines), dtype=float) for i in range(len(lines)): line = lines[i].split() wlengthb[i] = float(line[1]) fluxb[i] = float(line[2]) * 2.998e10 elif geo == 'transit': lines = lines[3:] wlengthb = np.zeros(len(lines), dtype=float) fluxb = np.zeros(len(lines), dtype=float) for i in range(len(lines)): line = lines[i].split() wlengthb[i] = float(line[1]) fluxb[i] = float(line[2]) / 100 # Resample code1 to code2's sampling # Use a linear interpolation! Splines can cause values smaller/bigger than # the known mix and max values rep = si.interp1d(wlength, flux) resamp = rep(wlengthb) # Set plot title and file output name if geo == 'transit': if atm == 'inv': titlenm = "inverted Transmission" filenm = "inverted_transmission_comp.png" elif atm == 'iso': titlenm = "Isothermal Transmission" filenm = "Isothermal_transmission_comp.png" elif atm == 'noi': titlenm = "Non-inverted Transmission" filenm = "noninverted_transmission_comp.png" else: print("Wrong `atm` specification. Use 'inv', 'iso', or 'noi'.\n") elif geo == 'eclipse': if atm == 'inv': titlenm = "inverted Emission" filenm = "inverted_emission_comp.png" elif atm == 'iso': titlenm = "Isothermal Emission" filenm = "Isothermal_emission_comp.png" elif atm == 'noi': titlenm = "Non-inverted Emission" filenm = "noninverted_emission_comp.png" else: print("Wrong `atm` specification. Use 'inv', 'iso', or 'noi'.\n") else: print("Wrong `geo` specification. Use 'transit' or 'eclipse'.\n") sys.exit() # Add code names to `filenm` cname1 = transit.split('/') cname1 = cname1[cname1.index('code-output') + 1][2:] cname2 = rhd.split('/') cname2 = cname2[cname2.index('code-output') + 1][2:] filenm = cname1 + '_' + cname2 + '_' + filenm # Plot it fig0 = plt.figure(0, (8, 5)) plt.clf() frame1 = fig0.add_axes((.14, .3, .8, .65)) if titles == True: plt.title(titlenm) if geo == 'transit': flux = 100 * flux # convert to % fluxb = 100 * fluxb resamp = 100 * resamp if geo == 'eclipse': if atm == 'iso': plt.plot(wlength, flux, "lightblue", label=cname1 + ' - high resolution', linewidth=10.0) plt.plot(wlengthb, resamp, "royalblue", label=cname1 + ' - resampled to ' + cname2) plt.plot(wlengthb, fluxb, "firebrick", label=cname2) else: plt.plot(wlength, flux, "lightblue", label=cname1 + ' - high resolution') plt.plot(wlengthb, resamp, "royalblue", label=cname1 + ' - resampled to ' + cname2) plt.plot(wlengthb, fluxb, "firebrick", label=cname2) else: plt.plot(wlength, flux, "lightblue", label=cname1 + ' - high resolution') plt.plot(wlengthb, resamp, "royalblue", label=cname1 + ' - resampled to ' + cname2) plt.plot(wlengthb, fluxb, "firebrick", label=cname2) plt.xlabel(u"Wavelength (\u03bcm)") if geo == 'transit': plt.ylabel("Modulation (%)") else: plt.ylabel("Flux (erg s$^{-1}$ cm$^{-1}$)") #plot black body curves h = 6.62607004e-27 # erg*s c = 2.9979245800e10 # cm/s k = 1.38064852e-16 # erg/K # Set min and max temperatures for plotting if atm == 'inv': T1 = 968.60 # K T2 = 1243.05 # K elif atm == 'iso': T1 = 1100.0 T2 = 1100.0 elif atm == 'noi': T1 = 882.93 T2 = 1408.70 w = np.linspace(10000. / 11.0, 10000. / 1.000, 10000) # cm^-1 # Blackbody associated with min/max temperature a = np.pi * 2 * h * (c**2) * (w**3) b1 = (h * c * w) / (k * T1) Bb1 = a / (np.exp(b1) - 1.0) b2 = (h * c * w) / (k * T2) Bb2 = a / (np.exp(b2) - 1.0) l = 10000. / w T1s = str(T1) + ' K' T2s = str(T2) + ' K' if geo == 'eclipse': frame1.set_ylim(0, max(Bb2) + 5000) if T1 != T2: plt.plot(l, Bb1, "r", linestyle=':', label="Blackbody at " + T1s, linewidth=2.0) plt.plot(l, Bb2, "b", linestyle=':', label="Blackbody at " + T2s, linewidth=2.0) else: plt.plot(l, Bb1, "k", linestyle=':', label="Blackbody at " + T2s, linewidth=2.0) plt.legend(loc='upper left', prop={'size': 8}) elif geo == 'transit': plt.legend(loc='lower right', prop={'size': 8}) frame1.set_xscale('log') frame1.set_xlim(1, 11.0) frame1.set_xticklabels([]) frame1.yaxis.set_label_coords(-0.1, 0.5) # Set y axis limits if geo == 'transit': frame1.set_ylim(np.amin(flux) * 0.99, np.amax(flux) * 1.01) frame1.yaxis.set_major_locator(MaxNLocator(nbins='6', prune='lower')) else: frame1.set_ylim(np.amin(Bb1) * 0.9, np.amax(Bb2) * 1.1) frame2 = fig0.add_axes((.14, .1, .8, .2)) #residual plots resid = np.zeros(len(lines), dtype=float) # Residuals, in units of % resid = (resamp - fluxb) / np.max(np.concatenate((resamp, fluxb))) * 100 plt.plot(wlengthb, resid, "k", linestyle=":") plt.ylabel('Residuals (%)') plt.xlabel(u"Wavelength (\u00b5m)") frame2.set_xscale('log') frame2.set_xlim(0, 11.0) frame2.set_ylim( np.amin(resid) - 0.2 * np.abs(np.amin(resid)), np.amax(resid) + 0.2 * np.abs(np.amax(resid))) frame2.set_xticklabels([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) frame2.yaxis.set_major_locator(MaxNLocator(nbins='5', prune='upper')) if (geo == 'eclipse') and (atm == 'iso'): frame2.yaxis.get_major_ticks()[-1].label1.set_visible(False) frame2.xaxis.set_major_locator(plt.MultipleLocator(1)) frame2.yaxis.set_label_coords(-0.1, 0.5) if (geo == 'eclipse') and (atm == 'iso'): frame3 = fig0.add_axes((.57, .38, .25, .25)) frame3.set_ylim(23000, 24000) frame3.set_xscale('log') frame3.set_xlim(3.5, 6.5) frame3.set_xticklabels([4, 4.5, 5, 5.5, 6]) frame3.xaxis.set_major_locator(plt.MultipleLocator(1)) plt.plot(wlength, flux, "lightblue", linewidth=10.0) plt.plot(wlengthb, resamp, "royalblue", linewidth=2.0) plt.plot(wlengthb, fluxb, "firebrick", linewidth=2.0) plt.plot(l, Bb1, "k", linestyle=':', linewidth=4.0) if outdir != None: plt.savefig(outdir + filenm) else: plt.savefig(filenm) plt.close()
def compspec(fspec1, fspec2, outname, outdir='../results/plots/', geo='eclipse'): """ This function produces a plot comparing two spectra for the forward tests (f##). Inputs ------ fspec1 : string. Path to specturm text file. fspec2 : string. Path to spectrum text file. outname: string. Savename of plot. The code names for `fspec1` and `fspec2` will be added to the beginning of this. """ # Load spectra wlength1, flux1 = rt.readspectrum(fspec1, 0) wlength2, flux2 = rt.readspectrum(fspec2, 0) # Add code names to `outname` cname1 = fspec1.split('/') cname1 = cname1[cname1.index('code-output') + 1][2:] cname2 = fspec2.split('/') cname2 = cname2[cname2.index('code-output') + 1][2:] outname = cname1 + '_' + cname2 + '_' + outname # Make plot fig0 = plt.figure(0, (8, 5)) plt.clf() frame1 = fig0.add_axes((.14, .3, .8, .65)) plt.xlabel(u"Wavelength (\u03bcm)") if geo == 'transit': plt.ylabel("Modulation (%)") else: plt.ylabel("Flux (erg s$^{-1}$ cm$^{-1}$)") frame1.set_xticklabels([]) frame1.yaxis.set_label_coords(-0.1, 0.5) plt.plot(wlength1, flux1, label=cname1) plt.plot(wlength2, flux2, label=cname2) yticks = frame1.yaxis.get_major_ticks() yticks[0].label1.set_visible(False) plt.legend(loc='best') frame2 = fig0.add_axes((.14, .1, .8, .2)) # Residuals, in units of % if np.all(wlength1 != wlength2): print("Wavelength arrays do not match.") print("Interpolating " + fspec2 + " to the grid of " + fspec1) fint = si.interp1d(wlength2, flux2, bounds_error=False, fill_value=0) flux2 = fint(wlength1) resid = (flux1 - flux2) / np.max(np.concatenate((flux1, flux2))) * 100 plt.plot(wlength1, resid, "k", linestyle=":") plt.ylabel('Residuals (%)') plt.xlabel(u"Wavelength (\u00b5m)") frame2.yaxis.set_major_locator(MaxNLocator(nbins='5', prune='upper')) plt.savefig(outdir + outname) plt.close()
def plot_bestFit_Spectrum(filters, kurucz, tepfile, solution, output, data, uncert, date_dir): ''' Plot BART best-model spectrum ''' # get star data R_star, T_star, sma, gstar = get_starData(tepfile) # get surface gravity grav, Rp = mat.get_g(tepfile) # convert Rp to m Rp = Rp * 1000 # ratio planet to star rprs = Rp/R_star # read kurucz file starfl, starwn, tmodel, gmodel = w.readkurucz(kurucz, T_star, gstar) # read best-fit spectrum output file, take wn and spectra values if solution == 'eclipse': specwn, bestspectrum = rt.readspectrum(date_dir + output, wn=True) # print on screen print(" Plotting BART best-fit eclipse spectrum figure.") elif solution == 'transit': specwn, bestspectrum = rt.readspectrum(date_dir + output, wn=True) # print on screen print(" Plotting BART best-fit modulation spectrum figure.") # convert wn to wl specwl = 1e4/specwn # number of filters nfilters = len(filters) # read and resample the filters: nifilter = [] # Normalized interpolated filter istarfl = [] # interpolated stellar flux wnindices = [] # wavenumber indices used in interpolation meanwn = [] # Filter mean wavenumber for i in np.arange(nfilters): # read filter: filtwaven, filttransm = w.readfilter(filters[i]) meanwn.append(np.sum(filtwaven*filttransm)/sum(filttransm)) # resample filter and stellar spectrum: nifilt, strfl, wnind = w.resample(specwn, filtwaven, filttransm, starwn, starfl) nifilter.append(nifilt) istarfl.append(strfl) wnindices.append(wnind) # convert mean wn to mean wl meanwl = 1e4/np.asarray(meanwn) # band-integrate the flux-ratio or modulation: bandflux = np.zeros(nfilters, dtype='d') bandmod = np.zeros(nfilters, dtype='d') for i in np.arange(nfilters): fluxrat = (bestspectrum[wnindices[i]]/istarfl[i]) * rprs*rprs bandflux[i] = w.bandintegrate(fluxrat, specwn, nifilter[i], wnindices[i]) bandmod[i] = w.bandintegrate(bestspectrum[wnindices[i]], specwn, nifilter[i], wnindices[i]) # stellar spectrum on specwn: sinterp = si.interp1d(starwn, starfl) sflux = sinterp(specwn) frat = bestspectrum/sflux * rprs * rprs # plot figure plt.rcParams["mathtext.default"] = 'rm' matplotlib.rcParams.update({'mathtext.default':'rm'}) matplotlib.rcParams.update({'font.size':10}) plt.figure(3, (8.5, 5)) plt.clf() # depending on solution plot eclipse or modulation spectrum if solution == 'eclipse': gfrat = gaussf(frat, 2) plt.semilogx(specwl, gfrat*1e3, "b", lw=1.5, label="Best-fit") plt.errorbar(meanwl, data*1e3, uncert*1e3, fmt="or", label="data") plt.plot(meanwl, bandflux*1e3, "ok", label="model", alpha=1.0) plt.ylabel(r"$F_p/F_s$ (10$^{3}$)", fontsize=12) elif solution == 'transit': gmodel = gaussf(bestspectrum, 2) plt.semilogx(specwl, gmodel, "b", lw=1.5, label="Best-fit") # Check units! plt.errorbar(meanwl, data, uncert, fmt="or", label="data") plt.plot(meanwl, bandmod, "ok", label="model", alpha=0.5) plt.ylabel(r"$(R_p/R_s)^2$", fontsize=12) leg = plt.legend(loc="lower right") leg.get_frame().set_alpha(0.5) ax = plt.subplot(111) ax.set_xscale('log') plt.xlabel(r"${\rm Wavelength\ \ (um)}$", fontsize=12) ax.get_xaxis().set_major_formatter(matplotlib.ticker.ScalarFormatter()) ax.set_xticks(np.arange(min(specwl),max(specwl),1)) plt.xlim(min(specwl),max(specwl)) plt.savefig(date_dir + "BART-bestFit-Spectrum.png")
# Atmospheric species: species = "He H2 CO CO2 CH4 H2O NH3 C2H2 C2H4" # Abundances (mole mixing ratio): abundances = "0.15 0.85 1e-4 1e-4 1e-4 1e-4 1e-10 1e-10 1e-10" # [Run script ( 2) to make a temperature profile] # Make the atmospheric file: ma.uniform(atmfile, pfile, elemabun, tep, species, abundances, temp) # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: # ( 3) Read and plot a transit spectrum: import readtransit as rt wl, spectrum = rt.readspectrum("eclipse_out.dat.-Flux", 0) plt.figure(1) plt.clf() plt.semilogx(wl, spectrum, "b", label="Planet spectrum") plt.xlim(wl[-1], wl[0]) plt.legend(loc="best") # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: # ( 4) Calculate the minimum and maximum line-profile widths: # TBD # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: # ( 5) Plot the posterior TP profiles:
def noiseup(kuruczfile, planetfile, filters, geometry, outdir, outpre, mass=0.806, radius=0.756, temp=5000., planetrad=1.138, distance=19.3, diameter=650., ecltime=1.827, seed=0): """ This function takes an input star and planet spectrum and computes transit/eclipse depths with photon noise, as observed by a JWST-like telescope. Inputs ------ kuruczfile: string. Path/to/file of the Kurucz stellar model. planetfile: string. Path/to/file of the planet's spectrum. filters : list, strings. Paths/to/filter files. geometry : string. Viewing geometry. 'eclipse' or 'transit' outdir : string. Path/to/directory/ where the outputs will be saved. outpre : string. Prefix to use for all saved out files. mass : float. Mass of the host star [M_sun] radius : float. Radius of the host star [R_sun] temp : float. Temperature of the host star [K] planetrad : float. Radius of the planet [R_jup] distance : float. Distance to planetary system [pc] diameter : float. Telescope diameter [cm] ecltime : float. Duration of the secondary eclipse [hours] seed : int. Seed for random number generation. Outputs ------- Four files are produced: - - - - Notes ----- The default values are based on the HD 189733 system. The default `ecltime` value comes from https://academic.oup.com/mnras/article/395/1/335/1746726/Secondary-radio-eclipse-of-the-transiting-planet Revisions --------- 2017-11-15 Ryan Original implementation 2018-04-18 Michael Added transit calculations 2019-06-06 Michael Reworked into function, incorporated into BARTTest """ # Set the random seed for reproducibility np.random.seed(seed=seed) # Make sure `outdir` has trailing slash: if outdir[-1] != '/': outdir = outdir + '/' # Constants in CGS units G = const.G.cgs.value h = const.h.cgs.value c = const.c.cgs.value Msun = const.M_sun.cgs.value Rsun = const.R_sun.cgs.value pc2cm = const.pc.cgs.value Rjup = const.R_jup.cgs.value # Convert system parameters mass *= Msun radius *= Rsun planetrad *= Rjup distance *= pc2cm ecltime *= 3600 #hours --> seconds # Temperature and log(g) of star logg = np.log10(G * mass / radius**2) #log g (cm/s2) # Read and interpolate Kurucz grid, planet spectrum starfl, starwn, tmodel, gmodel = wine.readkurucz(kuruczfile, temp, logg) planetwn, planetfl = rt.readspectrum(planetfile) nifilter = [] wnindices = [] # Multiply by 4pi steradians, surface area # Planetary spectrum is already integrated over steradians sPower = starfl * (4 * np.pi * radius**2) pPower = planetfl * (4 * np.pi * planetrad**2) # Multiply by eclipse duration sEnergy = sPower * ecltime pEnergy = pPower * ecltime # Spread out over distance sEdensity = sEnergy / (4 * np.pi * distance**2) pEdensity = pEnergy / (4 * np.pi * distance**2) # Multiply by telescope area (total energy received) sSED = sEdensity * np.pi * (diameter/2.)**2. pSED = pEdensity * np.pi * (diameter/2.)**2. # Interpolate stellar SED sSEDinterp = si.interp1d(starwn, sSED) isSED = sSEDinterp(planetwn) if geometry == 'transit': # Interpolate stellar flux starflinterp = si.interp1d(starwn, starfl) istarfl = starflinterp(planetwn) # Initialize arrays for band-integrated energy and mean wavelength bandintegratedstar = np.zeros(len(filters)) bandintegratedplanet = np.zeros(len(filters)) meanwn = np.zeros(len(filters)) # Read filters. Resample to planetwn for i in np.arange(len(filters)): filtwn, filttransm = wine.readfilter(filters[i]) meanwn[i] = np.mean(filtwn) nifilt, rsSED, wnind = wine.resample(planetwn, filtwn, filttransm, starwn, sSED) nifilter.append(nifilt) wnindices.append(wnind) # Loop over each filter file, read the file, interpolate to the # wavelength array, and integrate over the bandpass, weighted by the # filter. for i in np.arange(len(filters)): # integrate over the bandpass bandintegratedstar[i] = wine.bandintegrate( isSED[wnindices[i][0]], planetwn, nifilter[i], wnindices[i]) if geometry == 'eclipse': bandintegratedplanet[i] = wine.bandintegrate( pSED[wnindices[i][0]], planetwn, nifilter[i], wnindices[i]) elif geometry == 'transit': bandintegratedplanet[i] = wine.bandintegrate( planetfl[wnindices[i][0]], planetwn, nifilter[i], wnindices[i]) else: print("Invalid `geometry` specification.\n") sys.exit() # Divide by photon energy to get number of photons (counts) # Find total photon signal sphotons = bandintegratedstar / (h * meanwn * c) if geometry == 'eclipse': pphotons = bandintegratedplanet / (h * meanwn * c) phot_tot = sphotons + pphotons else: phot_tot = sphotons #planet flux is negligible in transit geometry # Multiply by 1 minus the transit depth = signal during transit phot_tot_rat = phot_tot * (1. - bandintegratedplanet) # Noise it up poisson_tot = phot_tot**(.5) poisson_s = sphotons**(.5) if geometry == 'eclipse': poisson_p = pphotons**(.5) noise = np.random.normal(0, poisson_tot) # Add noise to the band-integrated photon counts noisedpts = phot_tot + noise noisedplanet = pphotons + noise noisedstar = sphotons + noise # Calculate eclipse depths depths = noisedplanet / phot_tot # Calculate eclipse depth uncertainty unc = phot_tot/sphotons * ((poisson_tot/phot_tot)**2 + \ (poisson_s /sphotons)**2)**(.5) else: poisson_tot_rat = (phot_tot_rat)**(.5) # Propagate error unc = phot_tot_rat / phot_tot * \ ((poisson_tot_rat / phot_tot_rat)**2 + \ (poisson_tot / phot_tot )**2)**(.5) noise = np.random.normal(0, poisson_tot_rat) / phot_tot_rat depths = bandintegratedplanet + noise # Save out files # Save filter file paths for BART with open(outdir+outpre+'filters.txt', 'w') as f: for i in range(len(filters)): f.write('../00inputs/filters/' + \ os.path.basename(filters[i]) + '\n') if geometry == 'eclipse': # Save depths to a file with open(outdir+outpre+'ecldepths.txt', 'w') as f: for i in range(len(depths)): f.write(str(depths[i]) + '\n') # Save uncertainties to a file with open(outdir+outpre+'ecluncs.txt', 'w') as f: for i in range(len(unc)): f.write(str(unc[i]) + '\n') # Save noiseless depths with open(outdir+outpre+'ecl_noiseless.txt', 'w') as f: for i in range(len(filters)): f.write(str(pphotons[i]/phot_tot[i]) + '\n') else: # Save depths to a file with open(outdir+outpre+'tradepths.txt', 'w') as f: for i in range(len(depths)): f.write(str(depths[i]) + '\n') # Save uncertainties to a file with open(outdir+outpre+'trauncs.txt', 'w') as f: for i in range(len(unc)): f.write(str(unc[i]) + '\n') # Save noiseless depths with open(outdir+outpre+'tra_noiseless.txt', 'w') as f: for i in range(len(filters)): f.write(str(bandintegratedplanet[i]) + '\n')
plt.rcParams["mathtext.default"] = 'rm' matplotlib.rcParams.update({'mathtext.default':'rm'}) matplotlib.rcParams.update({'axes.labelsize': 16, 'xtick.labelsize': 14, 'ytick.labelsize': 14,}) fig= plt.figure(figsize=(8.5, 5)) plt.ylabel(r"$F_p/F_s$ (10$^{-3}$)", fontsize=14) plt.xlabel(r"${\rm Wavelength\ \ (um)}$", fontsize=14) ######################################### for each spectrum separately outflux = './4species_4opac_uniform/4species_4opac_uniform-flux.dat' # read best-fit spectrum output file, take wn and spectra values specwn, bestspectrum = rt.readspectrum(outflux, wn=True) # convert wn to wl specwl = 1e4/specwn # number of filters nfilters = len(filters) # read and resample the filters: nifilter = [] # Normalized interpolated filter istarfl = [] # interpolated stellar flux wnindices = [] # wavenumber indices used in interpolation meanwn = [] # Filter mean wavenumber for i in np.arange(nfilters): # read filter: filtwaven, filttransm = w.readfilter(filters[i]) meanwn.append(np.sum(filtwaven*filttransm)/sum(filttransm)) # resample filter and stellar spectrum: nifilt, strfl, wnind = w.resample(specwn, filtwaven, filttransm,
def plot_bestFit_Spectrum(low, high, xtic, xlab, spec, clr, specs, atmfile, filters, kurucz, tepfile, outflux, data, uncert, direct): ''' Plot Transit spectrum ''' # get star data R_star, T_star, sma, gstar = bf.get_starData(tepfile) # get surface gravity grav, Rp = mat.get_g(tepfile) # convert Rp to m Rp = Rp * 1000 # ratio planet to star rprs = Rp / R_star # read kurucz file starfl, starwn, tmodel, gmodel = w.readkurucz(kurucz, T_star, gstar) # read best-fit spectrum output file, take wn and spectra values (head, tail) = os.path.split(outflux) specwn, bestspectrum = rt.readspectrum(direct + '/' + tail, wn=True) # convert wn to wl specwl = 1e4 / specwn # number of filters nfilters = len(filters) # read and resample the filters: nifilter = [] # Normalized interpolated filter istarfl = [] # interpolated stellar flux wnindices = [] # wavenumber indices used in interpolation meanwn = [] # Filter mean wavenumber for i in np.arange(nfilters): # read filter: filtwaven, filttransm = w.readfilter(filters[i]) meanwn.append(np.sum(filtwaven * filttransm) / sum(filttransm)) # resample filter and stellar spectrum: nifilt, strfl, wnind = w.resample(specwn, filtwaven, filttransm, starwn, starfl) nifilter.append(nifilt) istarfl.append(strfl) wnindices.append(wnind) # convert mean wn to mean wl meanwl = 1e4 / np.asarray(meanwn) # band-integrate the flux-ratio or modulation: bandflux = np.zeros(nfilters, dtype='d') bandmod = np.zeros(nfilters, dtype='d') for i in np.arange(nfilters): fluxrat = (bestspectrum[wnindices[i]] / istarfl[i]) * rprs * rprs bandflux[i] = w.bandintegrate(fluxrat, specwn, nifilter[i], wnindices[i]) bandmod[i] = w.bandintegrate(bestspectrum[wnindices[i]], specwn, nifilter[i], wnindices[i]) # stellar spectrum on specwn: sinterp = si.interp1d(starwn, starfl) sflux = sinterp(specwn) frat = bestspectrum / sflux * rprs * rprs ###################### plot figure ############################# plt.rcParams["mathtext.default"] = 'rm' matplotlib.rcParams.update({'mathtext.default': 'rm'}) matplotlib.rcParams.update({ 'axes.labelsize': 16, 'xtick.labelsize': 20, 'ytick.labelsize': 20, }) plt.figure(2, (8.5, 5)) plt.clf() # smooth the spectrum a fit gfrat = gaussf(frat, 2) # plot eclipse spectrum plt.semilogx(specwl, gfrat * 1e3, clr, lw=1.5, label="Spectrum", linewidth=4) plt.errorbar(meanwl, data * 1e3, uncert * 1e3, fmt="ko", label="Data", alpha=0.7) plt.ylabel(r"$F_p/F_s$ (10$^{-3}$)", fontsize=24) leg = plt.legend(loc="upper left") leg.get_frame().set_alpha(0.5) ax = plt.subplot(111) ax.set_xscale('log') plt.xlabel(r"${\rm Wavelength\ \ (um)}$", fontsize=24) ax.get_xaxis().set_major_formatter(matplotlib.ticker.ScalarFormatter()) plt.gca().xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter()) ax.set_xticks([0.7, 0.8, 0.9, 1.0, 2.0, 3.0, 4.0, 5.0]) ax.set_xticklabels(["0.7", "", "", "1.0", "2.0", "3.0", "4.0", "5.0"]) plt.xlim(min(specwl), max(specwl)) nfilters = len(filters) # plot filter bandpasses for i in np.arange(nfilters - 15): (head, tail) = os.path.split(filters[i]) lbl = tail[:-4] # read filter: wn, respons = w.readfilter(filters[i]) respons = respons / 3 - 0.4 wl = 10000.0 / wn #plt.plot(wl, respons, color='crimson', linewidth =1) if lbl == 'spitzer_irac1_sa' or lbl == 'spitzer_irac2_sa': respons = respons * 2 + 0.4 plt.plot(wl, respons, color='grey', linewidth=1, alpha=0.5) #plt.plot(wl, respons*2, color='orangered', linewidth =1) elif lbl == 'Wang-Hband' or lbl == 'Wang-Kband': plt.plot(wl, respons, 'grey', linewidth=1, alpha=0.5) elif lbl == 'VLT_1190' or lbl == 'VLT_2090': plt.plot(wl, respons, color='grey', linewidth=2, alpha=0.5) #plt.plot(wl, respons, color='firebrick', linewidth =2) elif lbl == 'GROND_K_JB' or lbl == 'GROND_i_JB': plt.plot(wl, respons, 'grey', linewidth=1, alpha=0.5) elif lbl == 'Zhou_Ks': plt.plot(wl, respons, 'grey', linewidth=1, alpha=0.5) plt.ylim(-0.4, 7) plt.text(1.9, 3, specs, color=clr, fontsize=26) plt.subplots_adjust(bottom=0.20) plt.savefig(spec + "_BestFit-transSpec.png") plt.savefig(spec + "_BestFit-transSpec.ps")
def noiseup(snr, planetfile, filters, geometry, outdir, outpre, kuruczfile, mass=0.806, radius=0.756, temp=5000., planetrad=1.138, seed=0): """ This function takes an input star and planet spectrum and computes transit/eclipse depths with photon noise, as observed by a JWST-like telescope. Inputs ------ snr : float. Desired SNR value. planetfile: string. Path/to/file of the planet's spectrum. filters : list, strings. Paths/to/filter files. geometry : string. Viewing geometry. 'eclipse' or 'transit' outdir : string. Path/to/directory/ where the outputs will be saved. outpre : string. Prefix to use for all saved out files. kuruczfile: string. Path/to/file of the Kurucz stellar model. mass : float. Mass of the host star [M_sun] radius : float. Radius of the host star [R_sun] temp : float. Temperature of the host star [K] planetrad : float. Radius of the planet [R_jup] seed : int. Seed for random number generation. Outputs ------- Four text files are produced: - list of filers - noiseless spectrum - noised spectrum - uncertainties on noised spectrum Notes ----- The default values are based on the HD 189733 system. The default `ecltime` value comes from https://academic.oup.com/mnras/article/395/1/335/1746726/Secondary-radio-eclipse-of-the-transiting-planet Revisions --------- 2017-11-15 Ryan Original implementation 2018-04-18 Michael Added transit calculations 2019-06-06 Michael Reworked into function, incorporated into BARTTest 2020-08-08 Michael Simplified computation """ # Set the random seed for reproducibility np.random.seed(seed=seed) # Make sure `outdir` has trailing slash: if outdir[-1] != '/': outdir = outdir + '/' # Constants in CGS units G = const.G.cgs.value h = const.h.cgs.value c = const.c.cgs.value Msun = const.M_sun.cgs.value Rsun = const.R_sun.cgs.value pc2cm = const.pc.cgs.value Rjup = const.R_jup.cgs.value # Convert system parameters mass *= Msun radius *= Rsun planetrad *= Rjup rprs = planetrad / radius #ratio of planet/star radii # Temperature and log(g) of star logg = np.log10(G * mass / radius**2) #log g (cm/s2) # Read and interpolate Kurucz grid, planet spectrum starfl, starwn, tmodel, gmodel = wine.readkurucz(kuruczfile, temp, logg) planetwn, planetfl = rt.readspectrum(planetfile) # Initialize arrays for band-integrated spectrum and mean wavelength bandflux = np.zeros(len(filters)) meanwn = np.zeros(len(filters)) nifilter = [] wnindices = [] istarfl = [] # interpolated stellar flux # Read filters. Resample to planetwn for i in np.arange(len(filters)): filtwn, filttransm = wine.readfilter(filters[i]) meanwn[i] = np.mean(filtwn) nifilt, strfl, wnind = wine.resample(planetwn, filtwn, filttransm, starwn, starfl) nifilter.append(nifilt) wnindices.append(wnind) istarfl.append(strfl) # Loop over each filter file, read the file, interpolate to the # wavelength array, and integrate over the bandpass, weighted by the # filter. for i in np.arange(len(filters)): # integrate over the bandpass if geometry == "eclipse": fluxrat = (planetfl[wnindices[i]] / istarfl[i]) * rprs * rprs bandflux[i] = wine.bandintegrate(fluxrat, planetwn, nifilter[i], wnindices[i]) elif geometry == "transit": bandflux[i] = wine.bandintegrate(planetfl[wnindices[i]], planetwn, nifilter[i], wnindices[i]) # Uncertainty unc = bandflux / snr # Save out files # Save filter file paths for BART with open(outdir + outpre + 'filters.txt', 'w') as f: for i in range(len(filters)): f.write('../00inputs/filters/' + \ os.path.basename(filters[i]) + '\n') if geometry == 'eclipse': strname = 'ecl' elif geometry == 'transit': strname = 'tra' # Save noised depths to a file with open(outdir + outpre + strname + 'depths.txt', 'w') as f: for i in range(len(depths)): f.write(str(bandflux[i]) + '\n') # Save uncertainties to a file with open(outdir + outpre + strname + 'uncs.txt', 'w') as f: for i in range(len(unc)): f.write(str(unc[i]) + '\n')
def plotspectrum(fname, geo, wl=True, oname=False, title=False): """ This function produces a plot of the spectrum produced by Transit. Inputs ------ fname: string. File name of the spectrum file, with directory relative to /BARTTest/. geo : string. Geometry of the produced spectrum. 'transit' or 'eclipse' wl : bool. True plots over wavelength, False plots over wavenumber. oname: string. If oname is False, the default plotting name is used when saving the file. If it is a string, then that path/to/plotname is used. title: bool. True produces a plot with a title. False does not. Outputs ------- PNG file of the spectrum. Default naming convention is i.e. oneline_emission_spectrum.png fewline_transmission_spectrum.png Example ------- >>> plotspectrum('fewline/fewline_emission_spectrum.dat', 'eclipse') """ # Load the data try: if wl==True: wlength, flux = rt.readspectrum(fname, 0) else: wlength, flux = rt.readspectrum(fname) except: return # Get test name testname = fname.split('/')[-1].split('_')[0] # Plot the data plt.figure(0, (8,5)) plt.clf() plt.plot(wlength, flux, "b") # Set title and plot filename based on the geometry if geo=='eclipse': if title==True: plt.title(testname + " Emission Spectrum") plt.ylabel("Flux (erg s$^{-1}$ cm$^{-1}$)") plotname = testname + '_emission_spectrum.png' else: if title==True: plt.title(testname + " Transmission Spectrum") plt.ylabel("Modulation ($(R_p/R_s)^2$)") plotname = testname + '_transmission_spectrum.png' # Set X axis label to that specified if wl==True: plt.xlabel(u"Wavelength (\u00b5m)") else: plt.xlabel("Wavenumber (cm$^{-1}$)") # Set x limits plt.xlim(np.amin(wlength), np.amax(wlength)) # Save the plot if oname==False: plt.savefig(fname.split('/')[0] + '/' + plotname) else: plt.savefig(oname) plt.clf()
def plotspeciso(fname, atm, geo, wl=True, oname=False, title=False): """ This function produces a plot of the isothermal spectrum produced by the isothermal test with the Planck function overplotted. Inputs ------ fname: string. File name of the spectrum file, with directory relative to /BARTTest/. atm : string. File name of the atmospheric file used, with directory. geo : string. Geometry of the produced spectrum. 'transit' or 'eclipse' wl : bool. True plots over wavelength, False plots over wavenumber. oname: string. If oname is False, the default plotting name is used when saving the file. If it is a string, then that path/to/plotname is used. title: bool. If True, plots w/ title. If False, it does not. Outputs ------- PNG file of the spectrum. Default naming convention is i.e. isothermal_emission_spectrum.png isothermal_transmission_spectrum.png Example ------- >>> plotspeciso('isothermal/isothermal_emission_spectrum.dat', \ 'isothermal/isothermal.atm', 'eclipse') Notes ----- For this plot to be produced correctly, an isothermal atmospheric file must be supplied. There is a check for this in the function. A message will be printed to the screen, and the code will continue to run. The resulting plot will however not contain the overplotted Planck function. """ # Get the temperatures from the atm file foo = open(atm, 'r') lines = foo.readlines() lines = lines[13:] # trim the first 12 lines as they are headers # Array to hold temps temparr = np.zeros(len(lines), dtype=float) # Read in all the temps for i in range(len(lines)): temparr[i] = lines[i].split()[2] # Check that they are all the same--if not, yell at the user if len(np.unique(temparr)) != 1: print("The atmospheric file supplied is non-isothermal! " + \ "Please supply an isothermal atmospheric file.\n") bgtemp = None else: bgtemp = np.unique(temparr) # Load the data try: if wl: wlength, flux = rt.readspectrum(fname, 0) else: wlength, flux = rt.readspectrum(fname) except: print("\nIsothermal plot: invalid specification for the data file name.") print("Problematic file:", fname, '\n') return # Get test name testname = fname.split('/')[-1].split('_')[0] # Plot the data plt.clf() fig1 = plt.figure(1) frame1 = fig1.add_axes((.1, .3, .8, .6)) plt.plot(wlength, flux, color="k", lw=2, label='Transit') # Set title and plot filename based on the geometry if geo=='eclipse': if title: plt.title(testname + " Emission Spectrum") plt.ylabel("Flux (erg s$^{-1}$ cm$^{-1}$)", fontsize=14) plotname = testname + '_emission_spectrum.png' else: if title: plt.title(testname + " Transmission Spectrum") plt.ylabel("Modulation ($(R_p/R_s)^2$)", fontsize=14) plotname = testname + '_transmission_spectrum.png' # Plot the Planck function if wl: # Convert to wavenumber wavenum = 10000./wlength # Calculate the Planck function planck = 2. * const.h*1e7 * (wavenum**3) * (const.c*100)**2 / \ (np.exp(const.h*1e7 * wavenum * const.c*100 / \ (const.k*1e7) / bgtemp) - 1) # Plot it. Multiply Planck by pi because of how Transit calcs flux # Reverse Planck to match wlength plt.plot(wlength, np.pi*planck, color='goldenrod', lw=2, ls="--", label='Planck') else: planck = 2. * const.h*1e7 * (wlength**3) * (const.c*100)**2 / \ (np.exp(const.h*1e7 * wlength * const.c*100 / \ (const.k*1e7) / bgtemp) - 1) plt.plot(wlength, np.pi*planck, color='#ffff00', lw=2, ls="--", label='Planck') plt.legend(loc='upper right') frame1.set_xticklabels([]) # Residuals frame2 = fig1.add_axes((.1, .1, .8, .2)) plt.plot(wlength, 100*(flux - (np.pi*planck)) / (np.pi*planck)) plt.ylabel('Residuals (%)', fontsize=14) yticks = frame2.yaxis.get_major_ticks() yticks[-1].label1.set_visible(False) # Set X axis label to that specified if wl: plt.xlabel(u"Wavelength (\u00b5m)", fontsize=14) else: plt.xlabel("Wavenumber (cm$^{-1}$)", fontsize=14) # Save the plot if not oname: plt.savefig(fname.split('/')[0] + '/' + plotname, bbox_inches='tight') else: plt.savefig(oname, bbox_inches='tight') plt.clf()
def plotspeczoom(fname, geo, loc, wl=True, xlims=False, oname=False, title=True): """ This function produces a plot of a portion of the spectrum produced by Transit. Inputs ------ fname: string. File name of the spectrum file. geo : string. Geometry of the produced spectrum. 'transit' or 'eclipse' loc : float. Location in microns (if wl=True) or inverse centimeters (if wl=False) to zoom in on. wl : bool. True plots over wavelength, False plots over wavenumber. xlims: tuple. (xmin, xmax) Minimum and maximum X-axis values for the plot. Only use this if user desires a different range for the X axis than the default. oname: string. If oname is False, the default plotting name is used. If it is a string, then that path/to/plotname is used. title: bool. True plots w/ title. False does not. Outputs ------- PNG file of the portion of the spectrum. Naming convention is i.e. fewline_emission_spectrum_zoom2500nm.png Example ------- >>> plotspeczoom('fewline/fewline_emission_spectrum.dat', 'eclipse', 2.5) """ # Load the data try: if wl==True: wlength, flux = rt.readspectrum(fname, 0) # File is ordered by decreasing wavelength--reverse it wlength = wlength[::-1] flux = flux [::-1] else: wlength, flux = rt.readspectrum(fname) except: return # Get test name testname = fname.split('/')[-1].split('_')[0] # Find where to zoom in, trim data to this region hi = wlength[wlength < loc] wlentrim = wlength[len(hi) - 33 : len(hi) + 33] # This is why reverse wl fluxtrim = flux [len(hi) - 33 : len(hi) + 33] # above # Plot the data plt.figure(0, (8,5)) plt.clf() plt.plot(wlentrim, fluxtrim, "b") # Set title and plot filename based on the geometry if geo=='eclipse': if title==True: plt.title(testname + " Emission Spectrum") plt.ylabel("Flux (erg s$^{-1}$ cm$^{-1}$)") plotname = testname + '_emission_spectrum_zoom' else: if title==True: plt.title(testname + " Transmission Spectrum") plt.ylabel("Modulation ($(R_p/R_s)^2$)") plotname = testname + '_transmission_spectrum_zoom' # Set X axis label to that specified if wl==True: plt.xlabel(u"Wavelength (\u00b5m)") plotname += str(1000*loc)[:4] + 'nm' else: plt.xlabel("Wavenumber (cm$^{-1}$)") plotname += str(loc) + 'cm-1' # Set the X axis limits for the plot plt.xlim(wlentrim[0], wlentrim[-1]) # Save the plot if oname==False: plt.savefig(fname.split('/')[0] + '/' + plotname + '.png') else: plt.savefig(oname) plt.clf()
def compspec(specs, codes, geo, atm, outdir=None, titles=False, fext='.pdf'): """ This function produces a plot of supplied spectra. If multiple are given, it computes the residuals for each spectrum with respect to the average of all supplied spectra. Inputs ------ specs : list, string. path/to/file for spectrum data. codes : list, string. Code names corresponding to `specs`. geo : string. Viewing geometry. 'eclipse' or 'transit' atm : string. Atmosphere's PT profile. 'iso', 'inv', or 'noi' outdir : string. path/to/output. Default is execution directory titles : bool. Determines whether to include titles on plots or not. fext : str. Plot file extension. .pdf or .png Revisions --------- 2017-10-16 mhimes Initial implementation. 2018-02-03 raechel Improved plotting, added residuals subplot. 2019-04-01 mhimes Merged into BARTTest. 2021-01-08 mhimes Refactor to allow more codes to be easily added. """ # Set plot title and file output name if geo == 'transit': if atm == 'inv': titlenm = "Inverted Transmission" filenm = "Inverted_transmission_comp" elif atm == 'iso': titlenm = "Isothermal Transmission" filenm = "isothermal_transmission_comp" elif atm == 'noi': titlenm = "Non-inverted Transmission" filenm = "noninverted_transmission_comp" else: print("Wrong `atm` specification. Use 'inv', 'iso', or 'noi'.\n") elif geo == 'eclipse': if atm == 'inv': titlenm = "Inverted Emission" filenm = "Inverted_emission_comp" elif atm == 'iso': titlenm = "Isothermal Emission" filenm = "isothermal_emission_comp" elif atm == 'noi': titlenm = "Non-inverted Emission" filenm = "noninverted_emission_comp" else: print("Wrong `atm` specification. Use 'inv', 'iso', or 'noi'.\n") else: raise ValueError( "Wrong `geo` specification. Use 'transit' or 'eclipse'.\n") # Plot it fig0 = plt.figure(0, (8, 5)) plt.clf() frame1 = fig0.add_axes((.14, .3, .8, .65)) if titles: plt.title(titlenm) avgspec = 0 #average spectrum, for residuals for i in range(len(specs)): wlength, flux = rt.readspectrum(specs[i], 0) if geo == 'transit': flux *= 100 # convert to % avgspec += flux plt.plot(wlength, flux, label=codes[i]) avgspec /= len(specs) plt.xlabel(u"Wavelength (\u03bcm)") if geo == 'transit': plt.ylabel("Modulation (%)") else: plt.ylabel("Flux (erg s$^{-1}$ cm$^{-1}$)") #plot black body curves h = 6.62607004e-27 # erg*s c = 2.9979245800e10 # cm/s k = 1.38064852e-16 # erg/K # Set min and max temperatures for plotting if atm == 'inv': T1 = 911.14 # K T2 = 1012.68 # K elif atm == 'iso': T1 = 1100.0 T2 = 1100.0 elif atm == 'noi': T1 = 882.93 T2 = 1408.70 w = np.linspace(10000. / 11.0, 10000. / 1.000, 10000) # cm^-1 # Blackbody associated with min/max temperature a = np.pi * 2 * h * (c**2) * (w**3) b1 = (h * c * w) / (k * T1) Bb1 = a / (np.exp(b1) - 1.0) b2 = (h * c * w) / (k * T2) Bb2 = a / (np.exp(b2) - 1.0) l = 10000. / w T1s = str(T1) + ' K' T2s = str(T2) + ' K' if geo == 'eclipse': frame1.set_ylim(0, max(Bb2) + 5000) if T1 != T2: plt.plot(l, Bb1, "r", linestyle=':', label="Blackbody at " + T1s, linewidth=2.0) plt.plot(l, Bb2, "b", linestyle=':', label="Blackbody at " + T2s, linewidth=2.0) else: plt.plot(l, Bb1, "k", linestyle=':', label="Blackbody at " + T2s, linewidth=2.0) plt.legend(loc='center left', bbox_to_anchor=(1.0, 0.5)) frame1.set_xscale('log') frame1.set_xlim(1, 11.0) frame1.yaxis.set_label_coords(-0.1, 0.5) # For the x-axis to be integers, rather than C * 10^n formatter = mpl.ticker.FuncFormatter(lambda y, _: '{:.8g}'.format(y)) if len(specs) > 1: frame1.set_xticklabels([]) fig0.canvas.draw() if frame1.get_yticklabels()[0].get_position()[-1] == 0.0: plt.setp(frame1.get_yticklabels()[0], visible=False) #residual plots frame2 = fig0.add_axes((.14, .1, .8, .2)) for i in range(len(specs)): wlength, flux = rt.readspectrum(specs[i], 0) if geo == 'transit': flux *= 100 # convert to % plt.plot(wlength, flux - avgspec) plt.ylabel('Residuals\nw.r.t. average') plt.xlabel(u"Wavelength (\u00b5m)") frame2.set_xscale('log') frame2.set_xlim(1, 11.0) frame2.get_xaxis().set_major_formatter(formatter) frame2.get_xaxis().set_minor_formatter(formatter) frame2.yaxis.set_label_coords(-0.1, 0.5) else: frame1.get_xaxis().set_major_formatter(formatter) frame1.get_xaxis().set_minor_formatter(formatter) if (geo == 'eclipse') and (atm == 'iso'): frame3 = fig0.add_axes((.57, .38, .25, .25)) frame3.set_ylim(23000, 24000) frame3.set_xscale('log') frame3.set_xlim(3.5, 6.5) frame3.get_xaxis().set_major_formatter(formatter) frame3.get_xaxis().set_minor_formatter(formatter) for i in range(len(specs)): wlength, flux = rt.readspectrum(specs[i], 0) plt.plot(wlength, flux) plt.plot(l, Bb1, "k", linestyle=':', linewidth=4.0) if outdir is not None: plt.savefig(outdir + filenm + "_" + "_".join(codes) + fext, bbox_inches='tight') else: plt.savefig(filenm + "_" + "_".join(codes) + fext, bbox_inches='tight') plt.close()
def plot_bestFit_Spectrum(spec, clr, specs, atmfile, filters, kurucz, tepfile, outflux, data, uncert, direct): ''' Plot Transit spectrum ''' # get star data R_star, T_star, sma, gstar = bf.get_starData(tepfile) # get surface gravity grav, Rp = mat.get_g(tepfile) # convert Rp to m Rp = Rp * 1000 # ratio planet to star rprs = Rp / R_star # read kurucz file starfl, starwn, tmodel, gmodel = w.readkurucz(kurucz, T_star, gstar) # read best-fit spectrum output file, take wn and spectra values (head, tail) = os.path.split(outflux) specwn, bestspectrum = rt.readspectrum(direct + '/' + tail, wn=True) # convert wn to wl specwl = 1e4 / specwn # number of filters nfilters = len(filters) # read and resample the filters: nifilter = [] # Normalized interpolated filter istarfl = [] # interpolated stellar flux wnindices = [] # wavenumber indices used in interpolation meanwn = [] # Filter mean wavenumber for i in np.arange(nfilters): # read filter: filtwaven, filttransm = w.readfilter(filters[i]) meanwn.append(np.sum(filtwaven * filttransm) / sum(filttransm)) # resample filter and stellar spectrum: nifilt, strfl, wnind = w.resample(specwn, filtwaven, filttransm, starwn, starfl) nifilter.append(nifilt) istarfl.append(strfl) wnindices.append(wnind) # convert mean wn to mean wl meanwl = 1e4 / np.asarray(meanwn) # band-integrate the flux-ratio or modulation: bandflux = np.zeros(nfilters, dtype='d') bandmod = np.zeros(nfilters, dtype='d') for i in np.arange(nfilters): fluxrat = (bestspectrum[wnindices[i]] / istarfl[i]) * rprs * rprs bandflux[i] = w.bandintegrate(fluxrat, specwn, nifilter[i], wnindices[i]) bandmod[i] = w.bandintegrate(bestspectrum[wnindices[i]], specwn, nifilter[i], wnindices[i]) # stellar spectrum on specwn: sinterp = si.interp1d(starwn, starfl) sflux = sinterp(specwn) frat = bestspectrum / sflux * rprs * rprs ###################### plot figure ############################# plt.rcParams["mathtext.default"] = 'rm' matplotlib.rcParams.update({'mathtext.default': 'rm'}) #matplotlib.rcParams.update({'fontsize': 10,}) matplotlib.rcParams.update({ 'axes.labelsize': 16, #'text.fontsize': 10, 'legend.fontsize': 14, 'xtick.labelsize': 20, 'ytick.labelsize': 20, }) plt.figure(2, (8.5, 5)) plt.clf() #plt.xlim(0.60, 5.5) #plt.xlim(min(specwl),max(specwl)) # plot eclipse spectrum #gfrat = gaussf(frat, 0) plt.semilogx(specwl, frat * 1e3, clr, lw=1.5, label="Spectrum", linewidth=4) #cornflowerblue, lightskyblue #plt.errorbar(meanwl, data*1e3, uncert*1e3, fmt="ko", label="Data", alpha=0.7) plt.errorbar(meanwl, data * 1e3, uncert * 1e3, fmt=".", color='k', zorder=100, capsize=2, capthick=1, label="Data", alpha=0.7) #plt.plot(meanwl, bandflux*1e3, "ko", label="model") plt.ylabel(r"$F_p/F_s$ (10$^{-3}$)", fontsize=24) leg = plt.legend(loc="upper left") #leg.draw_frame(False) leg.get_frame().set_alpha(0.5) ax = plt.subplot(111) ax.set_xscale('log') ax.get_xaxis().set_major_formatter(matplotlib.ticker.ScalarFormatter()) ax.set_xticks([0.7, 0.8, 0.9, 1.0, 2.0, 3.0, 4.0, 5.0]) ax.set_xticklabels(["0.7", "", "", "1.0", "2.0", "3.0", "4.0", "5.0"]) plt.xlabel(r"${\rm Wavelength\ \ (um)}$", fontsize=24) nfilters = len(filters) # plot filter bandpasses for i in np.arange(nfilters - 15): (head, tail) = os.path.split(filters[i]) lbl = tail[:-4] # read filter: wn, respons = w.readfilter(filters[i]) respons = respons / 3 - 0.4 wl = 10000.0 / wn #plt.plot(wl, respons, color='crimson', linewidth =1) if lbl == 'spitzer_irac1_sa' or lbl == 'spitzer_irac2_sa': respons = respons * 2 + 0.4 plt.plot(wl, respons, color='grey', linewidth=1, alpha=0.5) #plt.plot(wl, respons*2, color='orangered', linewidth =1) elif lbl == 'Wang-Hband' or lbl == 'Wang-Kband': plt.plot(wl, respons, 'grey', linewidth=1, alpha=0.5) elif lbl == 'VLT_1190' or lbl == 'VLT_2090': plt.plot(wl, respons, color='grey', linewidth=2, alpha=0.5) #plt.plot(wl, respons, color='firebrick', linewidth =2) elif lbl == 'GROND_K_JB' or lbl == 'GROND_i_JB': plt.plot(wl, respons, 'grey', linewidth=1, alpha=0.5) elif lbl == 'Zhou_Ks': plt.plot(wl, respons, 'grey', linewidth=1, alpha=0.5) plt.ylim(-0.4, 7) plt.text(1.9, 3, specs, color=clr, fontsize=26) ###################### INSET PT and ABUN FIGURE #################### b = plt.axes([.21, .45, .14, .24]) # read atmfile molecules, pres, temp, abundances = mat.readatm(atmfile) plt.semilogy(temp, pres, color='r', linewidth=3) plt.xlim(1000, 2200) plt.ylim(max(pres), min(pres)) b.minorticks_off() yticks = [1e2, 1e1, 1, 1e-1, 1e-2, 1e-3, 1e-4, 1e-5] ylabels = [ "10$^{2}$", "", "10$^{0}$", "", "10$^{-2}$", "", "10$^{-4}$", "" ] plt.yticks(yticks, ylabels, fontsize=8) xticks = [1000, 1200, 1400, 1600, 1800, 2000, 2200] xlabels = ["", "1200", "", "", "1800", ""] plt.xticks(xticks, xlabels, fontsize=12) plt.xlabel('T (K)', fontsize=12) plt.ylabel('P (bar)', fontsize=12) # ############################## SECOND INSET ABUN c = plt.axes([.35, .45, .14, .24]) # Sets the second argument given as the species names species = spec # Open the atmospheric file and read f = open(atmfile, 'r') lines = np.asarray(f.readlines()) f.close() # Get molecules names imol = np.where(lines == "#SPECIES\n")[0][0] + 1 molecules = lines[imol].split() nmol = len(molecules) for m in np.arange(nmol): molecules[m] = molecules[m].partition('_')[0] nspec = 1 # Populate column numbers for requested species and # update list of species if order is not appropriate columns = [] spec = [] for i in np.arange(nmol): if molecules[i] == species: columns.append(i + 3) # defines p, T +2 or rad, p, T +3 spec.append(species) # Convert spec to tuple spec = tuple(spec) # Concatenate spec with pressure for data and columns data = tuple(np.concatenate((['p'], spec))) usecols = tuple(np.concatenate( ([1], columns))) # defines p as 0 columns, or p as 1 columns # Load all data for all interested species data = np.loadtxt(atmfile, dtype=float, comments='#', delimiter=None, \ converters=None, skiprows=13, usecols=usecols, unpack=True) plt.loglog(data[1], data[0], '-', color=clr, \ linewidth=3) plt.ylim(max(pres), min(pres)) c.minorticks_off() yticks = [1e2, 1e1, 1, 1e-1, 1e-2, 1e-3, 1e-4, 1e-5] ylabels = [] plt.yticks(yticks, ylabels) plt.xlim(1e-12, 1e-2) xticks = [1e-11, 1e-9, 1e-7, 1e-5, 1e-3] xlabels = ["10$^{-11}$", "", "10$^{-5}$", "", "10$^{-3}$"] plt.xticks(xticks, xlabels, fontsize=12) plt.xlabel('Mix. fraction', fontsize=12) plt.subplots_adjust(bottom=0.16) spec = spec[0] print(spec) plt.savefig(spec + "_transSpec_new.png") plt.savefig(spec + "_transSpec_new.ps")
def plot_bestFit_Spectrum(filters, kurucz, tepfile, solution, output, data, uncert, date_dir): ''' Plot BART best-model spectrum ''' # get star data R_star, T_star, sma, gstar = get_starData(tepfile) # get surface gravity grav, Rp = mat.get_g(tepfile) # convert Rp to m Rp = Rp * 1000 # ratio planet to star rprs = Rp/R_star # read kurucz file starfl, starwn, tmodel, gmodel = w.readkurucz(kurucz, T_star, gstar) # read best-fit spectrum output file, take wn and spectra values if solution == 'eclipse': specwn, bestspectrum = rt.readspectrum(date_dir + output, wn=True) # print on screen print(" Plotting BART best-fit eclipse spectrum figure.") elif solution == 'transit': specwn, bestspectrum = rt.readspectrum(date_dir + output, wn=True) # print on screen print(" Plotting BART best-fit modulation spectrum figure.") # convert wn to wl specwl = 1e4/specwn # number of filters nfilters = len(filters) # read and resample the filters: nifilter = [] # Normalized interpolated filter istarfl = [] # interpolated stellar flux wnindices = [] # wavenumber indices used in interpolation meanwn = [] # Filter mean wavenumber for i in np.arange(nfilters): # read filter: filtwaven, filttransm = w.readfilter(filters[i]) meanwn.append(np.sum(filtwaven*filttransm)/sum(filttransm)) # resample filter and stellar spectrum: nifilt, strfl, wnind = w.resample(specwn, filtwaven, filttransm, starwn, starfl) nifilter.append(nifilt) istarfl.append(strfl) wnindices.append(wnind) # convert mean wn to mean wl meanwl = 1e4/np.asarray(meanwn) # band-integrate the flux-ratio or modulation: bandflux = np.zeros(nfilters, dtype='d') bandmod = np.zeros(nfilters, dtype='d') for i in np.arange(nfilters): fluxrat = (bestspectrum[wnindices[i]]/istarfl[i]) * rprs*rprs bandflux[i] = w.bandintegrate(fluxrat, specwn, nifilter[i], wnindices[i]) bandmod[i] = w.bandintegrate(bestspectrum[wnindices[i]], specwn, nifilter[i], wnindices[i]) # stellar spectrum on specwn: sinterp = si.interp1d(starwn, starfl) sflux = sinterp(specwn) frat = bestspectrum/sflux * rprs * rprs # plot figure plt.rcParams["mathtext.default"] = 'rm' matplotlib.rcParams.update({'mathtext.default':'rm'}) matplotlib.rcParams.update({'font.size':10}) plt.figure(3, (8.5, 5)) plt.clf() # depending on solution plot eclipse or modulation spectrum if solution == 'eclipse': gfrat = gaussf(frat, 2) plt.semilogx(specwl, gfrat*1e3, "b", lw=1.5, label="Best-fit") plt.errorbar(meanwl, data*1e3, uncert*1e3, fmt="or", label="data") plt.plot(meanwl, bandflux*1e3, "ok", label="model", alpha=1.0) plt.ylabel(r"$F_p/F_s$ (10$^{3}$)", fontsize=12) elif solution == 'transit': gmodel = gaussf(bestspectrum, 2) plt.semilogx(specwl, gmodel, "b", lw=1.5, label="Best-fit") # Check units! plt.errorbar(meanwl, data, uncert, fmt="or", label="data") plt.plot(meanwl, bandmod, "ok", label="model", alpha=0.5) plt.ylabel(r"$(R_p/R_s)^2$", fontsize=12) leg = plt.legend(loc="lower right") leg.get_frame().set_alpha(0.5) ax = plt.subplot(111) ax.set_xscale('log') plt.xlabel(r"${\rm Wavelength\ \ (um)}$", fontsize=12) ax.get_xaxis().set_major_formatter(matplotlib.ticker.ScalarFormatter()) ax.set_xticks(np.arange(round(min(specwl)),max(specwl),1)) plt.xlim(min(specwl),max(specwl)) plt.savefig(date_dir + "BART-bestFit-Spectrum.png")