arvi 0.1.25__tar.gz → 0.1.26__tar.gz
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-0.1.25 → arvi-0.1.26}/PKG-INFO +1 -1
- {arvi-0.1.25 → arvi-0.1.26}/arvi/dace_wrapper.py +9 -15
- {arvi-0.1.25 → arvi-0.1.26}/arvi/extra_data.py +21 -5
- {arvi-0.1.25 → arvi-0.1.26}/arvi/gaia_wrapper.py +2 -1
- {arvi-0.1.25 → arvi-0.1.26}/arvi/plots.py +11 -6
- {arvi-0.1.25 → arvi-0.1.26}/arvi/timeseries.py +257 -45
- {arvi-0.1.25 → arvi-0.1.26}/arvi.egg-info/PKG-INFO +1 -1
- {arvi-0.1.25 → arvi-0.1.26}/arvi.egg-info/SOURCES.txt +2 -0
- arvi-0.1.26/tests/HD10700-Bcor_ESPRESSO18.rdb +3 -0
- arvi-0.1.26/tests/test_create_RV.py +21 -0
- {arvi-0.1.25 → arvi-0.1.26}/.github/workflows/docs-gh-pages.yml +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/.github/workflows/install.yml +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/.github/workflows/python-publish.yml +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/.gitignore +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/LICENSE +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/README.md +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/HZ.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/__init__.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/ariadne_wrapper.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/berv.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/binning.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/config.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/data/extra/HD86226_PFS1.rdb +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/data/extra/HD86226_PFS2.rdb +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/data/extra/metadata.json +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/data/info.svg +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/data/obs_affected_ADC_issues.dat +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/data/obs_affected_blue_cryostat_issues.dat +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/headers.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/instrument_specific.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/kima_wrapper.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/lbl_wrapper.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/nasaexo_wrapper.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/programs.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/reports.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/setup_logger.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/simbad_wrapper.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/spectra.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/stats.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/stellar.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/translations.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi/utils.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi.egg-info/dependency_links.txt +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi.egg-info/requires.txt +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/arvi.egg-info/top_level.txt +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/docs/API.md +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/docs/detailed.md +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/docs/index.md +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/docs/logo/detective.png +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/docs/logo/logo.png +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/mkdocs.yml +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/pyproject.toml +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/setup.cfg +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/setup.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/tests/test_binning.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/tests/test_import_object.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/tests/test_simbad.py +0 -0
- {arvi-0.1.25 → arvi-0.1.26}/tests/test_stats.py +0 -0
|
@@ -70,7 +70,6 @@ def get_arrays(result, latest_pipeline=True, ESPRESSO_mode='HR11', NIRPS_mode='H
|
|
|
70
70
|
if verbose and npipe > 1:
|
|
71
71
|
logger.info(f'selecting latest pipeline ({pipelines[0]}) for {inst}')
|
|
72
72
|
|
|
73
|
-
|
|
74
73
|
for pipe in pipelines:
|
|
75
74
|
modes = [m for m in result[inst][pipe].keys()]
|
|
76
75
|
|
|
@@ -85,24 +84,19 @@ def get_arrays(result, latest_pipeline=True, ESPRESSO_mode='HR11', NIRPS_mode='H
|
|
|
85
84
|
if verbose:
|
|
86
85
|
logger.warning(f'no observations for requested NIRPS mode ({NIRPS_mode})')
|
|
87
86
|
|
|
88
|
-
#
|
|
89
|
-
# done together with NIRPS
|
|
90
|
-
if '
|
|
87
|
+
# HARPS observations should not be separated by 'mode' if some are
|
|
88
|
+
# done together with NIRPS, but should be separated by 'EGGS' mode
|
|
89
|
+
if 'HARPS' in inst:
|
|
90
|
+
m0 = modes[0]
|
|
91
|
+
data = {
|
|
92
|
+
k: np.concatenate([result[inst][pipe][m][k] for m in modes])
|
|
93
|
+
for k in result[inst][pipe][m0].keys()
|
|
94
|
+
}
|
|
91
95
|
if 'HARPS+NIRPS' in modes:
|
|
92
|
-
m0 = modes[0]
|
|
93
|
-
data = {
|
|
94
|
-
k: np.concatenate([result[inst][pipe][m][k] for m in modes])
|
|
95
|
-
for k in result[inst][pipe][m0].keys()
|
|
96
|
-
}
|
|
97
96
|
arrays.append( ((str(inst), str(pipe), str(m0)), data) )
|
|
98
97
|
continue
|
|
99
98
|
|
|
100
|
-
if 'EGGS+NIRPS' in modes:
|
|
101
|
-
m0 = modes[0]
|
|
102
|
-
data = {
|
|
103
|
-
k: np.concatenate([result[inst][pipe][m][k] for m in modes])
|
|
104
|
-
for k in result[inst][pipe][m0].keys()
|
|
105
|
-
}
|
|
99
|
+
if 'EGGS+NIRPS' in modes or 'EGGS' in modes:
|
|
106
100
|
arrays.append( ((str(inst + '_EGGS'), str(pipe), str(m0)), data) )
|
|
107
101
|
continue
|
|
108
102
|
|
|
@@ -10,7 +10,8 @@ refs = {
|
|
|
10
10
|
'HD86226': 'Teske et al. 2020 (AJ, 160, 2)'
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
def get_extra_data(star, instrument=None, path=None, verbose=True
|
|
13
|
+
def get_extra_data(star, instrument=None, path=None, verbose=True,
|
|
14
|
+
check_for_kms=True):
|
|
14
15
|
if path is None:
|
|
15
16
|
path = os.path.dirname(__file__)
|
|
16
17
|
path = os.path.join(path, 'data', 'extra')
|
|
@@ -18,7 +19,7 @@ def get_extra_data(star, instrument=None, path=None, verbose=True):
|
|
|
18
19
|
metadata = json.load(open(os.path.join(path, 'metadata.json'), 'r'))
|
|
19
20
|
# print(metadata)
|
|
20
21
|
|
|
21
|
-
files = glob(os.path.join(path, star + '
|
|
22
|
+
files = glob(os.path.join(path, star.replace(' ', '') + '*.rdb'))
|
|
22
23
|
files = [f for f in files if os.path.isfile(f)]
|
|
23
24
|
files = [f for f in files if not os.path.basename(f).endswith('.zip')]
|
|
24
25
|
|
|
@@ -57,9 +58,24 @@ def get_extra_data(star, instrument=None, path=None, verbose=True):
|
|
|
57
58
|
if 'corrected_for_secular_acceleration' in metadata[file_basename]:
|
|
58
59
|
did_sa[i] = metadata[file_basename]['corrected_for_secular_acceleration']
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
with logger.contextualize(indent=' '):
|
|
62
|
+
s = timeseries.RV.from_rdb(files[0], star=star, instrument=instruments[0], units=units[0])
|
|
63
|
+
if check_for_kms and s.svrad.min() < 0.01:
|
|
64
|
+
units[0] = 'kms'
|
|
65
|
+
s = timeseries.RV.from_rdb(files[0], star=star, instrument=instruments[0], units=units[0])
|
|
66
|
+
if verbose:
|
|
67
|
+
logger.info(f'{instruments[0]:>12s} ├ ({s.N} observations)')
|
|
68
|
+
|
|
69
|
+
for file, instrument, unit in zip(files[1:], instruments[1:], units[1:]):
|
|
70
|
+
_s = timeseries.RV.from_rdb(file, star=star, instrument=instrument, units=unit)
|
|
71
|
+
if check_for_kms and _s.svrad.min() < 0.01:
|
|
72
|
+
unit = 'kms'
|
|
73
|
+
_s = timeseries.RV.from_rdb(file, star=star, instrument=instrument, units=unit)
|
|
74
|
+
if verbose:
|
|
75
|
+
logger.info(f'{instrument:>12s} ├ ({_s.N} observations)')
|
|
76
|
+
|
|
77
|
+
s = s + _s
|
|
78
|
+
|
|
63
79
|
|
|
64
80
|
for i, (inst, ref, inst_did_sa) in enumerate(zip(s.instruments, reference, did_sa)):
|
|
65
81
|
_s = getattr(s, inst)
|
|
@@ -31,6 +31,7 @@ gaia_source.source_id = {id}
|
|
|
31
31
|
|
|
32
32
|
translate = {
|
|
33
33
|
'Proxima': '5853498713190525696',
|
|
34
|
+
'GJ699': '4472832130942575872',
|
|
34
35
|
'LS II +14 13': '4318465066420528000',
|
|
35
36
|
}
|
|
36
37
|
|
|
@@ -81,7 +82,7 @@ class gaia:
|
|
|
81
82
|
pmra = simbad.pmra
|
|
82
83
|
pmdec = simbad.pmdec
|
|
83
84
|
rv = simbad.rvz_radvel
|
|
84
|
-
args = dict(ra=ra, dec=dec, plx=plx, pmra=pmra, pmdec=pmdec, rv=rv)
|
|
85
|
+
args = dict(ra=ra, dec=dec, plx=plx, pmra=pmra, pmdec=pmdec, rv=rv)
|
|
85
86
|
|
|
86
87
|
try:
|
|
87
88
|
if star in translate:
|
|
@@ -6,7 +6,7 @@ import numpy as np
|
|
|
6
6
|
from astropy.timeseries import LombScargle
|
|
7
7
|
|
|
8
8
|
from .setup_logger import logger
|
|
9
|
-
from . import config
|
|
9
|
+
from .config import config
|
|
10
10
|
from .stats import wmean
|
|
11
11
|
|
|
12
12
|
from .utils import lazy_import
|
|
@@ -123,8 +123,12 @@ def clickable_legend(fig, ax, leg):
|
|
|
123
123
|
h = handles[labels.index(artist.get_text())]
|
|
124
124
|
alpha_text = {None:0.2, 1.0: 0.2, 0.2:1.0}[artist.get_alpha()]
|
|
125
125
|
alpha_point = {None: 0.0, 1.0: 0.0, 0.2: 1.0}[artist.get_alpha()]
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
try:
|
|
127
|
+
h[0].set_alpha(alpha_point)
|
|
128
|
+
h[2][0].set_alpha(alpha_point)
|
|
129
|
+
except TypeError:
|
|
130
|
+
h.set_alpha(alpha_point)
|
|
131
|
+
|
|
128
132
|
artist.set_alpha(alpha_text)
|
|
129
133
|
fig.canvas.draw()
|
|
130
134
|
except ValueError:
|
|
@@ -488,14 +492,15 @@ def plot_quantity(self, quantity, ax=None, show_masked=False, instrument=None,
|
|
|
488
492
|
|
|
489
493
|
|
|
490
494
|
plot_fwhm = partialmethod(plot_quantity, quantity='fwhm')
|
|
491
|
-
|
|
495
|
+
plot_bispan = partialmethod(plot_quantity, quantity='bispan')
|
|
492
496
|
plot_contrast = partialmethod(plot_quantity, quantity='contrast')
|
|
493
497
|
plot_rhk = partialmethod(plot_quantity, quantity='rhk')
|
|
494
498
|
plot_berv = partialmethod(plot_quantity, quantity='berv')
|
|
495
499
|
|
|
496
500
|
|
|
497
501
|
@plot_fast
|
|
498
|
-
def gls(self, ax=None, label=None, fap=True, instrument=None,
|
|
502
|
+
def gls(self, ax=None, label=None, fap=True, instrument=None,
|
|
503
|
+
adjust_means=config.adjust_means_gls,
|
|
499
504
|
picker=True, **kwargs):
|
|
500
505
|
"""
|
|
501
506
|
Calculate and plot the Generalised Lomb-Scargle periodogram of the radial
|
|
@@ -711,7 +716,7 @@ def gls_quantity(self, quantity, ax=None, fap=True, instrument=None,
|
|
|
711
716
|
|
|
712
717
|
|
|
713
718
|
gls_fwhm = partialmethod(gls_quantity, quantity='fwhm')
|
|
714
|
-
|
|
719
|
+
gls_bispan = partialmethod(gls_quantity, quantity='bispan')
|
|
715
720
|
gls_rhk = partialmethod(gls_quantity, quantity='rhk')
|
|
716
721
|
|
|
717
722
|
|
|
@@ -25,7 +25,10 @@ units = lazy_import('astropy.units')
|
|
|
25
25
|
# from astropy import units
|
|
26
26
|
|
|
27
27
|
class ExtraFields:
|
|
28
|
-
|
|
28
|
+
@property
|
|
29
|
+
def fields(self):
|
|
30
|
+
return list(self.__dict__.keys())
|
|
31
|
+
|
|
29
32
|
|
|
30
33
|
@dataclass
|
|
31
34
|
class RV:
|
|
@@ -70,13 +73,17 @@ class RV:
|
|
|
70
73
|
_gaia = None
|
|
71
74
|
|
|
72
75
|
def __repr__(self):
|
|
76
|
+
ni = len(self.instruments)
|
|
73
77
|
if self.N == 0:
|
|
74
78
|
return f"RV(star='{self.star}', N=0)"
|
|
79
|
+
|
|
80
|
+
i = f'{ni} instrument' + ('s' if ni > 1 else '')
|
|
81
|
+
|
|
75
82
|
if self.time.size == self.mtime.size:
|
|
76
|
-
return f"RV(star='{self.star}', N={self.N})"
|
|
83
|
+
return f"RV(star='{self.star}', N={self.N}, {i})"
|
|
77
84
|
else:
|
|
78
85
|
nmasked = self.N - self.mtime.size
|
|
79
|
-
return f"RV(star='{self.star}', N={self.N}, masked={nmasked})"
|
|
86
|
+
return f"RV(star='{self.star}', N={self.N}, masked={nmasked}, {i})"
|
|
80
87
|
|
|
81
88
|
@property
|
|
82
89
|
def simbad(self):
|
|
@@ -272,11 +279,20 @@ class RV:
|
|
|
272
279
|
# if not isinstance(other, self.__class__):
|
|
273
280
|
# raise TypeError('unsupported operand type(s) for +: '
|
|
274
281
|
# f"'{self.__class__.__name__}' and '{other.__class__.__name__}'")
|
|
282
|
+
if other is None:
|
|
283
|
+
if inplace:
|
|
284
|
+
return
|
|
285
|
+
else:
|
|
286
|
+
return deepcopy(self)
|
|
275
287
|
|
|
276
288
|
if np.isin(self.instruments, other.instruments).any():
|
|
277
289
|
logger.error('the two objects share instrument(s), cannot add them')
|
|
278
290
|
return
|
|
279
291
|
|
|
292
|
+
if self._did_adjust_means or other._did_adjust_means:
|
|
293
|
+
self.adjust_means()
|
|
294
|
+
other.adjust_means()
|
|
295
|
+
|
|
280
296
|
if inplace:
|
|
281
297
|
#? could it be as simple as this?
|
|
282
298
|
for i in other.instruments:
|
|
@@ -511,18 +527,33 @@ class RV:
|
|
|
511
527
|
Examples:
|
|
512
528
|
s = RV.from_rdb('star_HARPS.rdb')
|
|
513
529
|
"""
|
|
530
|
+
from glob import glob
|
|
531
|
+
from os.path import splitext, basename
|
|
532
|
+
|
|
533
|
+
verbose = kwargs.pop('verbose', True)
|
|
534
|
+
|
|
514
535
|
if isinstance(files, str):
|
|
515
|
-
|
|
536
|
+
if '*' in files:
|
|
537
|
+
files = glob(files)
|
|
538
|
+
else:
|
|
539
|
+
files = [files]
|
|
540
|
+
|
|
541
|
+
if len(files) == 0:
|
|
542
|
+
if verbose:
|
|
543
|
+
logger.error('no files found')
|
|
544
|
+
return
|
|
516
545
|
|
|
517
546
|
if star is None:
|
|
518
|
-
star_ = np.unique([
|
|
547
|
+
star_ = np.unique([splitext(basename(f))[0].split('_')[0] for f in files])
|
|
519
548
|
if star_.size == 1:
|
|
520
|
-
|
|
521
|
-
|
|
549
|
+
star = star_[0].replace('-', '_')
|
|
550
|
+
if verbose:
|
|
551
|
+
logger.info(f'assuming star is {star}')
|
|
522
552
|
|
|
523
553
|
if instrument is None:
|
|
524
|
-
instruments = np.array([
|
|
525
|
-
|
|
554
|
+
instruments = np.array([splitext(basename(f))[0].split('_')[1] for f in files])
|
|
555
|
+
if verbose:
|
|
556
|
+
logger.info(f'assuming instruments: {instruments}')
|
|
526
557
|
else:
|
|
527
558
|
instruments = np.atleast_1d(instrument)
|
|
528
559
|
|
|
@@ -537,11 +568,14 @@ class RV:
|
|
|
537
568
|
has_col = np.array([name in data.dtype.fields for name in names])
|
|
538
569
|
if any(has_col):
|
|
539
570
|
col = np.where(has_col)[0][0]
|
|
540
|
-
return data[names[col]]
|
|
571
|
+
return np.atleast_1d(data[names[col]])
|
|
541
572
|
return False
|
|
542
573
|
|
|
543
574
|
for i, (f, instrument) in enumerate(zip(files, instruments)):
|
|
544
575
|
data = np.loadtxt(f, skiprows=2, usecols=range(3), unpack=True)
|
|
576
|
+
if data.ndim == 1:
|
|
577
|
+
data = data.reshape(-1, 1)
|
|
578
|
+
|
|
545
579
|
_s = cls(star, _child=True, **kwargs)
|
|
546
580
|
time = data[0]
|
|
547
581
|
_s.time = time
|
|
@@ -559,14 +593,20 @@ class RV:
|
|
|
559
593
|
names = header.split()
|
|
560
594
|
|
|
561
595
|
if len(names) > 3:
|
|
562
|
-
if f.endswith('.rdb'):
|
|
563
|
-
|
|
564
|
-
else:
|
|
565
|
-
|
|
596
|
+
# if f.endswith('.rdb'):
|
|
597
|
+
# kw = dict(skip_header=2, dtype=None, encoding=None)
|
|
598
|
+
# else:
|
|
599
|
+
comments = '#'
|
|
600
|
+
kw = dict(skip_header=2, comments=comments,
|
|
601
|
+
names=names, dtype=None, encoding=None)
|
|
566
602
|
if '\t' in header:
|
|
567
603
|
data = np.genfromtxt(f, **kw, delimiter='\t')
|
|
568
604
|
else:
|
|
569
605
|
data = np.genfromtxt(f, **kw)
|
|
606
|
+
|
|
607
|
+
# if data.ndim in (0, 1):
|
|
608
|
+
# data = data.reshape(-1, 1)
|
|
609
|
+
|
|
570
610
|
if len(names) == len(data.dtype.names):
|
|
571
611
|
data.dtype.names = names
|
|
572
612
|
else:
|
|
@@ -580,19 +620,20 @@ class RV:
|
|
|
580
620
|
else:
|
|
581
621
|
_s.fwhm_err = 2 * _s.svrad
|
|
582
622
|
else:
|
|
583
|
-
_s.fwhm = np.
|
|
623
|
+
_s.fwhm = np.full_like(time, np.nan)
|
|
584
624
|
_s.fwhm_err = np.full_like(time, np.nan)
|
|
585
625
|
|
|
586
626
|
_quantities.append('fwhm')
|
|
587
627
|
_quantities.append('fwhm_err')
|
|
588
628
|
|
|
629
|
+
# try to find R'HK and uncertainty
|
|
589
630
|
if (v := find_column(data, ['rhk'])) is not False:
|
|
590
631
|
_s.rhk = v
|
|
591
632
|
_s.rhk_err = np.full_like(time, np.nan)
|
|
592
633
|
if (sv := find_column(data, ['srhk', 'rhk_err', 'sig_rhk'])) is not False:
|
|
593
634
|
_s.rhk_err = sv
|
|
594
635
|
else:
|
|
595
|
-
_s.rhk = np.
|
|
636
|
+
_s.rhk = np.full_like(time, np.nan)
|
|
596
637
|
_s.rhk_err = np.full_like(time, np.nan)
|
|
597
638
|
|
|
598
639
|
_quantities.append('rhk')
|
|
@@ -636,8 +677,9 @@ class RV:
|
|
|
636
677
|
|
|
637
678
|
_s.extra_fields = ExtraFields()
|
|
638
679
|
for name in data.dtype.names:
|
|
639
|
-
|
|
640
|
-
|
|
680
|
+
# don't repeat some quantities
|
|
681
|
+
if name not in _quantities + ['bjd', 'rjd', 'vrad', 'svrad']:
|
|
682
|
+
name_ = name.replace(' ', '_').replace('-', '_')
|
|
641
683
|
setattr(_s.extra_fields, name_, data[name])
|
|
642
684
|
# _quantities.append(field)
|
|
643
685
|
|
|
@@ -744,8 +786,8 @@ class RV:
|
|
|
744
786
|
return s
|
|
745
787
|
|
|
746
788
|
@classmethod
|
|
747
|
-
@lru_cache(maxsize=60)
|
|
748
|
-
def from_KOBE_file(cls, star, **kwargs):
|
|
789
|
+
# @lru_cache(maxsize=60)
|
|
790
|
+
def from_KOBE_file(cls, star, directory='.', force_download=False, **kwargs):
|
|
749
791
|
assert 'KOBE' in star, f'{star} is not a KOBE star?'
|
|
750
792
|
import requests
|
|
751
793
|
from requests.auth import HTTPBasicAuth
|
|
@@ -766,7 +808,14 @@ class RV:
|
|
|
766
808
|
local_targz_file = os.path.join(get_data_path(), 'KOBE_fitsfiles.tar.gz')
|
|
767
809
|
fits_file = f'{star}_RVs.fits'
|
|
768
810
|
|
|
769
|
-
|
|
811
|
+
local_exists = os.path.exists(local_targz_file)
|
|
812
|
+
local_recent = os.path.getmtime(local_targz_file) > pytime() - 60*60*2
|
|
813
|
+
|
|
814
|
+
if os.path.exists(os.path.join(directory, fits_file)):
|
|
815
|
+
logger.info(f'found file "{fits_file}" in "{directory}"')
|
|
816
|
+
hdul = fits.open(fits_file)
|
|
817
|
+
|
|
818
|
+
elif local_exists and local_recent and not force_download:
|
|
770
819
|
tar = tarfile.open(local_targz_file)
|
|
771
820
|
|
|
772
821
|
if fits_file not in tar.getnames():
|
|
@@ -778,6 +827,7 @@ class RV:
|
|
|
778
827
|
else:
|
|
779
828
|
resp = requests.get(f'https://kobe.caha.es/internal/fitsfiles/{fits_file}',
|
|
780
829
|
auth=HTTPBasicAuth('kobeteam', config.kobe_password))
|
|
830
|
+
logger.info(f'found file "{fits_file}" on server')
|
|
781
831
|
|
|
782
832
|
if resp.status_code != 200:
|
|
783
833
|
# something went wrong, try to extract the file by downloading the
|
|
@@ -983,13 +1033,18 @@ class RV:
|
|
|
983
1033
|
|
|
984
1034
|
if load:
|
|
985
1035
|
try:
|
|
986
|
-
from os.path import basename, join
|
|
1036
|
+
from os.path import basename, join, exists
|
|
987
1037
|
from .utils import sanitize_path
|
|
988
1038
|
import iCCF
|
|
989
1039
|
downloaded = [
|
|
990
1040
|
sanitize_path(join(directory, basename(f).replace('.fits', '_CCF_A.fits')))
|
|
991
1041
|
for f in files[:limit]
|
|
992
1042
|
]
|
|
1043
|
+
downloaded = [
|
|
1044
|
+
skysub
|
|
1045
|
+
if exists(skysub := f.replace('CCF_A.fits', 'CCF_SKYSUB_A.fits')) else f
|
|
1046
|
+
for f in downloaded
|
|
1047
|
+
]
|
|
993
1048
|
if self.verbose:
|
|
994
1049
|
logger.info('loading the CCF(s) into `.CCF` attribute')
|
|
995
1050
|
|
|
@@ -1063,12 +1118,45 @@ class RV:
|
|
|
1063
1118
|
do_download_filetype('S2D', files[:limit], directory, verbose=self.verbose, **kwargs)
|
|
1064
1119
|
|
|
1065
1120
|
|
|
1066
|
-
from .plots import plot, plot_fwhm,
|
|
1067
|
-
from .plots import gls, gls_fwhm,
|
|
1121
|
+
from .plots import plot, plot_fwhm, plot_bispan, plot_contrast, plot_rhk, plot_berv, plot_quantity
|
|
1122
|
+
from .plots import gls, gls_fwhm, gls_bispan, gls_rhk, gls_quantity, window_function
|
|
1068
1123
|
from .reports import report
|
|
1069
1124
|
|
|
1070
1125
|
from .instrument_specific import known_issues
|
|
1071
1126
|
|
|
1127
|
+
def change_instrument_name(self, old_name, new_name, strict=False):
|
|
1128
|
+
""" Change the name of an instrument
|
|
1129
|
+
|
|
1130
|
+
Args:
|
|
1131
|
+
old_name (str):
|
|
1132
|
+
The old name of the instrument
|
|
1133
|
+
new_name (str):
|
|
1134
|
+
The new name of the instrument, or postfix if `strict` is False
|
|
1135
|
+
strict (bool):
|
|
1136
|
+
Whether to match (each) `instrument` exactly
|
|
1137
|
+
"""
|
|
1138
|
+
if new_name == '':
|
|
1139
|
+
if self.verbose:
|
|
1140
|
+
logger.error('new name cannot be empty string')
|
|
1141
|
+
return
|
|
1142
|
+
|
|
1143
|
+
instruments = self._check_instrument(old_name, strict, log=True)
|
|
1144
|
+
if instruments is not None:
|
|
1145
|
+
several = len(instruments) >= 2
|
|
1146
|
+
for instrument in instruments:
|
|
1147
|
+
if several:
|
|
1148
|
+
new_name_instrument = f'{instrument}_{new_name}'
|
|
1149
|
+
else:
|
|
1150
|
+
new_name_instrument = new_name
|
|
1151
|
+
if self.verbose:
|
|
1152
|
+
logger.info(f'Renaming {instrument} to {new_name_instrument}')
|
|
1153
|
+
|
|
1154
|
+
setattr(self, new_name_instrument, getattr(self, instrument))
|
|
1155
|
+
delattr(self, instrument)
|
|
1156
|
+
self.instruments[self.instruments.index(instrument)] = new_name_instrument
|
|
1157
|
+
|
|
1158
|
+
self._build_arrays()
|
|
1159
|
+
|
|
1072
1160
|
|
|
1073
1161
|
def remove_instrument(self, instrument, strict=False):
|
|
1074
1162
|
""" Remove all observations from one instrument
|
|
@@ -1697,8 +1785,52 @@ class RV:
|
|
|
1697
1785
|
if config.return_self:
|
|
1698
1786
|
return self
|
|
1699
1787
|
|
|
1788
|
+
def detrend(self, degree=1):
|
|
1789
|
+
""" Detrend the RVs of all instruments """
|
|
1790
|
+
instrument_indices = np.unique_inverse(self.instrument_array).inverse_indices
|
|
1791
|
+
def fun(p, t, degree, ninstruments, just_model=False, index=None):
|
|
1792
|
+
polyp, offsets = p[:degree], p[-ninstruments:]
|
|
1793
|
+
polyp = np.r_[polyp, 0.0]
|
|
1794
|
+
if index is None:
|
|
1795
|
+
model = offsets[instrument_indices] + np.polyval(polyp, t)
|
|
1796
|
+
else:
|
|
1797
|
+
model = offsets[index] + np.polyval(polyp, t)
|
|
1798
|
+
if just_model:
|
|
1799
|
+
return model
|
|
1800
|
+
return self.mvrad - model
|
|
1801
|
+
coef = np.polyfit(self.mtime, self.mvrad, degree)
|
|
1802
|
+
x0 = np.append(coef, [0.0] * (len(self.instruments) - 1))
|
|
1803
|
+
print(x0)
|
|
1804
|
+
fun(x0, self.mtime, degree, len(self.instruments))
|
|
1805
|
+
from scipy.optimize import leastsq
|
|
1806
|
+
xbest, _ = leastsq(fun, x0, args=(self.mtime, degree, len(self.instruments)))
|
|
1807
|
+
|
|
1808
|
+
fig, ax = self.plot()
|
|
1809
|
+
ax.remove()
|
|
1810
|
+
ax = fig.add_subplot(2, 1, 1)
|
|
1811
|
+
self.plot(ax=ax)
|
|
1812
|
+
for i, inst in enumerate(self.instruments):
|
|
1813
|
+
s = getattr(self, inst)
|
|
1814
|
+
ax.plot(s.time, fun(xbest, s.time, degree, len(self.instruments), just_model=True, index=i),
|
|
1815
|
+
color=f'C{i}')
|
|
1816
|
+
ax.set_title('original', loc='left', fontsize=10)
|
|
1817
|
+
ax.set_title(f'coefficients: {xbest[:degree]}', loc='right', fontsize=10)
|
|
1818
|
+
|
|
1819
|
+
self.add_to_vrad(-fun(xbest, self.time, degree, len(self.instruments), just_model=True))
|
|
1820
|
+
ax = fig.add_subplot(2, 1, 2)
|
|
1821
|
+
self.plot(ax=ax)
|
|
1822
|
+
ax.set_title('detrended', loc='left', fontsize=10)
|
|
1823
|
+
|
|
1824
|
+
# axs[0].plot(self.time, fun(xbest, self.time, degree, len(self.instruments), just_model=True))
|
|
1825
|
+
# axs[1].errorbar(self.mtime, fun(xbest, self.mtime, degree, len(self.instruments)), self.msvrad, fmt='o')
|
|
1826
|
+
|
|
1827
|
+
return
|
|
1828
|
+
|
|
1829
|
+
|
|
1830
|
+
|
|
1831
|
+
|
|
1700
1832
|
def add_to_vrad(self, values):
|
|
1701
|
-
""" Add a value
|
|
1833
|
+
""" Add a value or array of values to the RVs of all instruments """
|
|
1702
1834
|
values = np.atleast_1d(values)
|
|
1703
1835
|
if values.size == 1:
|
|
1704
1836
|
values = np.full_like(self.vrad, values)
|
|
@@ -1723,7 +1855,7 @@ class RV:
|
|
|
1723
1855
|
|
|
1724
1856
|
def add_to_quantity(self, quantity, values):
|
|
1725
1857
|
"""
|
|
1726
|
-
Add a value
|
|
1858
|
+
Add a value or array of values to the given quantity of all instruments
|
|
1727
1859
|
"""
|
|
1728
1860
|
if not hasattr(self, quantity):
|
|
1729
1861
|
logger.error(f"cannot find '{quantity}' attribute")
|
|
@@ -1742,6 +1874,75 @@ class RV:
|
|
|
1742
1874
|
setattr(s, quantity, getattr(s, quantity) + values[mask])
|
|
1743
1875
|
self._build_arrays()
|
|
1744
1876
|
|
|
1877
|
+
def replace_vrad(self, values):
|
|
1878
|
+
""" Replace the RVs of all instruments with a value or array of values """
|
|
1879
|
+
values = np.atleast_1d(values)
|
|
1880
|
+
if values.size == 1:
|
|
1881
|
+
values = np.full_like(self.vrad, values)
|
|
1882
|
+
|
|
1883
|
+
masked = False
|
|
1884
|
+
if values.size != self.vrad.size:
|
|
1885
|
+
if values.size == self.mvrad.size:
|
|
1886
|
+
logger.warning('adding to masked RVs only')
|
|
1887
|
+
masked = True
|
|
1888
|
+
else:
|
|
1889
|
+
raise ValueError(f"incompatible sizes: len(values) must equal self.N, got {values.size} != {self.vrad.size}")
|
|
1890
|
+
|
|
1891
|
+
for inst in self.instruments:
|
|
1892
|
+
s = getattr(self, inst)
|
|
1893
|
+
if masked:
|
|
1894
|
+
mask = self.instrument_array[self.mask] == inst
|
|
1895
|
+
s.vrad[s.mask] = values[mask]
|
|
1896
|
+
else:
|
|
1897
|
+
mask = self.instrument_array == inst
|
|
1898
|
+
s.vrad = values[mask]
|
|
1899
|
+
self._build_arrays()
|
|
1900
|
+
|
|
1901
|
+
def replace_svrad(self, values):
|
|
1902
|
+
""" Replace the RV uncertainties of all instruments with a value or array of values """
|
|
1903
|
+
values = np.atleast_1d(values)
|
|
1904
|
+
if values.size == 1:
|
|
1905
|
+
values = np.full_like(self.svrad, values)
|
|
1906
|
+
|
|
1907
|
+
masked = False
|
|
1908
|
+
if values.size != self.svrad.size:
|
|
1909
|
+
if values.size == self.msvrad.size:
|
|
1910
|
+
logger.warning('adding to masked RV uncertainties only')
|
|
1911
|
+
masked = True
|
|
1912
|
+
else:
|
|
1913
|
+
raise ValueError(f"incompatible sizes: len(values) must equal self.N, got {values.size} != {self.svrad.size}")
|
|
1914
|
+
|
|
1915
|
+
for inst in self.instruments:
|
|
1916
|
+
s = getattr(self, inst)
|
|
1917
|
+
if masked:
|
|
1918
|
+
mask = self.instrument_array[self.mask] == inst
|
|
1919
|
+
s.svrad[s.mask] = values[mask]
|
|
1920
|
+
else:
|
|
1921
|
+
mask = self.instrument_array == inst
|
|
1922
|
+
s.svrad = values[mask]
|
|
1923
|
+
self._build_arrays()
|
|
1924
|
+
|
|
1925
|
+
def replace_quantity(self, quantity, values):
|
|
1926
|
+
""" Replace the given quantity of all instruments by a value or array of values """
|
|
1927
|
+
if not hasattr(self, quantity):
|
|
1928
|
+
logger.error(f"cannot find '{quantity}' attribute")
|
|
1929
|
+
return
|
|
1930
|
+
q = getattr(self, quantity)
|
|
1931
|
+
|
|
1932
|
+
values = np.atleast_1d(values)
|
|
1933
|
+
if values.size == 1:
|
|
1934
|
+
values = np.full_like(q, values)
|
|
1935
|
+
if values.size != q.size:
|
|
1936
|
+
raise ValueError(f"incompatible sizes: len(values) must equal self.N, got {values.size} != {q.size}")
|
|
1937
|
+
|
|
1938
|
+
for inst in self.instruments:
|
|
1939
|
+
s = getattr(self, inst)
|
|
1940
|
+
mask = self.instrument_array == inst
|
|
1941
|
+
setattr(s, quantity, values[mask])
|
|
1942
|
+
self._build_arrays()
|
|
1943
|
+
|
|
1944
|
+
|
|
1945
|
+
|
|
1745
1946
|
def change_units(self, new_units):
|
|
1746
1947
|
possible = {'m/s': 'm/s', 'km/s': 'km/s', 'ms': 'm/s', 'kms': 'km/s'}
|
|
1747
1948
|
if new_units not in possible:
|
|
@@ -1809,20 +2010,18 @@ class RV:
|
|
|
1809
2010
|
""" Sort instruments by first or last observation date.
|
|
1810
2011
|
|
|
1811
2012
|
Args:
|
|
1812
|
-
by_first_observation (bool, optional):
|
|
2013
|
+
by_first_observation (bool, optional, default=True):
|
|
1813
2014
|
Sort by first observation date.
|
|
1814
|
-
by_last_observation (bool, optional):
|
|
1815
|
-
Sort by last observation
|
|
2015
|
+
by_last_observation (bool, optional, default=False):
|
|
2016
|
+
Sort by last observation date.
|
|
1816
2017
|
"""
|
|
1817
2018
|
if by_last_observation:
|
|
1818
2019
|
by_first_observation = False
|
|
1819
2020
|
if by_first_observation:
|
|
1820
|
-
|
|
1821
|
-
self.instruments = sorted(self.instruments, key=fun)
|
|
2021
|
+
self.instruments = sorted(self.instruments, key=lambda i: getattr(self, i).time.min())
|
|
1822
2022
|
self._build_arrays()
|
|
1823
2023
|
if by_last_observation:
|
|
1824
|
-
|
|
1825
|
-
self.instruments = sorted(self.instruments, key=fun)
|
|
2024
|
+
self.instruments = sorted(self.instruments, key=lambda i: getattr(self, i).time.max())
|
|
1826
2025
|
self._build_arrays()
|
|
1827
2026
|
|
|
1828
2027
|
|
|
@@ -1841,7 +2040,7 @@ class RV:
|
|
|
1841
2040
|
Postfix to add to the filenames ([star]_[instrument]_[postfix].rdb).
|
|
1842
2041
|
save_nans (bool, optional)
|
|
1843
2042
|
Whether to save NaN values in the indicators, if they exist. If
|
|
1844
|
-
False, the full observation is not saved.
|
|
2043
|
+
False, the full observation which contains NaN values is not saved.
|
|
1845
2044
|
"""
|
|
1846
2045
|
star_name = self.star.replace(' ', '')
|
|
1847
2046
|
|
|
@@ -1864,7 +2063,7 @@ class RV:
|
|
|
1864
2063
|
|
|
1865
2064
|
if full:
|
|
1866
2065
|
if save_masked:
|
|
1867
|
-
|
|
2066
|
+
arrays = [
|
|
1868
2067
|
_s.time, _s.vrad, _s.svrad,
|
|
1869
2068
|
_s.fwhm, _s.fwhm_err,
|
|
1870
2069
|
_s.bispan, _s.bispan_err,
|
|
@@ -1873,7 +2072,7 @@ class RV:
|
|
|
1873
2072
|
_s.berv,
|
|
1874
2073
|
]
|
|
1875
2074
|
else:
|
|
1876
|
-
|
|
2075
|
+
arrays = [
|
|
1877
2076
|
_s.mtime, _s.mvrad, _s.msvrad,
|
|
1878
2077
|
_s.fwhm[_s.mask], _s.fwhm_err[_s.mask],
|
|
1879
2078
|
_s.bispan[_s.mask], _s.bispan_err[_s.mask],
|
|
@@ -1882,12 +2081,13 @@ class RV:
|
|
|
1882
2081
|
_s.berv[_s.mask],
|
|
1883
2082
|
]
|
|
1884
2083
|
if not save_nans:
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
2084
|
+
raise NotImplementedError
|
|
2085
|
+
# if np.isnan(d).any():
|
|
2086
|
+
# # remove observations where any of the indicators are # NaN
|
|
2087
|
+
# nan_mask = np.isnan(d[:, 3:]).any(axis=1)
|
|
2088
|
+
# d = d[~nan_mask]
|
|
2089
|
+
# if self.verbose:
|
|
2090
|
+
# logger.warning(f'masking {nan_mask.sum()} observations with NaN in indicators')
|
|
1891
2091
|
|
|
1892
2092
|
header = '\t'.join(['bjd', 'vrad', 'svrad',
|
|
1893
2093
|
'fwhm', 'sig_fwhm',
|
|
@@ -1901,9 +2101,11 @@ class RV:
|
|
|
1901
2101
|
|
|
1902
2102
|
else:
|
|
1903
2103
|
if save_masked:
|
|
1904
|
-
|
|
2104
|
+
arrays = [_s.time, _s.vrad, _s.svrad]
|
|
1905
2105
|
else:
|
|
1906
|
-
|
|
2106
|
+
arrays = [_s.mtime, _s.mvrad, _s.msvrad]
|
|
2107
|
+
|
|
2108
|
+
# d = np.stack(arrays, axis=1)
|
|
1907
2109
|
header = 'bjd\tvrad\tsvrad\n---\t----\t-----'
|
|
1908
2110
|
|
|
1909
2111
|
file = f'{star_name}_{inst}.rdb'
|
|
@@ -1913,7 +2115,17 @@ class RV:
|
|
|
1913
2115
|
files.append(file)
|
|
1914
2116
|
file = os.path.join(directory, file)
|
|
1915
2117
|
|
|
1916
|
-
|
|
2118
|
+
N = len(arrays[0])
|
|
2119
|
+
with open(file, 'w') as f:
|
|
2120
|
+
f.write(header + '\n')
|
|
2121
|
+
for i in range(N):
|
|
2122
|
+
for j, a in enumerate(arrays):
|
|
2123
|
+
f.write(str(a[i]))
|
|
2124
|
+
if j < len(arrays) - 1:
|
|
2125
|
+
f.write('\t')
|
|
2126
|
+
f.write('\n')
|
|
2127
|
+
|
|
2128
|
+
# np.savetxt(file, d, header=header, delimiter='\t', comments='', fmt='%f')
|
|
1917
2129
|
|
|
1918
2130
|
if self.verbose:
|
|
1919
2131
|
logger.info(f'saving to {file}')
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import os
|
|
3
|
+
from numpy import isnan
|
|
4
|
+
|
|
5
|
+
@pytest.fixture
|
|
6
|
+
def change_test_dir(request):
|
|
7
|
+
os.chdir(request.fspath.dirname)
|
|
8
|
+
yield
|
|
9
|
+
os.chdir(request.config.invocation_params.dir)
|
|
10
|
+
|
|
11
|
+
def test_from_rdb(change_test_dir):
|
|
12
|
+
from arvi import RV
|
|
13
|
+
s = RV.from_rdb('./HD10700-Bcor_ESPRESSO18.rdb', verbose=False)
|
|
14
|
+
assert s.star == 'HD10700_Bcor'
|
|
15
|
+
assert s.instruments == ['ESPRESSO18']
|
|
16
|
+
assert s.N == 1
|
|
17
|
+
assert s.fwhm.shape == (1,)
|
|
18
|
+
assert (s.fwhm == 0).all()
|
|
19
|
+
assert (s.bispan == 0).all()
|
|
20
|
+
assert isnan(s.rhk).all()
|
|
21
|
+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|