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_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)
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_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)
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))
time_stamp = datetime.utcnow().strftime("%Y%m%d%H%M%S") result_dict = {'samples':result.samples, 'weights':result.weights, 'logl':result.logl} joblib.dump(result_dict, 'multinest_results_{}.joblib.save'.format(time_stamp)) # Establish the Range in Wavelength to plot high resolution figures wave_min = wave_bins.min() wave_max = wave_bins.max() n_theory_pts = 500 wavelengths_theory = np.linspace(wave_min, wave_max, n_theory_pts) half_diff_lam = 0.5*np.median(np.diff(wavelengths_theory)) # Setup calculator to use the theoretical wavelengths calculator = TransitDepthCalculator(include_condensation=True) calculator.change_wavelength_bins(np.transpose([wavelengths_theory-half_diff_lam, wavelengths_theory+half_diff_lam])) retriever._validate_params(fit_info, calculator) # Allocate the best-fit parameters from the `result` class best_params_arr = result.samples[np.argmax(result.logl)] best_params_dict = {key:val for key,val in zip(fit_info.fit_param_names, best_params_arr)} # Set the static parameteres to the default values for key in fit_info.all_params.keys(): if key not in best_params_dict.keys(): best_params_dict[key] = fit_info.all_params[key].best_guess # Assign the best fit model parameters to necessary variables Rs = best_params_dict['Rs']
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 __init__(self, *args, **kwargs): super(TestMieAbsorption, self).__init__(*args, **kwargs) # We're storing this object to take advantage of its cache, not # for speed, but to test the cache self.calc = TransitDepthCalculator()
return np.array(wavelength_bins) def spitzer_bins(): 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
def run_multinest(self, transit_bins, transit_depths, transit_errors, eclipse_bins, eclipse_depths, eclipse_errors, fit_info, include_condensation=True, plot_best=False, **nestle_kwargs): '''Runs nested sampling to retrieve atmospheric parameters. Parameters ---------- transit_bins : array_like, shape (N,2) Wavelength bins, where wavelength_bins[i][0] is the start wavelength and wavelength_bins[i][1] is the end wavelength for bin i. transit_depths : array_like, length N Measured transit depths for the specified wavelength bins transit_errors : array_like, length N Errors on the aforementioned transit depths eclipse_bins : array_like, shape (N,2) Wavelength bins, where wavelength_bins[i][0] is the start wavelength and wavelength_bins[i][1] is the end wavelength for bin i. eclipse_depths : array_like, length N Measured eclipse depths for the specified wavelength bins eclipse_errors : array_like, length N Errors on the aforementioned eclipse depths fit_info : :class:`.FitInfo` object Tells us what parameters to freely vary, and in what range those parameters can vary. Also sets default values for the fixed parameters. include_condensation : bool, optional When determining atmospheric abundances, whether to include condensation. plot_best : bool, optional If True, plots the best fit model with the data **nestle_kwargs : keyword arguments to pass to nestle's sample method Returns ------- result : Result object This returns the object returned by nestle.sample, slightly modified. The object is dictionary-like and has many useful items. For example, result.samples (or alternatively, result["samples"]) are the parameter values of each sample, result.weights contains the weights, result.logl contains the ln likelihoods, and result.logp contains the ln posteriors (this is added by PLATON). result.logz is the natural logarithm of the evidence. ''' transit_calc = TransitDepthCalculator( include_condensation=include_condensation) transit_calc.change_wavelength_bins(transit_bins) eclipse_calc = EclipseDepthCalculator() eclipse_calc.change_wavelength_bins(eclipse_bins) self._validate_params(fit_info, transit_calc) def transform_prior(cube): new_cube = np.zeros(len(cube)) for i in range(len(cube)): new_cube[i] = fit_info._from_unit_interval(i, cube[i]) return new_cube def multinest_ln_like(cube): return self._ln_like(cube, transit_calc, eclipse_calc, fit_info, transit_depths, transit_errors, eclipse_depths, eclipse_errors) def callback(callback_info): print("Iteration {}: {}".format(callback_info["it"], self.pretty_print(fit_info))) result = nestle.sample(multinest_ln_like, transform_prior, fit_info._get_num_fit_params(), callback=callback, method='multi', **nestle_kwargs) result.logp = result.logl + np.array( [fit_info._ln_prior(params) for params in result.samples]) best_params_arr = result.samples[np.argmax(result.logp)] write_param_estimates_file( nestle.resample_equal(result.samples, result.weights), best_params_arr, np.max(result.logp), fit_info.fit_param_names) if plot_best: self._ln_prob(best_params_arr, transit_calc, eclipse_calc, fit_info, transit_depths, transit_errors, eclipse_depths, eclipse_errors, plot=True) return result
def run_emcee(self, transit_bins, transit_depths, transit_errors, eclipse_bins, eclipse_depths, eclipse_errors, fit_info, nwalkers=50, nsteps=1000, include_condensation=True, plot_best=False): '''Runs affine-invariant MCMC to retrieve atmospheric parameters. Parameters ---------- transit_bins : array_like, shape (N,2) Wavelength bins, where wavelength_bins[i][0] is the start wavelength and wavelength_bins[i][1] is the end wavelength for bin i. transit_depths : array_like, length N Measured transit depths for the specified wavelength bins transit_errors : array_like, length N Errors on the aforementioned transit depths eclipse_bins : array_like, shape (N,2) Wavelength bins, where wavelength_bins[i][0] is the start wavelength and wavelength_bins[i][1] is the end wavelength for bin i. eclipse_depths : array_like, length N Measured eclipse depths for the specified wavelength bins eclipse_errors : array_like, length N Errors on the aforementioned eclipse depths fit_info : :class:`.FitInfo` object Tells the method what parameters to freely vary, and in what range those parameters can vary. Also sets default values for the fixed parameters. nwalkers : int, optional Number of walkers to use nsteps : int, optional Number of steps that the walkers should walk for include_condensation : bool, optional When determining atmospheric abundances, whether to include condensation. plot_best : bool, optional If True, plots the best fit model with the data Returns ------- result : EnsembleSampler object This returns emcee's EnsembleSampler object. The most useful attributes in this item are result.chain, which is a (W x S X P) array where W is the number of walkers, S is the number of steps, and P is the number of parameters; and result.lnprobability, a (W x S) array of log probabilities. For your convenience, this object also contains result.flatchain, which is a (WS x P) array where WS = W x S is the number of samples; and result.flatlnprobability, an array of length WS ''' initial_positions = fit_info._generate_rand_param_arrays(nwalkers) transit_calc = TransitDepthCalculator( include_condensation=include_condensation) transit_calc.change_wavelength_bins(transit_bins) eclipse_calc = EclipseDepthCalculator() eclipse_calc.change_wavelength_bins(eclipse_bins) self._validate_params(fit_info, transit_calc) sampler = emcee.EnsembleSampler( nwalkers, fit_info._get_num_fit_params(), self._ln_prob, args=(transit_calc, eclipse_calc, fit_info, transit_depths, transit_errors, eclipse_depths, eclipse_errors)) for i, result in enumerate( sampler.sample(initial_positions, iterations=nsteps)): if (i + 1) % 10 == 0: print("Step {}: {}".format(i + 1, self.pretty_print(fit_info))) best_params_arr = sampler.flatchain[np.argmax( sampler.flatlnprobability)] write_param_estimates_file(sampler.flatchain, best_params_arr, np.max(sampler.flatlnprobability), fit_info.fit_param_names) if plot_best: self._ln_prob(best_params_arr, transit_calc, eclipse_calc, fit_info, transit_depths, transit_errors, eclipse_depths, eclipse_errors, plot=True) return sampler
def __init__(self): ''' The SpectrumGenerator generates spectra at different resolutions and with noises ''' self.calculator = TransitDepthCalculator()