Coverage for starry/extensions/nexsci/nexsci.py : 92%

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.
4*Created by `Christina Hedges <mailto:christina.l.hedges@nasa.gov>`_ on
5November 1, 2019*
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.
13.. code-block:: python
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()
21"""
22import pandas as pd
23import numpy as np
24import os
25import datetime
26import astropy.units as u
27from glob import glob
28import logging
30from ... import _PACKAGEDIR
31from ... import Secondary, Primary, System, Map
34logger = logging.getLogger("starry.ops")
37def from_nexsci(name, limb_darkening=[0.4, 0.2]):
38 """Extension for retrieving a :py:class:`starry.System` initialized with parameters from NExSci.
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.
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.
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))
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.")
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 )
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
121def _fill_data(df):
122 """Takes a dataframe of nexsci data and fills in NaNs with sane defaults.
124 Parameters
125 ----------
126 df : pandas.DataFrame
127 Dataframe of planet parameters from nexsci
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
137 nan = ~np.isfinite(df.pl_eccen)
138 df.loc[nan, ["pl_eccen"]] = np.zeros(nan.sum())
140 nan = ~np.isfinite(df.pl_orblper)
141 df.loc[nan, ["pl_orblper"]] = np.zeros(nan.sum())
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
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)
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 )
222def _check_data_on_import():
223 """Checks whether nexsci data is "fresh" on import.
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()
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 )
251class DataRetrievalFailure(Exception):
252 """Exception raised if data can't be retrieved from NExSci
253 """
255 pass
258_check_data_on_import()