arvi 0.1.13__py3-none-any.whl → 0.1.14__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.

Potentially problematic release.


This version of arvi might be problematic. Click here for more details.

arvi/__init__.py CHANGED
@@ -2,18 +2,33 @@ __all__ = ['RV']
2
2
 
3
3
  from .timeseries import RV
4
4
 
5
- _ran_once = False
5
+ ## OLD
6
+ # # the __getattr__ function is always called twice, so we need this
7
+ # # to only build and return the RV object on the second time
8
+ # _ran_once = False
6
9
 
7
10
  def __getattr__(name: str):
8
11
  if name in (
9
12
  '_ipython_canary_method_should_not_exist_',
13
+ '_ipython_display_',
10
14
  '_repr_mimebundle_',
11
15
  '__wrapped__'
12
16
  ):
13
17
  return
14
18
 
15
- global _ran_once # can't do it any other way :(
16
- if _ran_once:
17
- return RV(name)
18
- else:
19
- _ran_once = True
19
+ try:
20
+ globals()[name] = RV(name)
21
+ return globals()[name]
22
+ except ValueError as e:
23
+ raise ImportError(e) from None
24
+ # raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
25
+
26
+ ## OLD
27
+ # # can't do it any other way :(
28
+ # global _ran_once
29
+
30
+ # if _ran_once:
31
+ # _ran_once = False
32
+ # return RV(name)
33
+ # else:
34
+ # _ran_once = True
arvi/berv.py ADDED
@@ -0,0 +1,437 @@
1
+ import os
2
+ import numpy as np
3
+ import matplotlib.pyplot as plt
4
+
5
+ from arvi.headers import get_headers
6
+ from barycorrpy import get_BC_vel
7
+ from astropy.coordinates import SkyCoord
8
+ from astropy.time import Time
9
+ from astropy import units as u
10
+ from astropy import constants as const
11
+ from astropy.timeseries import LombScargle
12
+ from tqdm import tqdm
13
+
14
+ from .setup_logger import logger
15
+ from . import config
16
+
17
+
18
+ def correct_rvs(self, simple=False, H=None, save_files=False, plot=True):
19
+ """
20
+ """
21
+ import pickle
22
+
23
+ if hasattr(self, '_did_correct_berv') and self._did_correct_berv:
24
+ logger.info('Already corrected for the BERV! Not doing anything.')
25
+ return
26
+
27
+ path = os.path.dirname(__file__)
28
+ path = os.path.join(path, 'data')
29
+ pkl = os.path.join(path, 'berv_espresso_sine.pkl')
30
+ berv_espresso = pickle.load(open(pkl, 'rb'))
31
+
32
+ if simple:
33
+ logger.info('Correcting RVs with a previously-fitted sinusoid function')
34
+ _f = berv_espresso['func'].replace('lambda t: ', '')
35
+ logger.info(f': {_f}')
36
+ f = eval(berv_espresso['func'])
37
+ if plot:
38
+ _, ax = self.plot()
39
+ ax.plot(self._tt, f(self._tt) + self.vrad.mean(), 'k')
40
+ _, axgls = self.gls(label='before')
41
+
42
+ self.vrad = self.vrad + f(self.time)
43
+
44
+ if plot:
45
+ self.gls(ax=axgls, label='after')
46
+
47
+ return f(self.time)
48
+
49
+ else:
50
+ logger.info('Correcting RVs with actual difference between BERVs')
51
+ logger.info('(basically, use BERV_barycorrpy for BERV correction)')
52
+
53
+ old_vrad = self.vrad.copy()
54
+
55
+ _, berv = BERV(self, H=H, use_gaia_meassurements=True, plx=self.gaia.plx,
56
+ plot=False, ignore_mask=True)
57
+
58
+ if plot:
59
+ fig, axs = plt.subplots(2, 1, constrained_layout=True, height_ratios=(3, 1), sharex=True)
60
+ _, ax = self.plot(ax=axs[0])
61
+ _, axgls = self.gls(label='before')
62
+
63
+ # undo secular acceleration, if it was done
64
+ _did_secular_acceleration = self._did_secular_acceleration
65
+ self._undo_secular_acceleration()
66
+
67
+ # transform RVs: RV --> RV - BERVpipe + BERVbarycorrpy
68
+
69
+ diff = berv[self.star]['berv_barycorrpy'] - berv[self.star]['berv_pipeline']
70
+
71
+ if save_files:
72
+ i_inst = np.hstack([np.arange(n) for n in self.NN.values()])
73
+ with open(f'{self.star}_berv_correction.rdb', 'w') as rdb:
74
+ rdb.write('# time\n')
75
+ rdb.write('# vrad\n')
76
+ rdb.write('# svrad\n')
77
+ rdb.write('# berv - BERV value from header\n')
78
+ rdb.write('# berv_pipe - BERV from header corrected for 1.55e-8 factor\n')
79
+ rdb.write('# berv_barycorrpy - BERV value from barycorrpy\n')
80
+ rdb.write('# diff - difference between berv_barycorrpy and berv_pipe\n')
81
+ rdb.write('# vrad_berv_corrected = vrad + diff\n')
82
+ rdb.write('# instrument\n')
83
+ rdb.write('# i - index\n')
84
+ rdb.write('# i_inst - index within the instrument\n')
85
+ rdb.write('#\n')
86
+ rdb.write('# --> TO CORRECT vrad, we ** add the diff column **\n')
87
+ rdb.write('# --> the result of this operation is in column vrad_berv_corrected\n')
88
+ rdb.write('# --> vrad_berv_corrected is already corrected for the secular acceleration, vrad is not\n')
89
+ rdb.write('#\n')
90
+ #
91
+ cols = [
92
+ 'time', 'vrad', 'svrad',
93
+ 'berv', 'berv_pipe', 'berv_barycorrpy', 'diff', 'vrad_berv_corrected',
94
+ 'instrument', 'i', 'i_inst'
95
+ ]
96
+ rdb.write('# ' + '\t'.join(cols) + '\n')
97
+ for i, t in enumerate(self.time):
98
+ rdb.write(f'{t:11.5f}\t')
99
+ # if _did_secular_acceleration:
100
+ # rdb.write(f'{old_vrad[i]:13.5f}\t')
101
+ # else:
102
+ rdb.write(f'{self.vrad[i]:13.7f}\t')
103
+ rdb.write(f'{self.svrad[i]:13.7f}\t')
104
+ rdb.write(f'{self.berv[i]:15.7f}\t')
105
+ rdb.write(f'{berv[self.star]["berv_pipeline"][i]/1e3:15.7f}\t')
106
+ rdb.write(f'{berv[self.star]["berv_barycorrpy"][i]/1e3:15.7f}\t')
107
+ rdb.write(f'{diff[i]:15.7f}\t')
108
+ rdb.write(f'{self.vrad[i] + diff[i]:13.7f}\t')
109
+ rdb.write(f'{self.instrument_array[i]}\t')
110
+ rdb.write(f'{i}\t')
111
+ rdb.write(f'{i_inst[i]}\t')
112
+ rdb.write('\n')
113
+
114
+ self.add_to_vrad(diff)
115
+ self._did_correct_berv = True
116
+ self._did_secular_acceleration = True # "automatically", by using BERV_barycorrpy
117
+ self._did_secular_acceleration_simbad = False
118
+ self._did_secular_acceleration_epoch = Time('J2016').jd - 24e5
119
+
120
+ # the secular acceleration hadn't been done, but it was introduced by
121
+ # BERV_barycorrpy, so we need to undo it
122
+ if not _did_secular_acceleration:
123
+ self._undo_secular_acceleration()
124
+
125
+ if plot:
126
+ self.plot(ax=axs[0], marker='+', ms=5)
127
+ axs[1].plot(self.time, old_vrad - self.vrad, '.k', label='old RV - new RV')
128
+ ma = np.abs(axs[1].get_ylim()).max()
129
+ axs[1].set(ylim=(-ma, ma), xlabel=axs[0].get_xlabel(), ylabel='RV difference [m/s]')
130
+ self.gls(ax=axgls, label='after')
131
+
132
+ return diff
133
+
134
+ def get_A_and_V_from_lesta(self, username=config.username):
135
+ try:
136
+ import paramiko
137
+ except ImportError:
138
+ raise ImportError("paramiko is not installed. Please install it with 'pip install paramiko'")
139
+
140
+ logs = []
141
+ for f in self.raw_file:
142
+ f = f.replace('espresso/', '/projects/astro/ESPRESSODRS/')
143
+ f = f.replace('nirps/', '/projects/astro/NIRPSDRS/')
144
+ f = f.replace('.fits', '_SCIENCE_FP.log')
145
+ f = f.replace('reduced', 'log')
146
+ f = f.replace('r.ESPRE', 'ESPRESSO')
147
+ logs.append(f)
148
+
149
+ A, V = [], []
150
+
151
+ try:
152
+ ssh = paramiko.SSHClient()
153
+ ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
154
+ ssh.connect("lesta02.astro.unige.ch", username=username, timeout=5)
155
+ except Exception as e:
156
+ if 'getaddrinfo failed' in str(e):
157
+ jump = paramiko.SSHClient()
158
+ jump.set_missing_host_key_policy(paramiko.AutoAddPolicy())
159
+ jump.connect('login01.astro.unige.ch', username=username, timeout=5)
160
+ jump_transport = jump.get_transport()
161
+ jump_channel = jump_transport.open_channel('direct-tcpip', ('10.194.64.162', 22), ('129.194.64.20', 22))
162
+ ssh = paramiko.SSHClient()
163
+ ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
164
+ ssh.connect('lesta02.astro.unige.ch', username=username, sock=jump_channel)
165
+ else:
166
+ raise e
167
+
168
+ with ssh.open_sftp() as sftp:
169
+ pbar = tqdm(logs, total=len(logs), unit='file', desc='Reading logs')
170
+ for f in pbar:
171
+ with sftp.open(f) as fp:
172
+ pattern1 = 'Sun-Earth'
173
+ pattern2 = "Barycentric Observer's Velocity"
174
+ for line in fp:
175
+ if pattern1 in line:
176
+ value = line.strip().split(':')[-1].replace('\x1b[32m', '').replace('\x1b[0m', '').replace(' ', '')
177
+ A.append(float(value))
178
+ if pattern2 in line:
179
+ value = line.strip().split(':')[-1].replace('\x1b[32m', '').replace('\x1b[0m', '').replace(' ', '')
180
+ V.append(float(value))
181
+
182
+ ssh.close()
183
+
184
+ return np.array(A), np.array(V)
185
+
186
+
187
+ def BERV(self, H=None, use_gaia_meassurements=False, plx=None,
188
+ A=None, V=None, plot=True, ignore_mask=False, verbose=False, dpi=None):
189
+ """ Calculate Barycentric Radial Velocity with barycorr and compare with pipeline
190
+
191
+ Args:
192
+ H (list, optional):
193
+ List of (CCF/S1D/etc) headers for the target. If None, try to
194
+ download the CCF files to get the headers.
195
+ use_gaia_meassurements (bool, optional):
196
+ Use Gaia coordinates and proper motions instead of those in the headers.
197
+ plx (float, optional):
198
+ Value of stellar parallax [mas] to use in barycorr.
199
+ A (array, optional):
200
+ Earth-Sun distance [AU] for each BJD (found in the pipeline logs).
201
+ V (array, optional):
202
+ Earth's orbital velocity [km/s] for each BJD (found in the pipeline logs).
203
+ plot (bool, optional):
204
+ Plot the results.
205
+ """
206
+ if H is None:
207
+ H = get_headers(self, check_lesta=False, check_exo2=False, instrument='ESPRE')
208
+
209
+ if len(H) != self.N:
210
+ raise ValueError(f'Expected {self.N} headers (in `H`), got {len(H)}')
211
+
212
+ if 'HARPS' in H[0]['INSTRUME'] or 'NIRPS' in H[0]['INSTRUME']:
213
+ obsname = 'lasilla'
214
+ elif 'ESPRESSO' in H[0]['INSTRUME']:
215
+ obsname = 'paranal'
216
+ else:
217
+ raise ValueError('unknown instrument')
218
+
219
+ bjd = np.array([h['HIERARCH ESO QC BJD'] for h in H])
220
+ bjd -= 24e5
221
+ berv_pipeline = np.array([h['HIERARCH ESO QC BERV'] for h in H])
222
+
223
+ # in the pipeline, the BERV is used to shift wavelenghts with this formula
224
+ # berv_factor = (1 + 1.55e-8) * (1 + BERV/c)
225
+ # The 1.55e-8 factor is an average of some relativistic effects, which are
226
+ # probably already included in the BERV calculated from barycorrpy.
227
+ # Therefore, we compute an "effective" BERV from the pipeline doing
228
+ # (1 + 1.55e-8) * (1 + BERV/c) = 1 + effBERV/c
229
+ # => effBERV = ((1 + 1.55e-8) * (1 + BERV/c) - 1) * c
230
+
231
+ if A is None and V is None:
232
+ if verbose:
233
+ logger.info("Using mean value for Earth-Sun distance and Earth's orbital velocity")
234
+
235
+ if A is None:
236
+ Φobs = const.G * const.M_sun / const.au + const.G * const.M_earth / const.R_earth
237
+ else:
238
+ A = np.atleast_1d(A) * u.km
239
+ Φobs = const.G * const.M_sun / A + const.G * const.M_earth / const.R_earth
240
+
241
+ if V is None:
242
+ V = 29785 *u.m / u.second
243
+ else:
244
+ V = np.atleast_1d(V) * u.km / u.second
245
+
246
+ f = 1 / (1 - Φobs / const.c**2 - V**2 / (2*const.c**2))
247
+ c = const.c.to(u.km / u.second).value
248
+ berv_pipeline = (f * (1 + berv_pipeline/c) - 1) * c
249
+
250
+
251
+ tmmean = np.array([h['HIERARCH ESO QC TMMEAN USED'] for h in H])
252
+ mjdobs = np.array([h['MJD-OBS'] for h in H])
253
+ texp = np.array([h['EXPTIME'] for h in H])
254
+ jd = mjdobs + 24e5 + 0.5 + (texp * tmmean)/60/60/24
255
+
256
+ if verbose:
257
+ logger.info(f"Unique exposure times: {np.unique(texp)}")
258
+
259
+ berv = []
260
+ if verbose:
261
+ pbar = enumerate(jd)
262
+ else:
263
+ pbar = tqdm(enumerate(jd), total=len(jd),
264
+ unit='observation', desc='Computing BERV')
265
+
266
+ for i, _jd in pbar:
267
+ if use_gaia_meassurements:
268
+ if not hasattr(self, 'gaia'):
269
+ raise ValueError('No Gaia data available')
270
+
271
+ target = self.gaia.coords
272
+ pmra = self.gaia.pmra
273
+ pmdec = self.gaia.pmdec
274
+ epoch = Time('J2016').jd
275
+ else:
276
+ ra = H[i]['* TARG ALPHA'][0]
277
+ ra = f'{ra:09.2f}'
278
+ ra = ra[:2] + 'h' + ra[2:4] + 'm' + ra[4:] + 's'
279
+
280
+ dec = H[i]['* TARG DELTA'][0]
281
+ if dec < 0:
282
+ dec = f'{dec:010.2f}'
283
+ else:
284
+ dec = f'{dec:09.2f}'
285
+ if dec.startswith('-'):
286
+ dec = dec[:3] + 'd' + dec[3:5] + 'm' + dec[5:] + 's'
287
+ else:
288
+ dec = dec[:2] + 'd' + dec[2:4] + 'm' + dec[4:] + 's'
289
+
290
+ target = SkyCoord(ra, dec)
291
+ pmra = H[i]['* TARG PMA'][0] * 1e3
292
+ pmdec = H[i]['* TARG PMD'][0] * 1e3
293
+ epoch = Time('J2000').jd
294
+
295
+ if verbose:
296
+ logger.info(f'jd: {_jd}')
297
+ logger.info(f'\t ra: {target.ra}')
298
+ logger.info(f'\t dec: {target.dec}')
299
+ logger.info(f'\t pmra: {pmra}')
300
+ logger.info(f'\t pmdec: {pmdec}')
301
+
302
+
303
+ px = plx or 0.0
304
+ out = get_BC_vel(_jd, obsname=obsname, rv=0.0, px=px, zmeas=0.0, epoch=epoch,
305
+ ra=target.ra.value, dec=target.dec.value, pmra=pmra, pmdec=pmdec)
306
+ # print(out[1][3])
307
+ berv.append(out[0])
308
+
309
+ berv = np.array(berv).flatten()
310
+
311
+ if ignore_mask: # ignore the system's masked points
312
+ pass
313
+ else: # mask points in the BERV output as well
314
+ bjd = bjd[self.mask]
315
+ berv = berv[self.mask]
316
+ berv_pipeline = berv_pipeline[self.mask]
317
+
318
+ fig = None
319
+ if plot:
320
+ fig, axs = plt.subplots(2, 1, figsize=(8, 6), dpi=dpi, sharex=True,
321
+ constrained_layout=True)
322
+
323
+ axs[0].set_title(f'{self.star}', loc='right')
324
+ axs[0].plot(bjd, berv_pipeline*1e3, '.', label='pipeline', alpha=0.5)
325
+ axs[0].plot(bjd, berv, '.', label='barycorrpy', alpha=0.5)
326
+ axs[0].legend(bbox_to_anchor=(0.0, 1.15), loc=2, borderaxespad=0., ncol=2)
327
+ axs[0].set(xlabel='BJD - 2450000', ylabel='BERV [m/s]')
328
+
329
+
330
+ if plx is not None:
331
+ epoch = 55500
332
+ sa = self.secular_acceleration(just_compute=True)
333
+ print('sa:', sa)
334
+ sec_acc = sa.value * (bjd - epoch) / 365.25
335
+
336
+ axs[0].plot(bjd, sec_acc)
337
+
338
+ # fitp = np.polyfit(bjd - epoch, diff, 1)
339
+ # axs[1].plot(bjd, np.polyval(fitp, bjd - epoch))
340
+ # axs[1].plot(bjd, np.mean(diff) + diff - np.polyval(fitp, bjd - epoch), '.')
341
+
342
+ if plx is None:
343
+ diff = berv - berv_pipeline*1e3
344
+ label=r'BERV$_{\rm barycorrpy}$ - BERV$_{\rm pipeline}$'
345
+ else:
346
+ diff = berv + sec_acc - berv_pipeline*1e3
347
+ label=r'BERV$_{\rm barycorrpy}$ (+SA) - BERV$_{\rm pipeline}$'
348
+
349
+ axs[1].plot(bjd, diff, 'k.', label=label)
350
+ axs[1].axhline(np.mean(diff), ls='--', c='k', alpha=0.1)
351
+
352
+ from adjustText import adjust_text
353
+ text = axs[1].text(bjd.max(), diff.min() + 0.1*diff.ptp(),
354
+ f'ptp: {diff.ptp()*1e2:.2f} cm/s',
355
+ ha='right', va='bottom', color='g', alpha=0.8)
356
+ axs[1].plot([bjd[np.argmax(diff)], bjd.max() + 0.05 * bjd.ptp()],
357
+ [np.max(diff), np.max(diff)], 'g--', alpha=0.3)
358
+ axs[1].plot([bjd[np.argmin(diff)], bjd.max() + 0.05 * bjd.ptp()],
359
+ [np.min(diff), np.min(diff)], 'g--', alpha=0.3)
360
+
361
+ ax = axs[1].twinx()
362
+ diff_cms = 1e2*(diff - np.mean(diff))
363
+ ax.plot(bjd, diff_cms, alpha=0)
364
+ ma = np.max(np.abs(ax.get_ylim()))
365
+ ax.set_ylim(-1 - 5*round(ma/5), 1 + 5*round(ma/5))
366
+ ax.set(ylabel='diff - mean(diff) [cm/s]')
367
+ axs[1].set_ylim(np.mean(diff)-ma/100, np.mean(diff)+ma/100)
368
+
369
+ axs[1].legend(bbox_to_anchor=(0.0, 1.15), loc=2, borderaxespad=0.)
370
+ axs[1].set(xlabel='BJD - 2450000', ylabel='diff [m/s]')
371
+
372
+ # adjust_text([text], va='bottom')
373
+
374
+ return fig, {
375
+ self.star: {
376
+ 'bjd': bjd,
377
+ 'berv_pipeline': berv_pipeline*1e3,
378
+ 'berv_barycorrpy': berv
379
+ }
380
+ }
381
+
382
+
383
+ def plot_BERV_correction(self, H, A, V, berv2=None, berv6=None,
384
+ inset=False, inset_range=(3, 5)):
385
+ fig, axs = plt.subplot_mosaic('ab\ncc\ndd\nee', constrained_layout=True, figsize=(2*3.57, 10))
386
+
387
+ if berv2 is None:
388
+ _, berv2 = BERV(self, H, plot=False)
389
+ if berv6 is None:
390
+ _, berv6 = BERV(self, H, A=A, V=V, plot=False)
391
+
392
+ self.plot(ax=axs['a'], ms=2)
393
+ axs['a'].set_title('original', loc='right')
394
+ self.gls(ax=axs['e'], label='original', color='r', alpha=0.5,
395
+ fill_between=True, samples_per_peak=20)
396
+
397
+ temp_vrad = self.vrad.copy()
398
+ self.vrad[self.mask] = self.vrad[self.mask] - berv2[self.star]['berv_pipeline'].value + berv6[self.star]['berv_pipeline'].value
399
+
400
+ self.plot(ax=axs['b'], ms=2)
401
+ axs['b'].set_title('after correction', loc='right')
402
+
403
+ diff = temp_vrad[self.mask] - self.vrad[self.mask]
404
+
405
+ axs['c'].plot(self.mtime, diff, 'k.')
406
+ axs['c'].set_title('RV difference', loc='right')
407
+ axs['c'].set(xlabel='BJD - 2450000', ylabel='RV diff [m/s]')
408
+
409
+ text = axs['c'].text(self.mtime.max(), diff.min() + 0.1*diff.ptp(),
410
+ f'ptp: {diff.ptp()*1e2:.2f} cm/s',
411
+ ha='right', va='bottom', color='g', alpha=0.8)
412
+ axs['c'].plot([self.mtime[np.argmax(diff)], self.mtime.max() + 0.05 * self.mtime.ptp()],
413
+ [np.max(diff), np.max(diff)], 'g--', alpha=0.3)
414
+ axs['c'].plot([self.mtime[np.argmin(diff)], self.mtime.max() + 0.05 * self.mtime.ptp()],
415
+ [np.min(diff), np.min(diff)], 'g--', alpha=0.3)
416
+
417
+
418
+ f, p = LombScargle(self.mtime, diff).autopower(maximum_frequency=1.0, samples_per_peak=10)
419
+ axs['d'].semilogx(1/f, p, color='k', alpha=0.8)
420
+ axs['d'].vlines([365.25, 365.25/2], 0, 1, color='k', ls='--', alpha=0.3)
421
+ axs['d'].set(xlabel='Period [days]', ylabel='normalized power', ylim=(0, 1))
422
+ axs['d'].set_title('GLS of RV difference', loc='right')
423
+
424
+ if inset:
425
+ inset = axs['d'].inset_axes(bounds=[0.15, 0.3, 0.3, 0.6])
426
+ m = (1/f > inset_range[0]) & (1/f < inset_range[1])
427
+ inset.plot(1/f[m], p[m], color='k', alpha=0.8)
428
+ inset.set(xlim=inset_range, yticks=[])
429
+ inset.minorticks_on()
430
+
431
+ self.gls(ax=axs['e'], label='after correction', color='g', alpha=1,
432
+ lw=0.8, samples_per_peak=20)
433
+ axs['e'].set(xlabel='Period [days]', ylabel='normalized power')
434
+ axs['e'].sharex(axs['d'])
435
+
436
+ self.vrad = temp_vrad
437
+ return fig
arvi/dace_wrapper.py CHANGED
@@ -193,7 +193,7 @@ def get_observations(star, instrument=None, main_id=None, verbose=True):
193
193
  raise ValueError(msg) from None
194
194
  else:
195
195
  try:
196
- result = get_observations_from_instrument(star, instrument, main_id, verbose)
196
+ result = get_observations_from_instrument(star, instrument, main_id)
197
197
  except ValueError:
198
198
  msg = f'no {instrument} observations for {star}'
199
199
  raise ValueError(msg) from None
arvi/headers.py ADDED
@@ -0,0 +1,47 @@
1
+ from tqdm import tqdm
2
+ from astropy.io import fits
3
+ import iCCF
4
+
5
+ from . import config
6
+
7
+ def get_headers(self, check_lesta=False, lesta_username=config.username,
8
+ check_exo2=False, instrument=None):
9
+ try:
10
+ import paramiko
11
+ except ImportError:
12
+ raise ImportError("paramiko is not installed. Please install it with 'pip install paramiko'")
13
+
14
+ H = []
15
+
16
+ if check_lesta:
17
+ with paramiko.SSHClient() as ssh:
18
+ ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
19
+ ssh.connect("lesta02.astro.unige.ch", username=lesta_username)
20
+ sftp = ssh.open_sftp()
21
+
22
+ pbar = tqdm(self.raw_file, total=len(self.raw_file), unit='file', desc='Reading headers')
23
+ for f in pbar:
24
+ f = f.replace('espresso/', '/projects/astro/ESPRESSODRS/')
25
+ f = f.replace('nirps/', '/projects/astro/NIRPSDRS/')
26
+ f = f.replace('.fits', '_CCF_A.fits')#.replace(':', r'\:')
27
+ with sftp.open(f) as fp:
28
+ header = fits.getheader(fp)
29
+ H.append(header)
30
+
31
+ if len(H) == 0 and check_exo2:
32
+ raise NotImplementedError('getting headers from exo2 not yet implemented')
33
+
34
+ if len(H) == 0:
35
+ self.download_ccf()
36
+ if instrument is None:
37
+ I = iCCF.from_file(f'{self.star}_downloads/*CCF_A.fits')
38
+ else:
39
+ I = iCCF.from_file(f'{self.star}_downloads/r.{instrument}.*CCF_A.fits',
40
+ guess_instrument='HARPS' not in instrument)
41
+ H = [i.HDU[0].header for i in I]
42
+
43
+ # sort by BJD
44
+ H = sorted(H, key=lambda x: x['HIERARCH ESO QC BJD'])
45
+
46
+ return H
47
+
arvi/plots.py CHANGED
@@ -407,6 +407,7 @@ def plot_quantity(self, quantity, ax=None, show_masked=False, instrument=None,
407
407
  'bispan': f'BIS [{self.units}]',
408
408
  'rhk': r"$\log$ R'$_{HK}$",
409
409
  'berv': f'BERV [km/s]',
410
+ quantity: quantity,
410
411
  }
411
412
  ax.set_ylabel(ylabel[quantity])
412
413
 
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/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
arvi/timeseries.py CHANGED
@@ -11,7 +11,7 @@ import numpy as np
11
11
  from astropy import units
12
12
 
13
13
  from .setup_logger import logger
14
- from .config import return_self, check_internet, debug
14
+ from . import config
15
15
  from .translations import translate
16
16
  from .dace_wrapper import do_download_filetype, do_symlink_filetype, get_observations, get_arrays
17
17
  from .simbad_wrapper import simbad
@@ -23,6 +23,9 @@ from .HZ import getHZ_period
23
23
  from .utils import strtobool, there_is_internet, timer
24
24
 
25
25
 
26
+ class ExtraFields:
27
+ pass
28
+
26
29
  @dataclass
27
30
  class RV:
28
31
  """
@@ -71,7 +74,7 @@ class RV:
71
74
  self.__star__ = translate(self.star)
72
75
 
73
76
  if not self._child:
74
- if check_internet and not there_is_internet():
77
+ if config.check_internet and not there_is_internet():
75
78
  raise ConnectionError('There is no internet connection?')
76
79
 
77
80
  # complicated way to query Simbad with self.__star__ or, if that
@@ -103,9 +106,9 @@ class RV:
103
106
  logger.info(f'querying DACE for {self.__star__}...')
104
107
  try:
105
108
  with timer():
109
+ mid = self.simbad.main_id if hasattr(self, 'simbad') else None
106
110
  self.dace_result = get_observations(self.__star__, self.instrument,
107
- main_id=self.simbad.main_id,
108
- verbose=self.verbose)
111
+ main_id=mid, verbose=self.verbose)
109
112
  except ValueError as e:
110
113
  # querying DACE failed, should we raise an error?
111
114
  if self._raise_on_error:
@@ -218,6 +221,7 @@ class RV:
218
221
  self._did_secular_acceleration = False
219
222
  self._did_sigma_clip = False
220
223
  self._did_adjust_means = False
224
+ self._did_correct_berv = False
221
225
  self.__post_init__()
222
226
 
223
227
  def snapshot(self):
@@ -382,6 +386,7 @@ class RV:
382
386
  s.mask = kwargs.get('mask', np.full_like(s.time, True, dtype=bool))
383
387
 
384
388
  s.instruments = [inst]
389
+ s._quantities = np.array([])
385
390
 
386
391
  return s
387
392
 
@@ -443,6 +448,13 @@ class RV:
443
448
 
444
449
  s = cls(star, _child=True, **kwargs)
445
450
 
451
+ def find_column(data, names):
452
+ has_col = np.array([name in data.dtype.fields for name in names])
453
+ if any(has_col):
454
+ col = np.where(has_col)[0][0]
455
+ return data[names[col]]
456
+ return False
457
+
446
458
  for i, (f, instrument) in enumerate(zip(files, instruments)):
447
459
  data = np.loadtxt(f, skiprows=2, usecols=range(3), unpack=True)
448
460
  _s = cls(star, _child=True, **kwargs)
@@ -471,10 +483,11 @@ class RV:
471
483
  else:
472
484
  data = np.array([], dtype=np.dtype([]))
473
485
 
474
- if 'fwhm' in data.dtype.fields:
475
- _s.fwhm = data['fwhm']
476
- if 'sfwhm' in data.dtype.fields:
477
- _s.fwhm_err = data['sfwhm']
486
+ # try to find FWHM and uncertainty
487
+ if (v := find_column(data, ['fwhm'])) is not False: # walrus !!
488
+ _s.fwhm = v
489
+ if (sv := find_column(data, ['sfwhm', 'fwhm_err', 'sig_fwhm'])) is not False:
490
+ _s.fwhm_err = sv
478
491
  else:
479
492
  _s.fwhm_err = 2 * _s.svrad
480
493
  else:
@@ -484,12 +497,11 @@ class RV:
484
497
  _quantities.append('fwhm')
485
498
  _quantities.append('fwhm_err')
486
499
 
487
- if 'rhk' in data.dtype.fields:
488
- _s.rhk = data['rhk']
500
+ if (v := find_column(data, ['rhk'])) is not False:
501
+ _s.rhk = v
489
502
  _s.rhk_err = np.full_like(time, np.nan)
490
- for possible_name in ['srhk', 'rhk_err']:
491
- if possible_name in data.dtype.fields:
492
- _s.rhk_err = data[possible_name]
503
+ if (sv := find_column(data, ['srhk', 'rhk_err', 'sig_rhk'])) is not False:
504
+ _s.rhk_err = sv
493
505
  else:
494
506
  _s.rhk = np.zeros_like(time)
495
507
  _s.rhk_err = np.full_like(time, np.nan)
@@ -516,6 +528,12 @@ class RV:
516
528
  setattr(_s, q, np.full(time.size, True))
517
529
  _quantities.append(q)
518
530
 
531
+ _s.extra_fields = ExtraFields()
532
+ for field in data.dtype.names:
533
+ if field not in _quantities:
534
+ setattr(_s.extra_fields, field, data[field])
535
+ # _quantities.append(field)
536
+
519
537
  #! end hack
520
538
 
521
539
  _s.mask = np.ones_like(time, dtype=bool)
@@ -543,31 +561,75 @@ class RV:
543
561
  logger.error('iCCF is not installed. Please install it with `pip install iCCF`')
544
562
  return
545
563
 
564
+ verbose = kwargs.get('verbose', True)
565
+
546
566
  if isinstance(files, str):
547
567
  files = [files]
548
568
 
549
- I = iCCF.from_file(files)
569
+ CCFs = iCCF.from_file(files)
570
+
571
+ if not isinstance(CCFs, list):
572
+ CCFs = [CCFs]
550
573
 
551
- objects = np.unique([i.HDU[0].header['OBJECT'].replace(' ', '') for i in I])
574
+ objects = np.unique([i.HDU[0].header['OBJECT'].replace(' ', '') for i in CCFs])
552
575
  if objects.size != 1:
553
576
  logger.warning(f'found {objects.size} different stars in the CCF files, '
554
577
  'choosing the first one')
555
578
  star = objects[0]
556
579
 
557
580
  s = cls(star, _child=True)
581
+ instruments = list(np.unique([i.instrument for i in CCFs]))
558
582
 
559
- # time, RVs, uncertainties
560
- s.time = np.array([i.bjd for i in I])
561
- s.vrad = np.array([i.RV*1e3 for i in I])
562
- s.svrad = np.array([i.RVerror*1e3 for i in I])
583
+ for instrument in instruments:
584
+ # time, RVs, uncertainties
585
+ time = np.array([i.bjd for i in CCFs])
586
+ vrad = np.array([i.RV*1e3 for i in CCFs])
587
+ svrad = np.array([i.RVerror*1e3 for i in CCFs])
588
+ _s = RV.from_arrays(star, time, vrad, svrad, inst=instrument)
563
589
 
564
- s.fwhm = np.array([i.FWHM*1e3 for i in I])
565
- s.fwhm_err = np.array([i.FWHMerror*1e3 for i in I])
590
+ _quantities = []
566
591
 
567
- # mask
568
- s.mask = np.full_like(s.time, True, dtype=bool)
592
+ _s.fwhm = np.array([i.FWHM*1e3 for i in CCFs])
593
+ _s.fwhm_err = np.array([i.FWHMerror*1e3 for i in CCFs])
594
+
595
+ _quantities.append('fwhm')
596
+ _quantities.append('fwhm_err')
597
+
598
+ _s.contrast = np.array([i.contrast for i in CCFs])
599
+ _s.contrast_err = np.array([i.contrast_error for i in CCFs])
600
+
601
+ _quantities.append('contrast')
602
+ _quantities.append('contrast_err')
603
+
604
+ _s.texp = np.array([i.HDU[0].header['EXPTIME'] for i in CCFs])
605
+ _quantities.append('texp')
606
+
607
+ _s.date_night = np.array([
608
+ i.HDU[0].header['DATE-OBS'].split('T')[0] for i in CCFs
609
+ ])
610
+ _quantities.append('date_night')
611
+
612
+ _s.mask = np.full_like(_s.time, True, dtype=bool)
569
613
 
570
- s.instruments = list(np.unique([i.instrument for i in I]))
614
+ _s.drs_qc = np.array([i.HDU[0].header['HIERARCH ESO QC SCIRED CHECK'] for i in CCFs], dtype=bool)
615
+ # mask out drs_qc = False
616
+ if not _s.drs_qc.all():
617
+ n = (~ _s.drs_qc).sum()
618
+ if verbose:
619
+ logger.warning(f'masking {n} points where DRS QC failed for {instrument}')
620
+ _s.mask &= _s.drs_qc
621
+ print(_s.mask)
622
+
623
+ _s._quantities = np.array(_quantities)
624
+ setattr(s, instrument, _s)
625
+
626
+ s._child = False
627
+ s.instruments = instruments
628
+ s._build_arrays()
629
+
630
+ if instruments == ['ESPRESSO']:
631
+ from .instrument_specific import divide_ESPRESSO
632
+ divide_ESPRESSO(s)
571
633
 
572
634
  return s
573
635
 
@@ -610,7 +672,6 @@ class RV:
610
672
  logger.info(f'available: {self.instruments}')
611
673
  return
612
674
 
613
-
614
675
  def _build_arrays(self):
615
676
  """ build all concatenated arrays of `self` from each of the `.inst`s """
616
677
  if self._child:
@@ -653,7 +714,6 @@ class RV:
653
714
  )
654
715
  setattr(self, q, arr)
655
716
 
656
-
657
717
  def download_ccf(self, instrument=None, index=None, limit=None,
658
718
  directory=None, symlink=False, **kwargs):
659
719
  """ Download CCFs from DACE
@@ -828,7 +888,7 @@ class RV:
828
888
  if self.verbose:
829
889
  logger.info(f"Removed observations from '{instrument}'")
830
890
 
831
- if return_self:
891
+ if config.return_self:
832
892
  return self
833
893
 
834
894
  def remove_condition(self, condition):
@@ -856,7 +916,7 @@ class RV:
856
916
  index = np.atleast_1d(index)
857
917
  try:
858
918
  instrument_index = self.obs[index]
859
- instrument = np.array(self.instruments)[instrument_index - 1]
919
+ np.array(self.instruments)[instrument_index - 1]
860
920
  except IndexError:
861
921
  logger.errors(f'index {index} is out of bounds for N={self.N}')
862
922
  return
@@ -869,7 +929,7 @@ class RV:
869
929
  # for i, inst in zip(index, instrument):
870
930
  # index_in_instrument = i - (self.obs < instrument_index).sum()
871
931
  # getattr(self, inst).mask[index_in_instrument] = False
872
- if return_self:
932
+ if config.return_self:
873
933
  return self
874
934
 
875
935
  def remove_non_public(self):
@@ -932,7 +992,7 @@ class RV:
932
992
  instruments = self._check_instrument(instrument)
933
993
  rng = np.random.default_rng(seed=seed)
934
994
  for inst in instruments:
935
- s = getattr(self, inst)
995
+ # s = getattr(self, inst)
936
996
  mask_for_this_inst = self.obs == self.instruments.index(inst) + 1
937
997
  # only choose if there are more than n points
938
998
  if self.mask[mask_for_this_inst].sum() > n:
@@ -1037,13 +1097,18 @@ class RV:
1037
1097
  self.vrad = self.vrad - sa * (self.time - epoch) / 365.25
1038
1098
  else:
1039
1099
  for inst in self.instruments:
1100
+ s = getattr(self, inst)
1101
+
1102
+ # if RVs come from a publication, don't remove the secular
1103
+ # acceleration
1104
+ if np.all(s.pub_reference != ''):
1105
+ continue
1106
+
1040
1107
  if 'HIRES' in inst: # never remove it from HIRES...
1041
1108
  continue
1042
1109
  if 'NIRPS' in inst: # never remove it from NIRPS...
1043
1110
  continue
1044
1111
 
1045
- s = getattr(self, inst)
1046
-
1047
1112
  if hasattr(s, '_did_secular_acceleration') and s._did_secular_acceleration:
1048
1113
  continue
1049
1114
 
@@ -1055,7 +1120,7 @@ class RV:
1055
1120
  self._did_secular_acceleration_epoch = epoch
1056
1121
  self._did_secular_acceleration_simbad = force_simbad
1057
1122
 
1058
- if return_self:
1123
+ if config.return_self:
1059
1124
  return self
1060
1125
 
1061
1126
  def _undo_secular_acceleration(self):
@@ -1111,7 +1176,7 @@ class RV:
1111
1176
  # # if insts.size == 1: # of the same instrument?
1112
1177
  # if self.verbose:
1113
1178
  # logger.warning(f'would remove all observations from {insts[0]}, skipping')
1114
- # if return_self:
1179
+ # if config.return_self:
1115
1180
  # return self
1116
1181
  # continue
1117
1182
 
@@ -1123,7 +1188,7 @@ class RV:
1123
1188
  self._did_adjust_means = False
1124
1189
  self.adjust_means()
1125
1190
 
1126
- if return_self:
1191
+ if config.return_self:
1127
1192
  return self
1128
1193
 
1129
1194
  def clip_maxerror(self, maxerror:float):
@@ -1145,7 +1210,7 @@ class RV:
1145
1210
  logger.warning(f'clip_maxerror ({maxerror} {self.units}) removed {n} point' + s)
1146
1211
 
1147
1212
  self._propagate_mask_changes()
1148
- if return_self:
1213
+ if config.return_self:
1149
1214
  return self
1150
1215
 
1151
1216
  def bin(self):
@@ -1309,7 +1374,7 @@ class RV:
1309
1374
 
1310
1375
  self._build_arrays()
1311
1376
  self._did_adjust_means = True
1312
- if return_self:
1377
+ if config.return_self:
1313
1378
  return self
1314
1379
 
1315
1380
  def add_to_vrad(self, values):
@@ -1546,6 +1611,8 @@ class RV:
1546
1611
 
1547
1612
 
1548
1613
  #
1614
+ from .stellar import calc_prot_age
1615
+
1549
1616
  @property
1550
1617
  def HZ(self):
1551
1618
  if not hasattr(self, 'star_mass'):
arvi/translations.py CHANGED
@@ -1,8 +1,10 @@
1
1
  import re
2
2
 
3
3
  STARS = {
4
+ '_51Peg': '51 Peg',
4
5
  'Barnard': 'GJ699',
5
6
  "Barnard's": 'GJ699',
7
+ 'Ross128': 'Ross 128',
6
8
  }
7
9
 
8
10
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: arvi
3
- Version: 0.1.13
3
+ Version: 0.1.14
4
4
  Summary: The Automated RV Inspector
5
5
  Author-email: João Faria <joao.faria@unige.ch>
6
6
  License: MIT
@@ -1,23 +1,26 @@
1
1
  arvi/HZ.py,sha256=u7rguhlILRBW-LOczlY3dkIB4LM8p8W7Xfg4FnNaYG0,2850
2
- arvi/__init__.py,sha256=cECMjLbPiolmft2lsnNoszU6_1a-dKOVY8cb2D1CBfg,366
2
+ arvi/__init__.py,sha256=stLL3UtXmR1jpNEoMyVohjvkCatunz1w5ZK0ApmWuYQ,814
3
3
  arvi/ariadne_wrapper.py,sha256=jv8Wl35LfHl1UH1EklbvxHcQHqaEDhRGNogSYjFt7Y4,2141
4
+ arvi/berv.py,sha256=5avwcmc2nkYH1KDo-z4eMPsS1ElNvold2DWkziV13gE,17633
4
5
  arvi/binning.py,sha256=jJ_resqSvfI-2BT0cnGPFckTIei_k2Mk95oZyE5b5zQ,15250
5
6
  arvi/config.py,sha256=wyj6FTxN7QOZj8LaHAe_ZdPKVfrNfobNNOHRNLH-S7Q,339
6
- arvi/dace_wrapper.py,sha256=VjC1YjZ7URkHkgRO7rUKjkxBtdMMjoEqcqTtZQ8M7NY,17034
7
+ arvi/dace_wrapper.py,sha256=HmsyThieljcjorSSZgmGTZPGBqNrm9QoPwjoR_GpVKo,17025
7
8
  arvi/extra_data.py,sha256=WEEaYeLh52Zdv0uyHO72Ys5MWS3naTAP4wJV2BJ1mbk,2551
8
9
  arvi/gaia_wrapper.py,sha256=YxqqlWkLAKBY7AGHfDpNbkOLYWK0lt0L5GZenn0FfxM,3555
10
+ arvi/headers.py,sha256=uvdJebw1M5YkGjE3vJJwYBOnLikib75uuZE9FXB5JJM,1673
9
11
  arvi/instrument_specific.py,sha256=fhkvR5jJzpJH7XbT8Fbzkaz6tGeoFYugkJCIAJgSR7o,4746
10
12
  arvi/lbl_wrapper.py,sha256=_ViGVkpakvuBR_xhu9XJRV5EKHpj5Go6jBZGJZMIS2Y,11850
11
13
  arvi/nasaexo_wrapper.py,sha256=mWt7eHgSZe4MBKCmUvMPTyUPGuiwGTqKugNBvmjOg9s,7306
12
- arvi/plots.py,sha256=2W1IwUeMdNq5--l6nQXn3EFft0UQgEyPyudRxTCRM40,25566
13
- arvi/programs.py,sha256=WpFE2cXYG-4ax2xmih0fFhvQbcVhnOEofVcwjePNmKQ,4505
14
+ arvi/plots.py,sha256=53_rHVh7SeyBylM91PP6mNr7WTGmIoRnR8JfKJPdu7E,25594
15
+ arvi/programs.py,sha256=C0Fbldjf-QEZYYJp5wBKP3h7zraD0O2mJC7Su967STg,4607
14
16
  arvi/reports.py,sha256=yrdajC-zz5_kH1Ucc6cU70DK-5dpG0Xyeru-EITKpNo,3355
15
17
  arvi/setup_logger.py,sha256=pBzaRTn0hntozjbaRVx0JIbWGuENkvYUApa6uB-FsRo,279
16
18
  arvi/simbad_wrapper.py,sha256=fwO0NbMvxAW3RyC58FPkLjXqLs9OkRyK0sYcb0ZVBQA,5571
17
19
  arvi/spectra.py,sha256=pTAWSW4vk96DWRQ-6l5mNJHUhiAyaPR-QDjZdOT6Ak0,7489
18
20
  arvi/stats.py,sha256=MQiyLvdiAFxIC29uajTy8kuxD-b2Y6mraL4AfWkRJkM,2576
19
- arvi/timeseries.py,sha256=PCQntiNK8MnlgKeyGOTd4BrQXm8NSjZRtGfLa4hfaE0,58257
20
- arvi/translations.py,sha256=AljjMsHRqzfcirdeavHsyVJFLvwKrM1wDLMv2RApG58,446
21
+ arvi/stellar.py,sha256=MdO1_-ZXHnMHbkG-a5LvrnYeULy6MCAnWMKPvR4NZxQ,2954
22
+ arvi/timeseries.py,sha256=sdm9l_LHItZHDAVI6r_wsQwkrfbuMX46q3Wqs0y-KGA,60787
23
+ arvi/translations.py,sha256=SUIrJHt3JZdL_GQh3OJyg2Gm3X5ut86w5zW8hZpxHe0,498
21
24
  arvi/utils.py,sha256=nuIOuUdysMesWF9ute4v-kbeo6DylWjWW-SJhCsHn9I,4078
22
25
  arvi/data/info.svg,sha256=0IMI6W-eFoTD8acnury79WJJakpBwLa4qKS4JWpsXiI,489
23
26
  arvi/data/obs_affected_ADC_issues.dat,sha256=tn93uOL0eCTYhireqp1wG-_c3CbxPA7C-Rf-pejVY8M,10853
@@ -25,8 +28,8 @@ arvi/data/obs_affected_blue_cryostat_issues.dat,sha256=z4AK17xfz8tGTDv1FjRvQFnio
25
28
  arvi/data/extra/HD86226_PFS1.rdb,sha256=vfAozbrKHM_j8dYkCBJsuHyD01KEM1asghe2KInwVao,3475
26
29
  arvi/data/extra/HD86226_PFS2.rdb,sha256=F2P7dB6gVyzCglUjNheB0hIHVClC5RmARrGwbrY1cfo,4114
27
30
  arvi/data/extra/metadata.json,sha256=C69hIw6CohyES6BI9vDWjxwSz7N4VOYX0PCgjXtYFmU,178
28
- arvi-0.1.13.dist-info/LICENSE,sha256=6JfQgl7SpM55t0EHMFNMnNh-AdkpGW25MwMiTnhdWQg,1068
29
- arvi-0.1.13.dist-info/METADATA,sha256=9wDBsoyGzFt-Eo186zVKLVJT_VdUlr_2C2uzrqQ5Fxs,1306
30
- arvi-0.1.13.dist-info/WHEEL,sha256=cpQTJ5IWu9CdaPViMhC9YzF8gZuS5-vlfoFihTBC86A,91
31
- arvi-0.1.13.dist-info/top_level.txt,sha256=4EeiKDVLD45ztuflTGfQ3TU8GVjJg5Y95xS5XjI-utU,5
32
- arvi-0.1.13.dist-info/RECORD,,
31
+ arvi-0.1.14.dist-info/LICENSE,sha256=6JfQgl7SpM55t0EHMFNMnNh-AdkpGW25MwMiTnhdWQg,1068
32
+ arvi-0.1.14.dist-info/METADATA,sha256=duTaG6amS-M9iVUNvfo2RFwnA_fz1gY1ysUB2N_yCaE,1306
33
+ arvi-0.1.14.dist-info/WHEEL,sha256=Wyh-_nZ0DJYolHNn1_hMa4lM7uDedD_RGVwbmTjyItk,91
34
+ arvi-0.1.14.dist-info/top_level.txt,sha256=4EeiKDVLD45ztuflTGfQ3TU8GVjJg5Y95xS5XjI-utU,5
35
+ arvi-0.1.14.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (70.1.0)
2
+ Generator: setuptools (71.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
File without changes