Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""" 

2Extension for querying NExSci for real planet systems. 

3 

4*Created by `Christina Hedges <mailto:christina.l.hedges@nasa.gov>`_ on 

5November 1, 2019* 

6 

7Example 

8------- 

9Obtain the :py:class:`starry.System` for Kepler-10 and evaluate at 

10some times. Here ``flux`` contains the normalized flux from the 

11Kepler-10 system. 

12 

13.. code-block:: python 

14 

15 from starry.extensions import from_nexsci 

16 import numpy as np 

17 sys = from_nexsci('Kepler-10') 

18 time = np.arange(0, 10, 0.1) 

19 flux = sys.flux(time).eval() 

20 

21""" 

22import pandas as pd 

23import numpy as np 

24import os 

25import datetime 

26import astropy.units as u 

27from glob import glob 

28import logging 

29 

30from ... import _PACKAGEDIR 

31from ... import Secondary, Primary, System, Map 

32 

33 

34logger = logging.getLogger("starry.ops") 

35 

36 

37def from_nexsci(name, limb_darkening=[0.4, 0.2]): 

38 """Extension for retrieving a :py:class:`starry.System` initialized with parameters from NExSci. 

39 

40 Specify the name of the system as a string to ``name`` to create that specific 

41 system. Users can also pass ``'random'`` to obtain a random system. Returns 

42 a :py:class:`starry.System` object. 

43 

44 This extension queries NExSci and then stores the data as a csv file in 

45 ``/extensions/data``. Loading this module will trigger a query of NExSci if 

46 this csv file is more than 7 days out of date. 

47 

48 Args: 

49 name (str): Name of system to retrieve. 

50 e.g. 'K2-43' or 'K2-43b' 

51 limb_darkening (list, optional): The quadratic limb-darkening 

52 parameters to apply to the model. Default is ``[0.4, 0.2]`` 

53 Returns: 

54 An instance of :py:class:`starry.System` using planet parameters \ 

55 from NExSci 

56 """ 

57 df = _get_nexsci_data() 

58 if name is "random": 

59 system_list = ( 

60 df[ 

61 [ 

62 "pl_hostname", 

63 "pl_letter", 

64 "pl_orbper", 

65 "pl_trandep", 

66 "pl_radj", 

67 ] 

68 ] 

69 .dropna()["pl_hostname"] 

70 .unique() 

71 ) 

72 name = np.random.choice(system_list) 

73 logger.warning("Randomly selected {}".format(name)) 

74 df = df[df.pl_hostname == name].reset_index(drop=True) 

75 else: 

76 sname = name.lower().replace("-", "").replace(" ", "") 

77 if np.any([name.endswith(i) for i in "bcdefghijklmnopqrstuvwxyz"]): 

78 df = df[(df.stripped_name + df.pl_letter) == sname].reset_index( 

79 drop=True 

80 ) 

81 else: 

82 df = df[(df.stripped_name) == sname].reset_index(drop=True) 

83 if len(df) == 0: 

84 raise ValueError("{} does not exist at NExSci".format(name)) 

85 

86 m = df.iloc[0].st_mass 

87 if ~np.isfinite(m): 

88 m = 1 

89 logger.warning("Stellar mass is NaN, setting to 1.") 

90 

91 star = Primary(Map(udeg=len(limb_darkening), amp=1), r=1, m=m) 

92 for idx in range(len(limb_darkening)): 

93 star.map[idx + 1] = limb_darkening[idx] 

94 planets = [] 

95 for idx, d in df.iterrows(): 

96 for key in ["pl_orbper", "pl_tranmid", "pl_radj"]: 

97 if ~np.isfinite(d[key]): 

98 logger.warning( 

99 "{} is not set for {}{}, planet will be skipped".format( 

100 key, d.pl_hostname, d.pl_letter 

101 ) 

102 ) 

103 r = np.asarray(d.pl_radj * u.jupiterRad.to(u.solRad)) / np.asarray( 

104 d.st_rad 

105 ) 

106 

107 planet = Secondary( 

108 Map(amp=0), 

109 porb=d.pl_orbper, 

110 t0=d.pl_tranmid, 

111 r=r, 

112 inc=d.pl_orbincl, 

113 ecc=d.pl_eccen, 

114 w=d.pl_orblper, 

115 ) 

116 planets.append(planet) 

117 sys = System(star, *planets) 

118 return sys 

119 

120 

121def _fill_data(df): 

122 """Takes a dataframe of nexsci data and fills in NaNs with sane defaults. 

123 

124 Parameters 

125 ---------- 

126 df : pandas.DataFrame 

127 Dataframe of planet parameters from nexsci 

128 

129 Returns 

130 ------- 

131 clean_df : pandas.DataFrame 

132 Dataframe of planet parameters from nexsci, with NaNs replaced. 

133 """ 

134 nan = ~np.isfinite(df.pl_orbincl) 

135 df.loc[nan, ["pl_orbincl"]] = np.zeros(nan.sum()) + 90 

136 

137 nan = ~np.isfinite(df.pl_eccen) 

138 df.loc[nan, ["pl_eccen"]] = np.zeros(nan.sum()) 

139 

140 nan = ~np.isfinite(df.pl_orblper) 

141 df.loc[nan, ["pl_orblper"]] = np.zeros(nan.sum()) 

142 

143 # Fill in missing trandep 

144 nan = ~np.isfinite(df.pl_trandep) 

145 trandep = ( 

146 np.asarray(df.pl_radj * u.jupiterRad.to(u.solRad)) 

147 / np.asarray(df.st_rad) 

148 ) ** 2 

149 df.loc[nan, ["pl_trandep"]] = trandep[nan] 

150 return df 

151 

152 

153def _retrieve_online_data(guess_masses=False): 

154 """Queries nexsci database and returns a dataframe of planet data. 

155 """ 

156 NEXSCI_API = "http://exoplanetarchive.ipac.caltech.edu/cgi-bin/nstedAPI/nph-nstedAPI" 

157 try: 

158 # Planet table 

159 planets = pd.read_csv( 

160 NEXSCI_API + "?table=planets&select=pl_hostname,pl_letter," 

161 "pl_disc,ra,dec,pl_trandep,pl_tranmid,pl_tranmiderr1," 

162 "pl_tranmiderr2,pl_tranflag,pl_trandur,pl_pnum,pl_k2flag," 

163 "pl_kepflag,pl_facility,pl_orbincl,pl_orbinclerr1,pl_orbinclerr2," 

164 "pl_orblper,st_mass,st_masserr1,st_masserr2,st_rad,st_raderr1," 

165 "st_raderr2,st_teff,st_tefferr1,st_tefferr2,st_optmag,st_j,st_h", 

166 comment="#", 

167 ) 

168 # Composite table 

169 composite = pd.read_csv( 

170 NEXSCI_API + "?table=compositepars&select=fpl_hostname,fpl_letter," 

171 "fpl_smax,fpl_smaxerr1,fpl_smaxerr2,fpl_radj,fpl_radjerr1," 

172 "fpl_radjerr2,fpl_bmassj,fpl_bmassjerr1,fpl_bmassjerr2," 

173 "fpl_bmassprov,fpl_eqt,fpl_orbper,fpl_orbpererr1,fpl_orbpererr2," 

174 "fpl_eccen,fpl_eccenerr1,fpl_eccenerr2,fst_spt", 

175 comment="#", 

176 ) 

177 # Rename columns 

178 composite.columns = [ 

179 "pl_hostname", 

180 "pl_letter", 

181 "pl_orbsmax", 

182 "pl_orbsmaxerr1", 

183 "pl_orbsmaxerr2", 

184 "pl_radj", 

185 "pl_radjerr1", 

186 "pl_radjerr2", 

187 "pl_bmassj", 

188 "pl_bmassjerr1", 

189 "pl_bmassjerr2", 

190 "pl_bmassprov", 

191 "pl_eqt", 

192 "pl_orbper", 

193 "pl_orbpererr1", 

194 "pl_orbpererr2", 

195 "pl_eccen", 

196 "pl_eccenerr1", 

197 "pl_eccenerr2", 

198 "st_spt", 

199 ] 

200 except: 

201 raise DataRetrievalFailure( 

202 "Couldn't obtain data from NEXSCI. Do you have an internet connection?" 

203 ) 

204 df = pd.merge( 

205 left=planets, 

206 right=composite, 

207 how="left", 

208 left_on=["pl_hostname", "pl_letter"], 

209 right_on=["pl_hostname", "pl_letter"], 

210 ) 

211 df = _fill_data(df) 

212 

213 df["stripped_name"] = np.asarray( 

214 [n.lower().replace("-", "").replace(" ", "") for n in df.pl_hostname] 

215 ) 

216 df[df.pl_tranflag == 1].to_csv( 

217 "{}/extensions/nexsci/data/planets.csv".format(_PACKAGEDIR), 

218 index=False, 

219 ) 

220 

221 

222def _check_data_on_import(): 

223 """Checks whether nexsci data is "fresh" on import. 

224 

225 Data is considered out of date if a week has passed since downloading it. 

226 """ 

227 fname = glob("{}/extensions/nexsci/data/planets.csv".format(_PACKAGEDIR)) 

228 if len(fname) == 0: 

229 _retrieve_online_data() 

230 fname = glob( 

231 "{}/extensions/nexsci/data/planets.csv".format(_PACKAGEDIR) 

232 ) 

233 st = os.stat(fname[0]) 

234 mtime = st.st_mtime 

235 # If database is out of date, get it again. 

236 if datetime.datetime.now() - datetime.datetime.fromtimestamp( 

237 mtime 

238 ) > datetime.timedelta(days=7): 

239 logger.warning("Database out of date. Redownloading...") 

240 _retrieve_online_data() 

241 

242 

243def _get_nexsci_data(): 

244 """Returns pandas dataframe of all exoplanet data from nexsci 

245 """ 

246 return pd.read_csv( 

247 "{}/extensions/nexsci/data/planets.csv".format(_PACKAGEDIR) 

248 ) 

249 

250 

251class DataRetrievalFailure(Exception): 

252 """Exception raised if data can't be retrieved from NExSci 

253 """ 

254 

255 pass 

256 

257 

258_check_data_on_import()