arvi 0.1.13__py3-none-any.whl → 0.1.15__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- arvi/__init__.py +21 -6
- arvi/berv.py +437 -0
- arvi/binning.py +14 -9
- arvi/dace_wrapper.py +2 -2
- arvi/gaia_wrapper.py +1 -1
- arvi/headers.py +47 -0
- arvi/plots.py +158 -90
- arvi/programs.py +4 -2
- arvi/reports.py +4 -3
- arvi/simbad_wrapper.py +3 -2
- arvi/stats.py +2 -3
- arvi/stellar.py +89 -0
- arvi/timeseries.py +104 -37
- arvi/translations.py +2 -0
- arvi/utils.py +13 -0
- {arvi-0.1.13.dist-info → arvi-0.1.15.dist-info}/METADATA +2 -4
- arvi-0.1.15.dist-info/RECORD +35 -0
- {arvi-0.1.13.dist-info → arvi-0.1.15.dist-info}/WHEEL +1 -1
- arvi-0.1.13.dist-info/RECORD +0 -32
- {arvi-0.1.13.dist-info → arvi-0.1.15.dist-info}/LICENSE +0 -0
- {arvi-0.1.13.dist-info → arvi-0.1.15.dist-info}/top_level.txt +0 -0
arvi/plots.py
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
from functools import partial, partialmethod
|
|
1
|
+
from functools import partialmethod, wraps
|
|
3
2
|
from itertools import cycle
|
|
4
3
|
|
|
5
|
-
import matplotlib.collections
|
|
6
4
|
import numpy as np
|
|
7
|
-
import matplotlib
|
|
8
|
-
import matplotlib.pyplot as plt
|
|
9
|
-
import mplcursors
|
|
10
5
|
|
|
11
6
|
from astropy.timeseries import LombScargle
|
|
12
7
|
|
|
@@ -14,11 +9,24 @@ from .setup_logger import logger
|
|
|
14
9
|
from . import config
|
|
15
10
|
from .stats import wmean
|
|
16
11
|
|
|
12
|
+
from .utils import lazy_import
|
|
13
|
+
plt = lazy_import('matplotlib.pyplot')
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def plot_fast(func):
|
|
17
|
+
@wraps(func)
|
|
18
|
+
def wrapper(*args, **kwargs):
|
|
19
|
+
with plt.style.context('fast'):
|
|
20
|
+
return func(*args, **kwargs)
|
|
21
|
+
return wrapper
|
|
22
|
+
|
|
23
|
+
|
|
17
24
|
|
|
18
25
|
class BlittedCursor:
|
|
19
26
|
""" A cross-hair cursor using blitting for faster redraw. """
|
|
20
27
|
def __init__(self, axes, vertical=True, horizontal=True, show_text=None,
|
|
21
28
|
transforms_x=None, transforms_y=None):
|
|
29
|
+
import matplotlib # delay import
|
|
22
30
|
if isinstance(axes, matplotlib.axes.Axes):
|
|
23
31
|
axes = [axes]
|
|
24
32
|
self.axes = axes
|
|
@@ -102,10 +110,31 @@ class BlittedCursor:
|
|
|
102
110
|
ax.draw_artist(self.text)
|
|
103
111
|
ax.figure.canvas.blit(ax.bbox)
|
|
104
112
|
|
|
113
|
+
def clickable_legend(fig, ax, leg):
|
|
114
|
+
from matplotlib.text import Text
|
|
115
|
+
handles, labels = ax.get_legend_handles_labels()
|
|
116
|
+
for text in leg.get_texts():
|
|
117
|
+
text.set_picker(True)
|
|
118
|
+
|
|
119
|
+
def on_pick_legend(event):
|
|
120
|
+
artist = event.artist
|
|
121
|
+
if isinstance(artist, Text):
|
|
122
|
+
try:
|
|
123
|
+
h = handles[labels.index(artist.get_text())]
|
|
124
|
+
alpha_text = {None:0.2, 1.0: 0.2, 0.2:1.0}[artist.get_alpha()]
|
|
125
|
+
alpha_point = {None: 0.0, 1.0: 0.0, 0.2: 1.0}[artist.get_alpha()]
|
|
126
|
+
h[0].set_alpha(alpha_point)
|
|
127
|
+
h[2][0].set_alpha(alpha_point)
|
|
128
|
+
artist.set_alpha(alpha_text)
|
|
129
|
+
fig.canvas.draw()
|
|
130
|
+
except ValueError:
|
|
131
|
+
pass
|
|
132
|
+
return on_pick_legend
|
|
105
133
|
|
|
134
|
+
# @plot_fast
|
|
106
135
|
def plot(self, ax=None, show_masked=False, instrument=None, time_offset=0,
|
|
107
|
-
remove_50000=False, tooltips=False,
|
|
108
|
-
versus_n=False, show_histogram=False, bw=False, **kwargs):
|
|
136
|
+
remove_50000=False, tooltips=False, show_legend=True, label=None,
|
|
137
|
+
N_in_label=False, versus_n=False, show_histogram=False, bw=False, **kwargs):
|
|
109
138
|
""" Plot the RVs
|
|
110
139
|
|
|
111
140
|
Args:
|
|
@@ -121,6 +150,8 @@ def plot(self, ax=None, show_masked=False, instrument=None, time_offset=0,
|
|
|
121
150
|
Whether to subtract 50000 from time. Defaults to False.
|
|
122
151
|
tooltips (bool, optional):
|
|
123
152
|
Show information upon clicking a point. Defaults to True.
|
|
153
|
+
show_legend (bool, optional):
|
|
154
|
+
Show legend. Defaults to True.
|
|
124
155
|
N_in_label (bool, optional):
|
|
125
156
|
Show number of observations in legend. Defaults to False.
|
|
126
157
|
versus_n (bool, optional):
|
|
@@ -145,12 +176,14 @@ def plot(self, ax=None, show_masked=False, instrument=None, time_offset=0,
|
|
|
145
176
|
fig, (ax, axh) = plt.subplots(1, 2, constrained_layout=True, gridspec_kw={'width_ratios': [3, 1]})
|
|
146
177
|
else:
|
|
147
178
|
fig, ax = plt.subplots(1, 1, constrained_layout=True)
|
|
179
|
+
elif ax == -1:
|
|
180
|
+
ax = plt.gca()
|
|
181
|
+
fig = ax.figure
|
|
148
182
|
else:
|
|
149
183
|
if show_histogram:
|
|
150
184
|
ax, axh = ax
|
|
151
185
|
fig = ax.figure
|
|
152
186
|
|
|
153
|
-
|
|
154
187
|
kwargs.setdefault('ls', '')
|
|
155
188
|
kwargs.setdefault('capsize', 0)
|
|
156
189
|
kwargs.setdefault('ms', 4)
|
|
@@ -172,7 +205,6 @@ def plot(self, ax=None, show_masked=False, instrument=None, time_offset=0,
|
|
|
172
205
|
except AttributeError:
|
|
173
206
|
zorders = cycle([1] * len(instruments))
|
|
174
207
|
|
|
175
|
-
cursors = {}
|
|
176
208
|
containers = {}
|
|
177
209
|
for _i, inst in enumerate(instruments):
|
|
178
210
|
s = self if self._child else getattr(self, inst)
|
|
@@ -207,22 +239,6 @@ def plot(self, ax=None, show_masked=False, instrument=None, time_offset=0,
|
|
|
207
239
|
hlabel = f'{s.mvrad.std():.2f} {self.units}'
|
|
208
240
|
axh.hist(s.mvrad, **kw, label=hlabel)
|
|
209
241
|
|
|
210
|
-
# cursors[inst] = crsr = mplcursors.cursor(container, multiple=False)
|
|
211
|
-
# @crsr.connect("add")
|
|
212
|
-
# def _(sel):
|
|
213
|
-
# inst = sel.artist.get_label()
|
|
214
|
-
# _s = getattr(self, inst)
|
|
215
|
-
# vrad, svrad = _s.vrad[sel.index], _s.svrad[sel.index]
|
|
216
|
-
# sel.annotation.get_bbox_patch().set(fc="white")
|
|
217
|
-
# text = f'{inst}\n'
|
|
218
|
-
# text += f'BJD: {sel.target[0]:9.5f}\n'
|
|
219
|
-
# text += f'RV: {vrad:.3f} ± {svrad:.3f}'
|
|
220
|
-
# # if fig.canvas.manager.toolmanager.get_tool('infotool').toggled:
|
|
221
|
-
# # text += '\n\n'
|
|
222
|
-
# # text += f'date: {_s.date_night[sel.index]}\n'
|
|
223
|
-
# # text += f'mask: {_s.ccf_mask[sel.index]}'
|
|
224
|
-
# sel.annotation.set_text(text)
|
|
225
|
-
|
|
226
242
|
if show_masked:
|
|
227
243
|
if versus_n:
|
|
228
244
|
pass
|
|
@@ -239,62 +255,104 @@ def plot(self, ax=None, show_masked=False, instrument=None, time_offset=0,
|
|
|
239
255
|
ax.errorbar(self.time[~self.mask] - time_offset, self.vrad[~self.mask], self.svrad[~self.mask],
|
|
240
256
|
label='masked', fmt='x', ms=10, color='k', zorder=-2)
|
|
241
257
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
def on_pick_legend(event):
|
|
248
|
-
artist = event.artist
|
|
249
|
-
if isinstance(artist, matplotlib.text.Text):
|
|
250
|
-
try:
|
|
251
|
-
h = handles[labels.index(artist.get_text())]
|
|
252
|
-
alpha_text = {None:0.2, 1.0: 0.2, 0.2:1.0}[artist.get_alpha()]
|
|
253
|
-
alpha_point = {None: 0.0, 1.0: 0.0, 0.2: 1.0}[artist.get_alpha()]
|
|
254
|
-
h[0].set_alpha(alpha_point)
|
|
255
|
-
h[2][0].set_alpha(alpha_point)
|
|
256
|
-
artist.set_alpha(alpha_text)
|
|
257
|
-
fig.canvas.draw()
|
|
258
|
-
except ValueError:
|
|
259
|
-
pass
|
|
260
|
-
plt.connect('pick_event', on_pick_legend)
|
|
258
|
+
if show_legend:
|
|
259
|
+
leg = ax.legend()
|
|
260
|
+
on_pick_legend = clickable_legend(fig, ax, leg)
|
|
261
|
+
plt.connect('pick_event', on_pick_legend)
|
|
261
262
|
|
|
262
263
|
if tooltips:
|
|
264
|
+
from matplotlib.lines import Line2D
|
|
265
|
+
from matplotlib.collections import LineCollection
|
|
263
266
|
annotations = []
|
|
264
|
-
|
|
265
|
-
|
|
267
|
+
axes_artists = []
|
|
268
|
+
selected_inds = []
|
|
269
|
+
selected_insts = []
|
|
270
|
+
|
|
271
|
+
def _cleanup():
|
|
266
272
|
for text in annotations:
|
|
267
273
|
text.remove()
|
|
268
274
|
annotations.remove(text)
|
|
275
|
+
for art in axes_artists:
|
|
276
|
+
art.remove()
|
|
277
|
+
axes_artists.remove(art)
|
|
278
|
+
for ind in selected_inds:
|
|
279
|
+
selected_inds.remove(ind)
|
|
280
|
+
|
|
281
|
+
def on_press(event):
|
|
282
|
+
# print('press', event.key)
|
|
283
|
+
if event.key in ('r',):
|
|
284
|
+
for i, inst in zip(selected_inds, selected_insts):
|
|
285
|
+
i = self._index_from_instrument_index(i, inst)
|
|
286
|
+
self.remove_point(i)
|
|
287
|
+
_cleanup()
|
|
288
|
+
fig.canvas.draw_idle()
|
|
289
|
+
if event.key == 'escape':
|
|
290
|
+
_cleanup()
|
|
291
|
+
kp_cid = None
|
|
292
|
+
for k, _f in fig.canvas.callbacks.callbacks['key_press_event'].items():
|
|
293
|
+
if _f._obj == on_press:
|
|
294
|
+
kp_cid = k
|
|
295
|
+
if kp_cid is not None:
|
|
296
|
+
fig.canvas.callbacks.disconnect(k)
|
|
297
|
+
fig.canvas.draw_idle()
|
|
269
298
|
|
|
299
|
+
def on_pick_point(event):
|
|
300
|
+
_cleanup()
|
|
270
301
|
artist = event.artist
|
|
271
|
-
if isinstance(artist, (
|
|
272
|
-
print(event.ind, artist)
|
|
273
|
-
if isinstance(artist,
|
|
302
|
+
if isinstance(artist, (Line2D, LineCollection)):
|
|
303
|
+
# print(event.ind, artist)
|
|
304
|
+
if isinstance(artist, Line2D):
|
|
305
|
+
ind = event.ind
|
|
274
306
|
matching_instrument = [k for k, v in containers.items() if artist in v]
|
|
275
|
-
print(matching_instrument)
|
|
276
307
|
if len(matching_instrument) == 0:
|
|
277
308
|
return
|
|
278
309
|
inst = matching_instrument[0]
|
|
310
|
+
|
|
279
311
|
_s = getattr(self, inst)
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
312
|
+
|
|
313
|
+
if event.ind.size > 1:
|
|
314
|
+
mint, maxt = _s.mtime[ind].min(), _s.mtime[ind].max()
|
|
315
|
+
miny, maxy = _s.mvrad[ind].min(), _s.mvrad[ind].max()
|
|
316
|
+
axins = ax.inset_axes([0.1, 0.5, 0.5, 0.4],
|
|
317
|
+
xlim=(mint - 0.1 * (maxt - mint), maxt + 0.1 * (maxt - mint)),
|
|
318
|
+
ylim=(miny - 0.1 * (maxy - miny), maxy + 0.1 * (maxy - miny)),
|
|
319
|
+
xticklabels=[], yticklabels=[]
|
|
320
|
+
)
|
|
321
|
+
axins.errorbar(_s.mtime[ind], _s.mvrad[ind], _s.msvrad[ind], fmt='o', ms=3,
|
|
322
|
+
color=artist.get_color())
|
|
323
|
+
axins.margins(x=0.5)
|
|
324
|
+
axins.autoscale_view()
|
|
325
|
+
rectangle_patch, connector_lines = ax.indicate_inset_zoom(axins, edgecolor="black")
|
|
326
|
+
axes_artists.append(axins)
|
|
327
|
+
axes_artists.append(rectangle_patch)
|
|
328
|
+
for line in connector_lines:
|
|
329
|
+
axes_artists.append(line)
|
|
330
|
+
else:
|
|
331
|
+
ind = event.ind[0]
|
|
332
|
+
selected_inds.append(ind)
|
|
333
|
+
selected_insts.append(inst)
|
|
334
|
+
# print(_s.mtime[ind], _s.mvrad[ind], _s.msvrad[ind])
|
|
335
|
+
|
|
336
|
+
text = f'{inst} ({ind})\n'
|
|
337
|
+
text += f'{_s.mtime[ind]:9.5f}\n'
|
|
338
|
+
text += f'RV: {_s.mvrad[ind]:.1f} ± {_s.msvrad[ind]:.1f}'
|
|
339
|
+
|
|
340
|
+
annotations.append(
|
|
341
|
+
ax.annotate(text, (_s.mtime[ind], _s.mvrad[ind]), xycoords='data',
|
|
342
|
+
xytext=(5, 10), textcoords='offset points', fontsize=9,
|
|
343
|
+
bbox={'boxstyle': 'round', 'fc': 'w'}, arrowprops=dict(arrowstyle="-"))
|
|
344
|
+
)
|
|
292
345
|
# ax.annotate(f'{inst}', (0.5, 0.5), xycoords=artist, ha='center', va='center')
|
|
293
346
|
fig.canvas.draw()
|
|
294
347
|
# print(event.ind, artist.get_label())
|
|
348
|
+
|
|
349
|
+
_ = fig.canvas.mpl_connect('key_press_event', on_press)
|
|
350
|
+
|
|
295
351
|
plt.connect('pick_event', on_pick_point)
|
|
296
352
|
|
|
297
353
|
|
|
354
|
+
|
|
355
|
+
|
|
298
356
|
if show_histogram:
|
|
299
357
|
axh.legend()
|
|
300
358
|
|
|
@@ -309,25 +367,23 @@ def plot(self, ax=None, show_masked=False, instrument=None, time_offset=0,
|
|
|
309
367
|
else:
|
|
310
368
|
ax.set_xlabel('BJD - 2400000 [days]')
|
|
311
369
|
|
|
312
|
-
from matplotlib.backend_tools import ToolBase, ToolToggleBase
|
|
313
|
-
tm = fig.canvas.manager.toolmanager
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
except
|
|
328
|
-
|
|
329
|
-
except UnidentifiedImageError:
|
|
330
|
-
pass
|
|
370
|
+
# from matplotlib.backend_tools import ToolBase, ToolToggleBase
|
|
371
|
+
# tm = fig.canvas.manager.toolmanager
|
|
372
|
+
# class InfoTool(ToolToggleBase):
|
|
373
|
+
# description = "Show extra information about each observation"
|
|
374
|
+
# image = os.path.abspath(os.path.join(os.path.dirname(__file__), 'data', 'info.svg'))
|
|
375
|
+
# # def enable(self, *args, **kwargs):
|
|
376
|
+
# # self.figure.add_axes([1, 0, 0.3, 1])
|
|
377
|
+
# # self.figure.canvas.draw_idle()
|
|
378
|
+
# from PIL import UnidentifiedImageError
|
|
379
|
+
# try:
|
|
380
|
+
# tm.add_tool("infotool", InfoTool)
|
|
381
|
+
# fig.canvas.manager.toolbar.add_tool(tm.get_tool("infotool"), "toolgroup")
|
|
382
|
+
# raise UnidentifiedImageError
|
|
383
|
+
# except AttributeError:
|
|
384
|
+
# pass
|
|
385
|
+
# except UnidentifiedImageError:
|
|
386
|
+
# pass
|
|
331
387
|
|
|
332
388
|
|
|
333
389
|
if config.return_self:
|
|
@@ -339,8 +395,9 @@ def plot(self, ax=None, show_masked=False, instrument=None, time_offset=0,
|
|
|
339
395
|
return fig, ax
|
|
340
396
|
|
|
341
397
|
|
|
398
|
+
@plot_fast
|
|
342
399
|
def plot_quantity(self, quantity, ax=None, show_masked=False, instrument=None,
|
|
343
|
-
time_offset=0, remove_50000=False, tooltips=False,
|
|
400
|
+
time_offset=0, remove_50000=False, tooltips=False, show_legend=True,
|
|
344
401
|
N_in_label=False, **kwargs):
|
|
345
402
|
if self.N == 0:
|
|
346
403
|
if self.verbose:
|
|
@@ -399,16 +456,21 @@ def plot_quantity(self, quantity, ax=None, show_masked=False, instrument=None,
|
|
|
399
456
|
getattr(self, quantity + '_err')[~self.mask],
|
|
400
457
|
label='masked', fmt='x', ms=10, color='k', zorder=-2)
|
|
401
458
|
|
|
402
|
-
|
|
459
|
+
if show_legend:
|
|
460
|
+
leg = ax.legend()
|
|
461
|
+
on_pick_legend = clickable_legend(fig, ax, leg)
|
|
462
|
+
plt.connect('pick_event', on_pick_legend)
|
|
463
|
+
|
|
403
464
|
ax.minorticks_on()
|
|
404
465
|
|
|
405
466
|
ylabel = {
|
|
467
|
+
quantity: quantity,
|
|
406
468
|
'fwhm': f'FWHM [{self.units}]',
|
|
407
469
|
'bispan': f'BIS [{self.units}]',
|
|
408
470
|
'rhk': r"$\log$ R'$_{HK}$",
|
|
409
|
-
'berv':
|
|
471
|
+
'berv': 'BERV [km/s]',
|
|
410
472
|
}
|
|
411
|
-
ax.set_ylabel(ylabel[quantity])
|
|
473
|
+
ax.set_ylabel(ylabel[quantity.lower()])
|
|
412
474
|
|
|
413
475
|
if remove_50000:
|
|
414
476
|
ax.set_xlabel('BJD - 2450000 [days]')
|
|
@@ -427,6 +489,7 @@ plot_rhk = partialmethod(plot_quantity, quantity='rhk')
|
|
|
427
489
|
plot_berv = partialmethod(plot_quantity, quantity='berv')
|
|
428
490
|
|
|
429
491
|
|
|
492
|
+
@plot_fast
|
|
430
493
|
def gls(self, ax=None, label=None, fap=True, instrument=None, adjust_means=config.adjust_means_gls,
|
|
431
494
|
picker=True, **kwargs):
|
|
432
495
|
"""
|
|
@@ -470,7 +533,7 @@ def gls(self, ax=None, label=None, fap=True, instrument=None, adjust_means=confi
|
|
|
470
533
|
if self.verbose:
|
|
471
534
|
logger.info(f'calculating periodogram for instrument {instrument}')
|
|
472
535
|
|
|
473
|
-
if adjust_means:
|
|
536
|
+
if adjust_means and not self._child:
|
|
474
537
|
if self.verbose:
|
|
475
538
|
logger.info('adjusting instrument means before gls')
|
|
476
539
|
means = np.empty_like(y)
|
|
@@ -485,7 +548,7 @@ def gls(self, ax=None, label=None, fap=True, instrument=None, adjust_means=confi
|
|
|
485
548
|
y = self.vrad[self.mask].copy()
|
|
486
549
|
e = self.svrad[self.mask].copy()
|
|
487
550
|
|
|
488
|
-
if adjust_means:
|
|
551
|
+
if adjust_means and not self._child:
|
|
489
552
|
if self.verbose:
|
|
490
553
|
logger.info('adjusting instrument means before gls')
|
|
491
554
|
means = np.empty_like(y)
|
|
@@ -528,6 +591,7 @@ def gls(self, ax=None, label=None, fap=True, instrument=None, adjust_means=confi
|
|
|
528
591
|
ax.legend()
|
|
529
592
|
|
|
530
593
|
if ax.get_legend() is not None:
|
|
594
|
+
from matplotlib.text import Text
|
|
531
595
|
leg = ax.get_legend()
|
|
532
596
|
for text in leg.get_texts():
|
|
533
597
|
text.set_picker(True)
|
|
@@ -535,7 +599,7 @@ def gls(self, ax=None, label=None, fap=True, instrument=None, adjust_means=confi
|
|
|
535
599
|
def on_pick_legend(event):
|
|
536
600
|
handles, labels = ax.get_legend_handles_labels()
|
|
537
601
|
artist = event.artist
|
|
538
|
-
if isinstance(artist,
|
|
602
|
+
if isinstance(artist, Text):
|
|
539
603
|
# print('handles:', handles)
|
|
540
604
|
# print('labels:', labels)
|
|
541
605
|
# print(artist.get_text())
|
|
@@ -559,6 +623,7 @@ def gls(self, ax=None, label=None, fap=True, instrument=None, adjust_means=confi
|
|
|
559
623
|
return fig, ax
|
|
560
624
|
|
|
561
625
|
|
|
626
|
+
@plot_fast
|
|
562
627
|
def gls_quantity(self, quantity, ax=None, fap=True, picker=True):
|
|
563
628
|
if not hasattr(self, quantity):
|
|
564
629
|
logger.error(f"cannot find '{quantity}' attribute")
|
|
@@ -571,7 +636,10 @@ def gls_quantity(self, quantity, ax=None, fap=True, picker=True):
|
|
|
571
636
|
|
|
572
637
|
t = self.mtime
|
|
573
638
|
y = getattr(self, quantity)[self.mask]
|
|
574
|
-
|
|
639
|
+
try:
|
|
640
|
+
ye = getattr(self, quantity + '_err')[self.mask]
|
|
641
|
+
except AttributeError:
|
|
642
|
+
ye = None
|
|
575
643
|
|
|
576
644
|
if np.isnan(y).any():
|
|
577
645
|
if self.verbose:
|
arvi/programs.py
CHANGED
|
@@ -20,12 +20,14 @@ def get_star(star, instrument=None):
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class LazyRV:
|
|
23
|
-
def __init__(self, stars: list, instrument: str = None
|
|
23
|
+
def __init__(self, stars: list, instrument: str = None,
|
|
24
|
+
_parallel_limit=10):
|
|
24
25
|
self.stars = stars
|
|
25
26
|
if isinstance(self.stars, str):
|
|
26
27
|
self.stars = [self.stars]
|
|
27
28
|
self.instrument = instrument
|
|
28
29
|
self._saved = None
|
|
30
|
+
self._parallel_limit = _parallel_limit
|
|
29
31
|
|
|
30
32
|
@property
|
|
31
33
|
def N(self):
|
|
@@ -35,7 +37,7 @@ class LazyRV:
|
|
|
35
37
|
return f"RV({self.N} stars)"
|
|
36
38
|
|
|
37
39
|
def _get(self):
|
|
38
|
-
if self.N >
|
|
40
|
+
if self.N > self._parallel_limit:
|
|
39
41
|
# logger.info('Querying DACE...')
|
|
40
42
|
_get_star = partial(get_star, instrument=self.instrument)
|
|
41
43
|
with multiprocessing.Pool() as pool:
|
arvi/reports.py
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
from functools import partial
|
|
2
2
|
import numpy as np
|
|
3
3
|
from astropy.timeseries import LombScargle
|
|
4
|
-
import matplotlib.pyplot as plt
|
|
5
|
-
import matplotlib.gridspec as gridspec
|
|
6
|
-
from matplotlib.backends.backend_pdf import PdfPages
|
|
7
4
|
|
|
8
5
|
from .setup_logger import logger
|
|
9
6
|
|
|
@@ -31,6 +28,10 @@ def sine_picker(event, self, fig, ax, ax1):
|
|
|
31
28
|
|
|
32
29
|
|
|
33
30
|
def report(self, save=None):
|
|
31
|
+
import matplotlib.pyplot as plt
|
|
32
|
+
import matplotlib.gridspec as gridspec
|
|
33
|
+
from matplotlib.backends.backend_pdf import PdfPages
|
|
34
|
+
|
|
34
35
|
# size = A4
|
|
35
36
|
size = 8.27, 11.69
|
|
36
37
|
fig = plt.figure(figsize=size, constrained_layout=True)
|
arvi/simbad_wrapper.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import requests
|
|
3
3
|
|
|
4
|
-
from astropy.coordinates import SkyCoord
|
|
5
4
|
import pysweetcat
|
|
6
5
|
|
|
7
6
|
DATA_PATH = os.path.dirname(__file__)
|
|
@@ -44,7 +43,7 @@ def run_query(query):
|
|
|
44
43
|
url = 'http://simbad.u-strasbg.fr/simbad/sim-tap/sync'
|
|
45
44
|
data = dict(query=query, request='doQuery', lang='ADQL', format='text/plain', phase='run')
|
|
46
45
|
try:
|
|
47
|
-
response = requests.post(url, data=data, timeout=
|
|
46
|
+
response = requests.post(url, data=data, timeout=5)
|
|
48
47
|
except requests.ReadTimeout as err:
|
|
49
48
|
raise IndexError(err)
|
|
50
49
|
except requests.ConnectionError as err:
|
|
@@ -97,6 +96,8 @@ class simbad:
|
|
|
97
96
|
Args:
|
|
98
97
|
star (str): The name of the star to query simbad
|
|
99
98
|
"""
|
|
99
|
+
from astropy.coordinates import SkyCoord
|
|
100
|
+
|
|
100
101
|
self.star = star
|
|
101
102
|
|
|
102
103
|
if 'kobe' in self.star.lower():
|
arvi/stats.py
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import numpy as np
|
|
2
|
-
from scipy.stats import median_abs_deviation
|
|
3
|
-
from scipy.stats._stats_py import SigmaclipResult
|
|
4
|
-
|
|
5
2
|
|
|
6
3
|
def wmean(a, e):
|
|
7
4
|
"""Weighted mean of array `a`, with uncertainties given by `e`.
|
|
@@ -69,6 +66,8 @@ def sigmaclip_median(a, low=4.0, high=4.0):
|
|
|
69
66
|
- `lower`: Lower clipping limit
|
|
70
67
|
- `upper`: Upper clipping limit
|
|
71
68
|
"""
|
|
69
|
+
from scipy.stats import median_abs_deviation
|
|
70
|
+
from scipy.stats._stats_py import SigmaclipResult
|
|
72
71
|
c = np.asarray(a).ravel()
|
|
73
72
|
delta = 1
|
|
74
73
|
while delta:
|
arvi/stellar.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
class prot_age_result:
|
|
4
|
+
prot_n84: float
|
|
5
|
+
prot_n84_err: float
|
|
6
|
+
prot_m08: float
|
|
7
|
+
prot_m08_err: float
|
|
8
|
+
age_m08: float
|
|
9
|
+
age_m08_err: float
|
|
10
|
+
def __init__(self):
|
|
11
|
+
pass
|
|
12
|
+
def __repr__(self):
|
|
13
|
+
s = f'{self.prot_n84=:.2f} ± {self.prot_n84_err:.2f}, '
|
|
14
|
+
s += f'{self.prot_m08=:.2f} ± {self.prot_m08_err:.2f}, '
|
|
15
|
+
s += f'{self.age_m08=:.2f} ± {self.age_m08_err:.2f}'
|
|
16
|
+
return s.replace('self.', '')
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def calc_prot_age(self, bv=None):
|
|
20
|
+
"""
|
|
21
|
+
Calculate rotation period and age from logR'HK activity level, based on the
|
|
22
|
+
empirical relations of Noyes et al. (1984) and Mamajek & Hillenbrand (2008).
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
self (`arvi.RV`):
|
|
26
|
+
RV object
|
|
27
|
+
bv (float, optional):
|
|
28
|
+
B-V colour. If None, use value from Simbad
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
An object with the following attributes:
|
|
32
|
+
|
|
33
|
+
prot_n84 (float, array):
|
|
34
|
+
Chromospheric rotational period via Noyes et al. (1984)
|
|
35
|
+
prot_n84_err (float, array):
|
|
36
|
+
Error on 'prot_n84'
|
|
37
|
+
prot_m08 (float, array):
|
|
38
|
+
Chromospheric rotational period via Mamajek & Hillenbrand (2008)
|
|
39
|
+
prot_m08_err (float, array):
|
|
40
|
+
Error on 'prot_m08'
|
|
41
|
+
age_m08 (float, array):
|
|
42
|
+
Gyrochronology age via Mamajek & Hillenbrand (2008)
|
|
43
|
+
age_m08_err (float, array):
|
|
44
|
+
Error on 'age_m08'
|
|
45
|
+
|
|
46
|
+
Range of logR'HK-Prot relation: -5.5 < logR'HK < -4.3
|
|
47
|
+
Range of Mamajek & Hillenbrand (2008) relation for ages: 0.5 < B-V < 0.9
|
|
48
|
+
"""
|
|
49
|
+
log_rhk = np.nanmean(self.rhk[self.mask])
|
|
50
|
+
if bv is None:
|
|
51
|
+
bv = self.simbad.B - self.simbad.V
|
|
52
|
+
|
|
53
|
+
# Calculate chromospheric Prot:
|
|
54
|
+
if np.any(log_rhk < -4.3) & np.any(log_rhk > -5.5):
|
|
55
|
+
if bv < 1:
|
|
56
|
+
tau = 1.362 - 0.166*(1-bv) + 0.025*(1-bv)**2 - 5.323*(1-bv)**3
|
|
57
|
+
else:
|
|
58
|
+
tau = 1.362 - 0.14*(1-bv)
|
|
59
|
+
|
|
60
|
+
prot_n84 = 0.324 - 0.400*(5 + log_rhk) - 0.283*(5 + log_rhk)**2 - 1.325*(5 + log_rhk)**3 + tau
|
|
61
|
+
prot_n84 = 10**prot_n84
|
|
62
|
+
prot_n84_err = np.log(10)*0.08*prot_n84
|
|
63
|
+
|
|
64
|
+
prot_m08 = (0.808 - 2.966*(log_rhk + 4.52))*10**tau
|
|
65
|
+
prot_m08_err = 4.4*bv*1.7 - 1.7
|
|
66
|
+
else:
|
|
67
|
+
prot_n84 = np.nan
|
|
68
|
+
prot_n84_err = np.nan
|
|
69
|
+
prot_m08 = np.nan
|
|
70
|
+
prot_m08_err = np.nan
|
|
71
|
+
|
|
72
|
+
# Calculate gyrochronology age:
|
|
73
|
+
if np.any(prot_m08 > 0.0) & (bv > 0.50) & (bv < 0.9):
|
|
74
|
+
age_m08 = 1e-3*(prot_m08/0.407/(bv - 0.495)**0.325)**(1./0.566)
|
|
75
|
+
#age_m08_err = 0.05*np.log(10)*age_m08
|
|
76
|
+
age_m08_err = 0.2 * age_m08 * np.log(10) # using 0.2 dex typical error from paper
|
|
77
|
+
else:
|
|
78
|
+
age_m08 = np.nan
|
|
79
|
+
age_m08_err = np.nan
|
|
80
|
+
|
|
81
|
+
r = prot_age_result()
|
|
82
|
+
r.prot_n84 = prot_n84
|
|
83
|
+
r.prot_n84_err = prot_n84_err
|
|
84
|
+
r.prot_m08 = prot_m08
|
|
85
|
+
r.prot_m08_err = prot_m08_err
|
|
86
|
+
r.age_m08 = age_m08
|
|
87
|
+
r.age_m08_err = age_m08_err
|
|
88
|
+
return r
|
|
89
|
+
# return prot_n84, prot_n84_err, prot_m08, prot_m08_err, age_m08, age_m08_err
|