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/plots.py CHANGED
@@ -1,12 +1,7 @@
1
- import os
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, label=None, N_in_label=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
- leg = ax.legend()
243
- handles, labels = ax.get_legend_handles_labels()
244
- for text in leg.get_texts():
245
- text.set_picker(True)
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
- def on_pick_point(event):
265
- print('annotations:', annotations)
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, (matplotlib.lines.Line2D, matplotlib.collections.LineCollection)):
272
- print(event.ind, artist)
273
- if isinstance(artist, matplotlib.lines.Line2D):
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
- ind = event.ind[0]
281
- # print(_s.mtime[ind], _s.mvrad[ind], _s.msvrad[ind])
282
-
283
- text = f'{inst}\n'
284
- text += f'{_s.mtime[ind]:9.5f}\n'
285
- text += f'RV: {_s.mvrad[ind]:.1f} ± {_s.msvrad[ind]:.1f}'
286
-
287
- annotations.append(
288
- ax.annotate(text, (_s.mtime[ind], _s.mvrad[ind]), xycoords='data',
289
- xytext=(5, 10), textcoords='offset points', fontsize=9,
290
- bbox={'boxstyle': 'round', 'fc': 'w'}, arrowprops=dict(arrowstyle="-"))
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
- class InfoTool(ToolToggleBase):
316
- description = "Show extra information about each observation"
317
- image = os.path.abspath(os.path.join(os.path.dirname(__file__), 'data', 'info.svg'))
318
- # def enable(self, *args, **kwargs):
319
- # self.figure.add_axes([1, 0, 0.3, 1])
320
- # self.figure.canvas.draw_idle()
321
-
322
- from PIL import UnidentifiedImageError
323
- try:
324
- tm.add_tool("infotool", InfoTool)
325
- fig.canvas.manager.toolbar.add_tool(tm.get_tool("infotool"), "toolgroup")
326
- raise UnidentifiedImageError
327
- except AttributeError:
328
- pass
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
- ax.legend()
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': f'BERV [km/s]',
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, matplotlib.text.Text):
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
- ye = getattr(self, quantity + '_err')[self.mask]
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 > 10:
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=10)
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