def test_parametric(self): profile = Profile() P0 = np.min(profile.pressures) T0 = 1300 P1 = 1e-3 alpha1 = 0.3 alpha2 = 0.5 P3 = 1e4 T3 = 2000 P2, T2 = profile.set_parametric(T0, P1, alpha1, alpha2, P3, T3) self.assertTrue( abs(P3 - P2 * np.exp(alpha2 * (T3 - T2)**0.5)) < 1e-3 * P3) T1 = np.log(P1 / P0)**2 / alpha1**2 + T0 self.assertTrue( abs(P1 - P0 * np.exp(alpha1 * (T1 - T0)**0.5)) < 1e-3 * P1) self.assertTrue( abs(P1 - P2 * np.exp(-alpha2 * (T1 - T2)**0.5)) < 1e-3 * P1)
def test_isothermal(self): Ts = 5700 Tp = 1500 p = Profile() p.set_isothermal(Tp) calc = EclipseDepthCalculator() wavelengths, depths, info_dict = calc.compute_depths(p, R_sun, M_jup, R_jup, Ts, full_output=True) blackbody = np.pi * 2 * h * c**2 / wavelengths**5 / ( np.exp(h * c / wavelengths / k_B / Tp) - 1) rel_diffs = (info_dict["planet_spectrum"] - blackbody) / blackbody plt.loglog(1e6 * wavelengths, 1e-3 * blackbody, label="Blackbody") plt.loglog(1e6 * wavelengths, 1e-3 * info_dict["planet_spectrum"], label="PLATON") plt.xlabel("Wavelength (micron)", fontsize=12) plt.ylabel("Planet flux (erg/s/cm$^2$/micron)", fontsize=12) plt.legend() plt.tight_layout() plt.figure() plt.semilogx(1e6 * wavelengths, 100 * rel_diffs) plt.xlabel("Wavelength (micron)", fontsize=12) plt.ylabel("Relative difference (%)", fontsize=12) plt.tight_layout() plt.show() # Should be exact, but in practice isn't, due to our discretization self.assertLess(np.percentile(np.abs(rel_diffs), 50), 0.02) self.assertLess(np.percentile(np.abs(rel_diffs), 99), 0.05) self.assertLess(np.max(np.abs(rel_diffs)), 0.1) blackbody_star = np.pi * 2 * h * c**2 / wavelengths**5 / ( np.exp(h * c / wavelengths / k_B / Ts) - 1) approximate_depths = blackbody / blackbody_star * (R_jup / R_sun)**2 # Not expected to be very accurate because the star is not a blackbody self.assertLess( np.median( np.abs(approximate_depths - depths) / approximate_depths), 0.2)
def test_ktables_unbinned(self): profile = Profile() profile.set_from_radiative_solution(5052, 0.75 * R_sun, 0.03142 * AU, 1.129 * M_jup, 1.115 * R_jup, 0.983, -1.77, -0.44, -0.56, 0.23) xsec_calc = EclipseDepthCalculator(method="xsec") ktab_calc = EclipseDepthCalculator(method="ktables") xsec_wavelengths, xsec_depths = xsec_calc.compute_depths( profile, 0.75 * R_sun, 1.129 * M_jup, 1.115 * R_jup, 5052) 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( profile, 0.75 * R_sun, 1.129 * M_jup, 1.115 * R_jup, 5052) rel_diffs = np.abs(ktab_depths - smoothed_xsec_depths[:-1]) / ktab_depths self.assertTrue(np.median(rel_diffs) < 0.05)
def test_radiative_solution(self): p = Profile() # Parameters from Table 1 of http://iopscience.iop.org/article/10.1088/0004-637X/775/2/137/pdf p.set_from_radiative_solution(5040, 0.756 * R_sun, 0.031 * AU, 0.885 * M_jup, R_jup, 1, np.log10(3e-3), np.log10(1.58e-1), np.log10(1.58e-1), 0.5, 100) # Compare to Figure 2 of aforementioned paper is_upper_atm = np.logical_and(p.pressures > 0.1, p.pressures < 1e3) self.assertTrue(np.all(p.temperatures[is_upper_atm] > 1000)) self.assertTrue(np.all(p.temperatures[is_upper_atm] < 1100)) is_lower_atm = np.logical_and(p.pressures > 1e5, p.pressures < 3e6) self.assertTrue(np.all(p.temperatures[is_lower_atm] > 1600)) self.assertTrue(np.all(p.temperatures[is_lower_atm] < 1700)) self.assertTrue(np.all(np.diff(p.temperatures) > 0))
def test_ktables_binned(self): wavelengths = np.exp(np.arange(np.log(0.31e-6), np.log(29e-6), 1. / 20)) wavelengths = np.append(wavelengths[0:20], wavelengths[50:90]) wavelength_bins = np.array([wavelengths[0:-1], wavelengths[1:]]).T profile = Profile() profile.set_from_radiative_solution(5052, 0.75 * R_sun, 0.03142 * AU, 1.129 * M_jup, 1.115 * R_jup, 0.983, -1.77, -0.44, -0.56, 0.23) xsec_calc = EclipseDepthCalculator(method="xsec") xsec_calc.change_wavelength_bins(wavelength_bins) ktab_calc = EclipseDepthCalculator(method="ktables") ktab_calc.change_wavelength_bins(wavelength_bins) xsec_wavelengths, xsec_depths = xsec_calc.compute_depths( profile, 0.75 * R_sun, 1.129 * M_jup, 1.115 * R_jup, 5052) ktab_wavelengths, ktab_depths = ktab_calc.compute_depths( profile, 0.75 * R_sun, 1.129 * M_jup, 1.115 * R_jup, 5052) rel_diffs = np.abs(ktab_depths - xsec_depths) / ktab_depths self.assertTrue(np.median(rel_diffs) < 0.03) self.assertTrue(np.percentile(rel_diffs, 95) < 0.15) self.assertTrue(np.max(rel_diffs) < 0.2) '''print(np.median(rel_diffs), np.percentile(rel_diffs, 95), np.max(rel_diffs))
def test_set_opacity(self): # Can't easily test whether this is "right", but at least make sure it # runs p = Profile() p.set_isothermal(1200) calc = EclipseDepthCalculator() wavelengths, depths, info_dict = calc.compute_depths(p, R_sun, M_jup, R_jup, 5700, full_output=True) p.set_from_opacity(1700, info_dict) self.assertTrue(np.all(p.temperatures > 0)) self.assertTrue(np.all(~np.isnan(p.temperatures)))
import matplotlib.pyplot as plt import numpy as np from platon.constants import h, c, k_B, R_jup, M_jup, R_sun from platon.eclipse_depth_calculator import EclipseDepthCalculator from platon.TP_profile import Profile edges = np.linspace(1.1e-6, 1.7e-6, 30) bins = np.array([edges[0:-1], edges[1:]]).T p = Profile() p.set_parametric(1200, 500, 0.5, 0.6, 1e6, 1900) #p.set_isothermal(1500) calc = EclipseDepthCalculator() #calc.change_wavelength_bins(bins) wavelengths, depths, info_dict = calc.compute_depths(p, R_sun, M_jup, R_jup, 5700, full_output=True) plt.semilogy(p.temperatures, p.pressures) plt.xlabel("Temperature (K)") plt.ylabel("Pressure (Pa)") plt.figure() plt.loglog(1e6 * wavelengths, 1e6 * depths) plt.xlabel("Wavelength ($\mu m$)") plt.ylabel("Eclipse depth (ppm)") plt.show()
def _ln_like(self, params, transit_calc, eclipse_calc, fit_info, measured_transit_depths, measured_transit_errors, measured_eclipse_depths, measured_eclipse_errors, plot=False): if not fit_info._within_limits(params): return -np.inf params_dict = fit_info._interpret_param_array(params) Rp = params_dict["Rp"] T = params_dict["T"] logZ = params_dict["logZ"] CO_ratio = params_dict["CO_ratio"] scatt_factor = 10.0**params_dict["log_scatt_factor"] scatt_slope = params_dict["scatt_slope"] cloudtop_P = 10.0**params_dict["log_cloudtop_P"] error_multiple = params_dict["error_multiple"] Rs = params_dict["Rs"] Mp = params_dict["Mp"] T_star = params_dict["T_star"] T_spot = params_dict["T_spot"] spot_cov_frac = params_dict["spot_cov_frac"] frac_scale_height = params_dict["frac_scale_height"] number_density = 10.0**params_dict["log_number_density"] part_size = 10.0**params_dict["log_part_size"] ri = params_dict["ri"] """ adding cloud fraction """ cloud_fraction = params_dict["cloud_fraction"] if cloud_fraction < 0. or cloud_fraction > 1.: return -np.inf """ Add offset """ offset = params_dict["offset"] offset2 = params_dict["offset2"] #offset3=params_dict["offset3"] #measured_transit_depths[46:75]+=offset """ Done """ if Rs <= 0 or Mp <= 0: return -np.inf ln_likelihood = 0 try: if measured_transit_depths is not None: if T is None: raise ValueError("Must fit for T if using transit depths") transit_wavelengths, calculated_transit_depths, info_dict = transit_calc.compute_depths( Rs, Mp, Rp, T, logZ, CO_ratio, scattering_factor=scatt_factor, scattering_slope=scatt_slope, cloudtop_pressure=cloudtop_P, T_star=T_star, T_spot=T_spot, spot_cov_frac=spot_cov_frac, frac_scale_height=frac_scale_height, number_density=number_density, part_size=part_size, ri=ri, full_output=True) """ computing clear depth """ if cloud_fraction != 1: xxx, clear_calculated_transit_depths, clear_info_dict = transit_calc.compute_depths( Rs, Mp, Rp, T, logZ, CO_ratio, scattering_factor=scatt_factor, scattering_slope=scatt_slope, cloudtop_pressure=10**8, T_star=T_star, T_spot=T_spot, spot_cov_frac=spot_cov_frac, frac_scale_height=frac_scale_height, number_density=number_density, part_size=part_size, ri=ri, full_output=True) calculated_transit_depths = cloud_fraction * calculated_transit_depths + ( 1. - cloud_fraction) * clear_calculated_transit_depths info_dict['unbinned_depths'] = cloud_fraction * info_dict[ 'unbinned_depths'] + ( 1. - cloud_fraction ) * clear_info_dict['unbinned_depths'] #calculated_transit_depths[46:75]+=offset*1.0e-5 #calculated_transit_depths[:29]+=offset2*1.0e-5 #calculated_transit_depths[29:46]+=offset3*1.0e-5 wfc3_range = np.where((transit_wavelengths > 1.12e-6) & (transit_wavelengths < 1.66e-6)) stis_range = np.where(transit_wavelengths < 9.3e-7) #stis_range1=np.where(transit_wavelengths<5.69e-7) #stis_range2=np.where((transit_wavelengths>5.69e-7) & (transit_wavelengths<9.3e-7)) calculated_transit_depths[wfc3_range] += offset * 1.0e-5 calculated_transit_depths[stis_range] += offset2 * 1.0e-5 #calculated_transit_depths[stis_range1]+=offset2*1.0e-5 #calculated_transit_depths[stis_range2]+=offset3*1.0e-5 wfc3_range = np.where( (info_dict['unbinned_wavelengths'] > 1.12e-6) & (info_dict['unbinned_wavelengths'] < 1.66e-6)) stis_range = np.where( info_dict['unbinned_wavelengths'] < 9.3e-7) #stis_range1=np.where(info_dict['unbinned_wavelengths']<5.69e-7) #stis_range2=np.where((info_dict['unbinned_wavelengths']>5.69e-7) & (info_dict['unbinned_wavelengths']<9.3e-7)) info_dict['unbinned_depths'][wfc3_range] += offset * 1.0e-5 info_dict['unbinned_depths'][stis_range] += offset2 * 1.0e-5 #info_dict['unbinned_depths'][stis_range1]+=offset2*1.0e-5 #info_dict['unbinned_depths'][stis_range2]+=offset3*1.0e-5 residuals = calculated_transit_depths - measured_transit_depths scaled_errors = error_multiple * measured_transit_errors ln_likelihood += -0.5 * np.sum( residuals**2 / scaled_errors**2 + np.log(2 * np.pi * scaled_errors**2)) if plot: plt.figure(1) plt.plot(METRES_TO_UM * info_dict["unbinned_wavelengths"], info_dict["unbinned_depths"], alpha=0.2, color='b', label="Calculated (unbinned)") plt.errorbar(METRES_TO_UM * transit_wavelengths, measured_transit_depths, yerr=measured_transit_errors, fmt='.', color='k', label="Observed") plt.scatter(METRES_TO_UM * transit_wavelengths, calculated_transit_depths, color='r', label="Calculated (binned)") plt.xlabel("Wavelength ($\mu m$)") plt.ylabel("Transit depth") plt.xscale('log') plt.tight_layout() plt.legend() if measured_eclipse_depths is not None: t_p_profile = Profile() t_p_profile.set_from_params_dict(params_dict["profile_type"], params_dict) if np.any(np.isnan(t_p_profile.temperatures)): raise AtmosphereError("Invalid T/P profile") eclipse_wavelengths, calculated_eclipse_depths, info_dict = eclipse_calc.compute_depths( t_p_profile, Rs, Mp, Rp, T_star, logZ, CO_ratio, scattering_factor=scatt_factor, scattering_slope=scatt_slope, cloudtop_pressure=cloudtop_P, T_spot=T_spot, spot_cov_frac=spot_cov_frac, frac_scale_height=frac_scale_height, number_density=number_density, part_size=part_size, ri=ri, full_output=True) residuals = calculated_eclipse_depths - measured_eclipse_depths scaled_errors = error_multiple * measured_eclipse_errors ln_likelihood += -0.5 * np.sum( residuals**2 / scaled_errors**2 + np.log(2 * np.pi * scaled_errors**2)) if plot: plt.figure(2) plt.plot(METRES_TO_UM * info_dict["unbinned_wavelengths"], info_dict["unbinned_eclipse_depths"], alpha=0.2, color='b', label="Calculated (unbinned)") plt.errorbar(METRES_TO_UM * eclipse_wavelengths, measured_eclipse_depths, yerr=measured_eclipse_errors, fmt='.', color='k', label="Observed") plt.scatter(METRES_TO_UM * eclipse_wavelengths, calculated_eclipse_depths, color='r', label="Calculated (binned)") plt.legend() plt.xlabel("Wavelength ($\mu m$)") plt.ylabel("Eclipse depth") plt.xscale('log') plt.tight_layout() plt.legend() except AtmosphereError as e: print(e) return -np.inf self.last_params = params self.last_lnprob = fit_info._ln_prior(params) + ln_likelihood return ln_likelihood
def test_isothermal(self): profile = Profile(num_profile_heights=130) profile.set_isothermal(1300) self.assertEqual(len(profile.pressures), 130) self.assertEqual(len(profile.temperatures), 130) self.assertTrue(np.all(profile.temperatures == 1300))