def get_frac_dev(self, logZ, CO_ratio, custom_abundances): Rp = 7.14e7 Mp = 2.0e27 Rs = 7e8 T = 1200 depth_calculator = TransitDepthCalculator() wavelengths, transit_depths = depth_calculator.compute_depths( Rs, Mp, Rp, T, logZ=logZ, CO_ratio=CO_ratio, custom_abundances=custom_abundances, cloudtop_pressure=1e4) # This ExoTransmit run is done without SH, since it's not present in # GGchem ref_wavelengths, ref_depths = np.loadtxt("tests/testing_data/hot_jupiter_spectra.dat", unpack=True, skiprows=2) ref_depths /= 100 frac_dev = np.abs(ref_depths - transit_depths) / ref_depths '''plt.plot(wavelengths, transit_depths, label="platon") plt.plot(ref_wavelengths, ref_depths, label="ExoTransmit") plt.legend() plt.figure() plt.plot(np.log10(frac_dev)) plt.show()''' return frac_dev
def test_power_law_haze(self): Rs = R_sun Mp = M_jup Rp = R_jup T = 1200 abundances = AbundanceGetter().get(0, 0.53) for key in abundances: abundances[key] *= 0 abundances["H2"] += 1 depth_calculator = TransitDepthCalculator() wavelengths, transit_depths, info_dict = depth_calculator.compute_depths( Rs, Mp, Rp, T, logZ=None, CO_ratio=None, cloudtop_pressure=np.inf, custom_abundances = abundances, add_gas_absorption=False, add_collisional_absorption=False, full_output=True) g = G * Mp / Rp**2 H = k_B * T / (2 * AMU * g) gamma = 0.57721 polarizability = 0.8059e-30 sigma = 128. * np.pi**5/3 * polarizability**2 / depth_calculator.atm.lambda_grid**4 kappa = sigma / (2 * AMU) P_surface = 1e8 R_surface = info_dict["radii"][-1] tau_surface = P_surface/g * np.sqrt(2*np.pi*R_surface/H) * kappa analytic_R = R_surface + H*(gamma + np.log(tau_surface) + scipy.special.expn(1, tau_surface)) analytic_depths = analytic_R**2 / Rs**2 ratios = analytic_depths / transit_depths relative_diffs = np.abs(ratios - 1) self.assertTrue(np.all(relative_diffs < 0.001))
def test_k_coeffs_unbinned(self): xsec_calc = TransitDepthCalculator(method="xsec") ktab_calc = TransitDepthCalculator(method="ktables") xsec_wavelengths, xsec_depths = xsec_calc.compute_depths(R_sun, M_jup, R_jup, 1000) #Smooth from R=1000 to R=100 to match ktables N = 10 smoothed_xsec_wavelengths = uniform_filter(xsec_wavelengths, N)[::N] smoothed_xsec_depths = uniform_filter(xsec_depths, N)[::N] ktab_wavelengths, ktab_depths = ktab_calc.compute_depths(R_sun, M_jup, R_jup, 1000) diffs = np.abs(ktab_depths - smoothed_xsec_depths[:-1]) self.assertTrue(np.median(diffs) < 20e-6) self.assertTrue(np.percentile(diffs, 95) < 50e-6) self.assertTrue(np.max(diffs) < 150e-6)
def test_unbound_atmosphere(self): Rp = 6.378e6 Mp = 5.97e20 # Note how low this is--10^-4 Earth masses! Rs = 6.97e8 T = 300 depth_calculator = TransitDepthCalculator() with self.assertRaises(AtmosphereError): wavelengths, transit_depths = depth_calculator.compute_depths( Rs, Mp, Rp, T, logZ=0.2, CO_ratio=1.1, T_star=6100)
def test_bin_wavelengths(self): Rp = 7.14e7 Mp = 7.49e26 Rs = 7e8 T = 1200 depth_calculator = TransitDepthCalculator() bins = np.array([[0.4,0.6], [1,1.1], [1.2,1.4], [3.2,4], [5,6]]) bins *= 1e-6 depth_calculator.change_wavelength_bins(bins) wavelengths, transit_depths = depth_calculator.compute_depths( Rs, Mp, Rp, T, logZ=0.2, CO_ratio=1.1, T_star=6100) self.assertEqual(len(wavelengths), len(bins)) self.assertEqual(len(transit_depths), len(bins)) wavelengths, transit_depths = depth_calculator.compute_depths( Rs, Mp, Rp, T, logZ=0.2, CO_ratio=1.1, T_star=12000) self.assertEqual(len(wavelengths), len(bins)) self.assertEqual(len(transit_depths), len(bins))
def test_k_coeffs_binned(self): wavelengths = np.exp(np.arange(np.log(0.31e-6), np.log(29e-6), 1./20)) wavelength_bins = np.array([wavelengths[0:-1], wavelengths[1:]]).T xsec_calc = TransitDepthCalculator(method="xsec") xsec_calc.change_wavelength_bins(wavelength_bins) ktab_calc = TransitDepthCalculator(method="ktables") ktab_calc.change_wavelength_bins(wavelength_bins) wavelengths, xsec_depths = xsec_calc.compute_depths(R_sun, M_jup, R_jup, 300, logZ=1, CO_ratio=1.5) wavelengths, ktab_depths = ktab_calc.compute_depths(R_sun, M_jup, R_jup, 300, logZ=1, CO_ratio=1.5) diffs = np.abs(ktab_depths - xsec_depths) '''plt.semilogx(wavelengths, xsec_depths) plt.semilogx(wavelengths, ktab_depths) plt.figure() plt.semilogx(wavelengths, 1e6 * diffs) plt.show()''' self.assertTrue(np.median(diffs) < 10e-6) self.assertTrue(np.percentile(diffs, 95) < 20e-6) self.assertTrue(np.max(diffs) < 30e-6)
scatt_slope = best_params_dict['scatt_slope'] error_multiple = best_params_dict['error_multiple'] T_star = best_params_dict['T_star'] T_spot = best_params_dict['T_spot'] spot_cov_frac = best_params_dict['spot_cov_frac'] frac_scale_height = best_params_dict['frac_scale_height'] log_number_density = best_params_dict['log_number_density'] log_part_size = best_params_dict['log_part_size'] part_size_std = best_params_dict['part_size_std'] ri = best_params_dict['ri'] # Compute best-fit theoretical model try: wavelengths, calculated_depths = calculator.compute_depths( Rs, Mp, Rp, T_eq, logZ, CO_ratio, scattering_factor=10**log_scatt_factor, scattering_slope=scatt_slope, cloudtop_pressure=10**log_cloudtop_P, T_star=T_star) except AtmosphereError as e: print(e) # Plot the data on top of the best fit high-resolution model plt.errorbar(METRES_TO_UM * np.mean(wave_bins, axis=1), depths, yerr=errors, fmt='.', color='k', zorder=100) plt.plot(METRES_TO_UM * wavelengths, calculated_depths) plt.xlabel("Wavelength (um)") plt.ylabel("Transit depth") plt.xscale('log') plt.tight_layout() if bayesian_model == 'multinest':
import numpy as np import matplotlib.pyplot as plt from platon.transit_depth_calculator import TransitDepthCalculator from platon.constants import M_jup, R_sun, R_jup # All quantities in SI Rs = 1.16 * R_sun #Radius of star Mp = 0.73 * M_jup #Mass of planet Rp = 1.40 * R_jup #Radius of planet T = 1200 #Temperature of isothermal part of the atmosphere #create a TransitDepthCalculator object and compute wavelength dependent transit depths depth_calculator = TransitDepthCalculator( method="ktables") #put "xsec" for opacity sampling wavelengths, transit_depths = depth_calculator.compute_depths( Rs, Mp, Rp, T, CO_ratio=0.2, cloudtop_pressure=1e4) # Uncomment the code below to print #print("#Wavelength(m) Depth") #for i in range(len(wavelengths)): # print(wavelengths[i], transit_depths[i]) # Uncomment the code below to plot #plt.semilogx(1e6*wavelengths, transit_depths) #plt.xlabel("Wavelength (um)") #plt.ylabel("Transit depth") #plt.show()
def test_bounds_checking(self): Rp = 7.14e7 Mp = 7.49e26 Rs = 7e8 T = 1200 logZ = 0 CO_ratio = 1.1 calculator = TransitDepthCalculator() with self.assertRaises(AtmosphereError): calculator.compute_depths(Rs, Mp, Rp, 199, logZ=logZ, CO_ratio=CO_ratio) with self.assertRaises(AtmosphereError): calculator.compute_depths(Rs, Mp, Rp, 3001, logZ=logZ, CO_ratio=CO_ratio) with self.assertRaises(ValueError): calculator.compute_depths(Rs, Mp, Rp, T, logZ=-1.1, CO_ratio=CO_ratio) with self.assertRaises(ValueError): calculator.compute_depths(Rs, Mp, Rp, T, logZ=3.1, CO_ratio=CO_ratio) with self.assertRaises(ValueError): calculator.compute_depths(Rs, Mp, Rp, T, logZ=logZ, CO_ratio=0.01) with self.assertRaises(ValueError): calculator.compute_depths(Rs, Mp, Rp, T, logZ=logZ, CO_ratio=11) with self.assertRaises(ValueError): calculator.compute_depths(Rs, Mp, Rp, T, logZ=logZ, CO_ratio=CO_ratio, cloudtop_pressure=1e-4) with self.assertRaises(ValueError): calculator.compute_depths(Rs, Mp, Rp, T, logZ=logZ, CO_ratio=CO_ratio, cloudtop_pressure=1.1e8) # Infinity should be fine calculator.compute_depths(Rs, Mp, Rp, T, logZ=logZ, CO_ratio=CO_ratio, cloudtop_pressure=np.inf)
wave_bins = [] wave_bins.append([3.2, 4.0]) wave_bins.append([4.0, 5.0]) return 1e-6 * np.array(wave_bins) bins = np.concatenate([stis_bins(), wfc3_bins(), spitzer_bins()]) R_guess = 1.4 * R_jup T_guess = 1200 depth_calculator = TransitDepthCalculator() depth_calculator.change_wavelength_bins(bins) wavelengths, depths = depth_calculator.compute_depths(1.19 * R_sun, 0.73 * M_jup, R_guess, T_guess, T_star=6091) # Uncomment the code below to print #print("#Wavelength(um) Depth") #for i in range(len(wavelengths)): # print(wavelengths[i], depths[i]) # Uncomment the code below to plot #plt.plot(1e6*wavelengths, depths) #plt.xlabel("Wavelength (um)") #plt.ylabel("Transit depth") #plt.show()
depth_calculator = TransitDepthCalculator(Rs, g) wavelength_bins = [] stis_wavelengths = np.linspace(0.4e-6, 0.7e-6, 30) for i in range(len(stis_wavelengths) - 1): wavelength_bins.append([stis_wavelengths[i], stis_wavelengths[i + 1]]) wfc_wavelengths = np.linspace(1.1e-6, 1.7e-6, 30) for i in range(len(wfc_wavelengths) - 1): wavelength_bins.append([wfc_wavelengths[i], wfc_wavelengths[i + 1]]) wavelength_bins.append([3.2e-6, 4e-6]) wavelength_bins.append([4e-6, 5e-6]) depth_calculator.change_wavelength_bins(wavelength_bins) wavelengths, transit_depths = depth_calculator.compute_depths( Rp, temperature, logZ=logZ, CO_ratio=CO, cloudtop_pressure=1e3) #wavelengths, depths2 = depth_calculator.compute_depths(71414515.1348402, P_prof retriever = Retriever() fit_info = retriever.get_default_fit_info(Rs, g, 0.99 * Rp, 0.9 * temperature, logZ=2, CO_ratio=1, add_fit_params=True) errors = np.random.normal(scale=50e-6, size=len(transit_depths)) transit_depths += errors
Rs = 0.947 * R_sun Mp = 8.145 * M_earth Rp = 1.7823 * R_earth T = 1970 logZ = 1.09 CO_ratio = 1.57 log_cloudtop_P = 4.875 #create a TransitDepthCalculator object and compute wavelength dependent transit depths depth_calculator = TransitDepthCalculator() wavelengths, transit_depths, info = depth_calculator.compute_depths( Rs, Mp, Rp, T, T_star=5196, logZ=logZ, CO_ratio=CO_ratio, cloudtop_pressure=10.0**log_cloudtop_P, full_output=True) color_bins = 1e-6 * np.array([ [4, 5], [3.2, 4], [1.1, 1.7], ]) visualizer = Visualizer() image, scale = visualizer.draw(info, color_bins, star_color=[1, 1, 0.9],
class SpectrumGenerator: def __init__(self): ''' The SpectrumGenerator generates spectra at different resolutions and with noises ''' self.calculator = TransitDepthCalculator() def generate_spectrum(self, Rs, Mp, Rp, T_planet, T_star, Ms, logZ=0, CO_ratio=0.53, scattering_factor=1, cloudtop_pressure=np.inf, resolution=None, noise_level=None, max_wavelength=3e-5, min_wavelength=3e-7, albedo=0.8): ''' Generates a spectrum using the PLATON TransitDepthCalculator. Parameters ---------- Rs : float Stellar radius in solar radii Mp : float Planet mass in Jupiter masses Rp : float Planet radius in Jupiter radii T_planet : float Temperature of the isothermal atmosphere in Kelvin T_star : float Blackbody temperature of the star Ms : float Mass of the star in solar masses logZ : float, optional base-10 logarithm of the metallicity in solar units. Default is 0. CO_ratio : float, optional C/O atomic ratio in the atmosphere. The default value is 0.53 (solar) value. scattering_factor : float, optional Makes rayleigh scattering this many times as strong. Default is 1 cloudtop_pressure : float, optional Pressure level (in PA) below which light cannot penetrate. Use np.inf for cloudless. Default is np.inf resolution : int or None, optional If provided, will produce spectra of the given spectral resolution. Default is None noise_level : float or None, optional The noise on the data in ppm. If provided, Gaussian noise will be added to the spectrum. Default is None. max_wavelength : float, optional The maximum wavelength to consider in m. Default is 3e-5 min_wavelength : float, optional The minimum wavelength to consider in m. Default is 3e-7 Returns ------- spectrum : TransitCurveGen.Spectrum A Spectrum object containing all the information on the spectrum ''' # Generate the basic spectrum wavelengths, depths = self.calculator.compute_depths( Rs * R_sun, Mp * M_jup, Rp * R_jup, T_planet, logZ=logZ, CO_ratio=CO_ratio, scattering_factor=scattering_factor, cloudtop_pressure=cloudtop_pressure) # Remove wavelengths not of interest # First cut off the large wavelengths from depths and wavelengths. # MUST be done in this order else depths can't reference wavelengths depths = depths[wavelengths <= max_wavelength] wavelengths = wavelengths[wavelengths <= max_wavelength] depths = depths[wavelengths >= min_wavelength] wavelengths = wavelengths[wavelengths >= min_wavelength] # Convolve to give a particular resolution if resolution is not None: wavelengths, depths = self._bin_spectrum(wavelengths, depths, resolution) # Add in noise if required if noise_level is not None: wavelengths, depths = self._add_noise(wavelengths, depths, noise_level) info_dict = { 'Rs': Rs, 'Mp': Mp, 'Rp': Rp, 'T_planet': T_planet, 'logZ': logZ, 'CO_ratio': CO_ratio, 'scattering_factor': scattering_factor, 'cloudtop_pressure': cloudtop_pressure, 'resolution': resolution, 'noise': noise_level, 'T_star': T_star, 'Ms': Ms, 'albedo': albedo } return Spectrum(wavelengths, depths, info_dict) def _bin_spectrum(self, wavelengths, depths, R): ''' Bins a given spectrum to a spectral resolution R Parameters ---------- wavelengths : array_like, shape (n_wavelengths,) The wavelengths of each data point. We assume that these are logarithmically spaced. depths : array_like, shape (n_wavelengths,) The transit depth of each data point. R : int Spectral resolution to bin to. Returns ------- wavelengths : np.array The centre wavelength of each new wavelength bin depths : np.array The transit depth in each of the new wavelength bins Notes ----- The binning is done using convolution with a top-hat. ''' # Take log base-10 of the wavelengths to work on resolution log_wl = np.log10(wavelengths) # Find the wavelength spacing in log space delta = round(log_wl[1] - log_wl[0], 7) # Find the original resolution of the spectrum R_original = 1 // delta if R > R_original: raise ValueError( 'Spectrum has resolution {}: cannot increase resolution to {}'. format(R_original, R)) # Work out how many current wl bins we need for the given resolution nbins = int(1 // (R * delta)) # Do the binning wavelengths = wavelengths[:(wavelengths.size // nbins) * nbins].reshape(-1, nbins).mean(axis=1) depths = depths[:(depths.size // nbins) * nbins].reshape( -1, nbins).mean(axis=1) return wavelengths, depths def _add_noise(self, wavelengths, depths, noise_ppm): ''' Adds Gaussian noise to a given spectrum Parameters ---------- wavelengths : array_like, shape (n_wavelengths,) The wavelengths of each data point. We assume that these are logarithmically spaced. depths : array_like, shape (n_wavelengths,) The transit depth of each data point. noise_ppm : int The noise in parts per million Returns ------- wavelengths : np.array The wavelengths depths : np.array The depths with Gaussian noise added. ''' disp_arr = np.zeros(len(depths)) for i, d in enumerate(depths): disp_arr[i] = np.random.normal(0, noise_ppm * 1e-6) depths += disp_arr return wavelengths, depths