arvi 0.1.10__py3-none-any.whl → 0.1.12__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 +1 -0
- arvi/ariadne_wrapper.py +70 -0
- arvi/config.py +7 -0
- arvi/dace_wrapper.py +79 -54
- arvi/extra_data.py +11 -2
- arvi/gaia_wrapper.py +94 -0
- arvi/plots.py +107 -27
- arvi/programs.py +41 -18
- arvi/simbad_wrapper.py +22 -1
- arvi/stats.py +16 -2
- arvi/timeseries.py +188 -81
- arvi/translations.py +11 -0
- arvi/utils.py +14 -0
- {arvi-0.1.10.dist-info → arvi-0.1.12.dist-info}/METADATA +1 -1
- arvi-0.1.12.dist-info/RECORD +31 -0
- arvi-0.1.10.dist-info/RECORD +0 -29
- {arvi-0.1.10.dist-info → arvi-0.1.12.dist-info}/LICENSE +0 -0
- {arvi-0.1.10.dist-info → arvi-0.1.12.dist-info}/WHEEL +0 -0
- {arvi-0.1.10.dist-info → arvi-0.1.12.dist-info}/top_level.txt +0 -0
arvi/__init__.py
CHANGED
arvi/ariadne_wrapper.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
from matplotlib import pyplot as plt
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
from astroARIADNE.star import Star
|
|
7
|
+
from astroARIADNE.fitter import Fitter
|
|
8
|
+
except ImportError:
|
|
9
|
+
print('This module requires astroARIADNE. Install with `pip install astroARIADNE`')
|
|
10
|
+
sys.exit(0)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def run_ariadne(self, fit=True, plot=True, priors={},
|
|
14
|
+
models = ('phoenix', 'btsettl', 'btnextgen', 'btcond', 'kurucz', 'ck04'),
|
|
15
|
+
nlive=300, dlogz=1, threads=6, dynamic=False, **kwargs):
|
|
16
|
+
if hasattr(self, 'gaia'):
|
|
17
|
+
s = Star(self.star, self.gaia.ra, self.gaia.dec, g_id=self.gaia.dr3_id,
|
|
18
|
+
search_radius=1)
|
|
19
|
+
else:
|
|
20
|
+
s = Star(self.star, self.simbad.ra, self.simbad.dec, g_id=self.simbad.gaia_id,
|
|
21
|
+
search_radius=1)
|
|
22
|
+
|
|
23
|
+
out_folder = f'{self.star}_ariadne'
|
|
24
|
+
|
|
25
|
+
setup = dict(engine='dynesty', nlive=nlive, dlogz=dlogz,
|
|
26
|
+
bound='multi', sample='auto', threads=threads, dynamic=dynamic)
|
|
27
|
+
setup = list(setup.values())
|
|
28
|
+
|
|
29
|
+
f = Fitter()
|
|
30
|
+
f.star = s
|
|
31
|
+
f.setup = setup
|
|
32
|
+
f.av_law = 'fitzpatrick'
|
|
33
|
+
f.out_folder = out_folder
|
|
34
|
+
f.bma = True
|
|
35
|
+
f.models = models
|
|
36
|
+
f.n_samples = 10_000
|
|
37
|
+
|
|
38
|
+
f.prior_setup = {
|
|
39
|
+
'teff': priors.get('teff', ('default')),
|
|
40
|
+
'logg': ('default'),
|
|
41
|
+
'z': priors.get('feh', ('default')),
|
|
42
|
+
'dist': ('default'),
|
|
43
|
+
'rad': ('default'),
|
|
44
|
+
'Av': ('default')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if fit:
|
|
48
|
+
f.initialize()
|
|
49
|
+
f.fit_bma()
|
|
50
|
+
|
|
51
|
+
if plot:
|
|
52
|
+
from pkg_resources import resource_filename
|
|
53
|
+
from astroARIADNE.plotter import SEDPlotter
|
|
54
|
+
modelsdir = resource_filename('astroARIADNE', 'Datafiles/models')
|
|
55
|
+
artist = SEDPlotter(os.path.join(out_folder, 'BMA.pkl'), out_folder, models_dir=modelsdir)
|
|
56
|
+
|
|
57
|
+
artist.plot_SED_no_model()
|
|
58
|
+
try:
|
|
59
|
+
artist.plot_SED()
|
|
60
|
+
except FileNotFoundError as e:
|
|
61
|
+
print('No model found:', e)
|
|
62
|
+
except IndexError as e:
|
|
63
|
+
print('Error!')
|
|
64
|
+
artist.plot_bma_hist()
|
|
65
|
+
artist.plot_bma_HR(10)
|
|
66
|
+
artist.plot_corner()
|
|
67
|
+
plt.close('all')
|
|
68
|
+
return s, f, artist
|
|
69
|
+
|
|
70
|
+
return s, f
|
arvi/config.py
CHANGED
arvi/dace_wrapper.py
CHANGED
|
@@ -5,10 +5,15 @@ import numpy as np
|
|
|
5
5
|
from dace_query import DaceClass
|
|
6
6
|
from dace_query.spectroscopy import SpectroscopyClass, Spectroscopy as default_Spectroscopy
|
|
7
7
|
from .setup_logger import logger
|
|
8
|
-
from .utils import create_directory, all_logging_disabled, stdout_disabled
|
|
8
|
+
from .utils import create_directory, all_logging_disabled, stdout_disabled, tqdm
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def load_spectroscopy() -> SpectroscopyClass:
|
|
12
|
+
from .config import request_as_public
|
|
13
|
+
if request_as_public:
|
|
14
|
+
with all_logging_disabled():
|
|
15
|
+
dace = DaceClass(dace_rc_config_path='none')
|
|
16
|
+
return SpectroscopyClass(dace_instance=dace)
|
|
12
17
|
if 'DACERC' in os.environ:
|
|
13
18
|
dace = DaceClass(dace_rc_config_path=os.environ['DACERC'])
|
|
14
19
|
return SpectroscopyClass(dace_instance=dace)
|
|
@@ -156,6 +161,13 @@ def check_existing(output_directory, files, type):
|
|
|
156
161
|
f.partition('.fits')[0] for f in os.listdir(output_directory)
|
|
157
162
|
if type in f
|
|
158
163
|
]
|
|
164
|
+
|
|
165
|
+
# also check for lowercase type
|
|
166
|
+
existing += [
|
|
167
|
+
f.partition('.fits')[0] for f in os.listdir(output_directory)
|
|
168
|
+
if type.lower() in f
|
|
169
|
+
]
|
|
170
|
+
|
|
159
171
|
if os.name == 'nt': # on Windows, be careful with ':' in filename
|
|
160
172
|
import re
|
|
161
173
|
existing = [re.sub(r'T(\d+)_(\d+)_(\d+)', r'T\1:\2:\3', f) for f in existing]
|
|
@@ -174,9 +186,9 @@ def check_existing(output_directory, files, type):
|
|
|
174
186
|
def download(files, type, output_directory):
|
|
175
187
|
""" Download files from DACE """
|
|
176
188
|
Spectroscopy = load_spectroscopy()
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
189
|
+
with stdout_disabled(), all_logging_disabled():
|
|
190
|
+
Spectroscopy.download_files(files, file_type=type.lower(),
|
|
191
|
+
output_directory=output_directory)
|
|
180
192
|
|
|
181
193
|
def extract_fits(output_directory):
|
|
182
194
|
""" Extract fits files from tar.gz file """
|
|
@@ -194,86 +206,99 @@ def extract_fits(output_directory):
|
|
|
194
206
|
return files
|
|
195
207
|
|
|
196
208
|
|
|
197
|
-
def
|
|
198
|
-
|
|
209
|
+
def do_download_filetype(type, raw_files, output_directory, clobber=False,
|
|
210
|
+
verbose=True, chunk_size=20):
|
|
211
|
+
""" Download CCFs / S1Ds / S2Ds from DACE """
|
|
199
212
|
raw_files = np.atleast_1d(raw_files)
|
|
200
213
|
|
|
201
214
|
create_directory(output_directory)
|
|
202
215
|
|
|
203
216
|
# check existing files to avoid re-downloading
|
|
204
217
|
if not clobber:
|
|
205
|
-
raw_files = check_existing(output_directory, raw_files,
|
|
218
|
+
raw_files = check_existing(output_directory, raw_files, type)
|
|
219
|
+
|
|
220
|
+
n = raw_files.size
|
|
206
221
|
|
|
207
222
|
# any file left to download?
|
|
208
|
-
if
|
|
223
|
+
if n == 0:
|
|
209
224
|
if verbose:
|
|
210
225
|
logger.info('no files to download')
|
|
211
226
|
return
|
|
212
227
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
download(raw_files, 'ccf', output_directory)
|
|
228
|
+
# avoid an empty chunk
|
|
229
|
+
if chunk_size > n:
|
|
230
|
+
chunk_size = n
|
|
218
231
|
|
|
219
232
|
if verbose:
|
|
220
|
-
|
|
233
|
+
if chunk_size < n:
|
|
234
|
+
msg = f"Downloading {n} {type}s "
|
|
235
|
+
msg += f"(in chunks of {chunk_size}) "
|
|
236
|
+
msg += f"into '{output_directory}'..."
|
|
237
|
+
logger.info(msg)
|
|
238
|
+
else:
|
|
239
|
+
msg = f"Downloading {n} {type}s into '{output_directory}'..."
|
|
240
|
+
logger.info(msg)
|
|
221
241
|
|
|
222
|
-
|
|
242
|
+
iterator = [raw_files[i:i + chunk_size] for i in range(0, n, chunk_size)]
|
|
243
|
+
for files in tqdm(iterator, total=len(iterator)):
|
|
244
|
+
download(files, type, output_directory)
|
|
245
|
+
extract_fits(output_directory)
|
|
223
246
|
|
|
247
|
+
logger.info('Extracted .fits files')
|
|
224
248
|
|
|
225
|
-
def do_download_s1d(raw_files, output_directory, clobber=False, verbose=True):
|
|
226
|
-
""" Download S1Ds from DACE """
|
|
227
|
-
raw_files = np.atleast_1d(raw_files)
|
|
228
249
|
|
|
229
|
-
|
|
250
|
+
# def do_download_s1d(raw_files, output_directory, clobber=False, verbose=True):
|
|
251
|
+
# """ Download S1Ds from DACE """
|
|
252
|
+
# raw_files = np.atleast_1d(raw_files)
|
|
230
253
|
|
|
231
|
-
|
|
232
|
-
if not clobber:
|
|
233
|
-
raw_files = check_existing(output_directory, raw_files, 'S1D')
|
|
254
|
+
# create_directory(output_directory)
|
|
234
255
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
logger.info('no files to download')
|
|
239
|
-
return
|
|
256
|
+
# # check existing files to avoid re-downloading
|
|
257
|
+
# if not clobber:
|
|
258
|
+
# raw_files = check_existing(output_directory, raw_files, 'S1D')
|
|
240
259
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
260
|
+
# # any file left to download?
|
|
261
|
+
# if raw_files.size == 0:
|
|
262
|
+
# if verbose:
|
|
263
|
+
# logger.info('no files to download')
|
|
264
|
+
# return
|
|
244
265
|
|
|
245
|
-
|
|
266
|
+
# if verbose:
|
|
267
|
+
# n = raw_files.size
|
|
268
|
+
# logger.info(f"Downloading {n} S1Ds into '{output_directory}'...")
|
|
246
269
|
|
|
247
|
-
|
|
248
|
-
logger.info('Extracting .fits files')
|
|
270
|
+
# download(raw_files, 's1d', output_directory)
|
|
249
271
|
|
|
250
|
-
|
|
272
|
+
# if verbose:
|
|
273
|
+
# logger.info('Extracting .fits files')
|
|
251
274
|
|
|
275
|
+
# extract_fits(output_directory)
|
|
252
276
|
|
|
253
|
-
def do_download_s2d(raw_files, output_directory, clobber=False, verbose=True):
|
|
254
|
-
""" Download S2Ds from DACE """
|
|
255
|
-
raw_files = np.atleast_1d(raw_files)
|
|
256
277
|
|
|
257
|
-
|
|
278
|
+
# def do_download_s2d(raw_files, output_directory, clobber=False, verbose=True):
|
|
279
|
+
# """ Download S2Ds from DACE """
|
|
280
|
+
# raw_files = np.atleast_1d(raw_files)
|
|
258
281
|
|
|
259
|
-
|
|
260
|
-
if not clobber:
|
|
261
|
-
raw_files = check_existing(output_directory, raw_files, 'S2D')
|
|
282
|
+
# create_directory(output_directory)
|
|
262
283
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
logger.info('no files to download')
|
|
267
|
-
return
|
|
284
|
+
# # check existing files to avoid re-downloading
|
|
285
|
+
# if not clobber:
|
|
286
|
+
# raw_files = check_existing(output_directory, raw_files, 'S2D')
|
|
268
287
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
288
|
+
# # any file left to download?
|
|
289
|
+
# if raw_files.size == 0:
|
|
290
|
+
# if verbose:
|
|
291
|
+
# logger.info('no files to download')
|
|
292
|
+
# return
|
|
272
293
|
|
|
273
|
-
|
|
294
|
+
# if verbose:
|
|
295
|
+
# n = raw_files.size
|
|
296
|
+
# logger.info(f"Downloading {n} S2Ds into '{output_directory}'...")
|
|
274
297
|
|
|
275
|
-
|
|
276
|
-
|
|
298
|
+
# download(raw_files, 's2d', output_directory)
|
|
299
|
+
|
|
300
|
+
# if verbose:
|
|
301
|
+
# logger.info('Extracting .fits files')
|
|
277
302
|
|
|
278
|
-
|
|
279
|
-
|
|
303
|
+
# extracted_files = extract_fits(output_directory)
|
|
304
|
+
# return extracted_files
|
arvi/extra_data.py
CHANGED
|
@@ -19,15 +19,24 @@ def get_extra_data(star, instrument=None, path=None, verbose=True):
|
|
|
19
19
|
# print(metadata)
|
|
20
20
|
|
|
21
21
|
files = glob(os.path.join(path, star + '*'))
|
|
22
|
+
files = [f for f in files if os.path.isfile(f)]
|
|
23
|
+
files = [f for f in files if not os.path.basename(f).endswith('.zip')]
|
|
24
|
+
|
|
22
25
|
if len(files) == 0:
|
|
23
26
|
raise FileNotFoundError
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
def get_instruments(files):
|
|
29
|
+
instruments = [os.path.basename(f).split('.')[0] for f in files]
|
|
30
|
+
instruments = [i.split('_', maxsplit=1)[1] for i in instruments]
|
|
31
|
+
return instruments
|
|
32
|
+
|
|
33
|
+
instruments = get_instruments(files)
|
|
27
34
|
|
|
28
35
|
if instrument is not None:
|
|
29
36
|
if not any([instrument in i for i in instruments]):
|
|
30
37
|
raise FileNotFoundError
|
|
38
|
+
files = [f for f in files if instrument in f]
|
|
39
|
+
instruments = get_instruments(files)
|
|
31
40
|
|
|
32
41
|
if verbose:
|
|
33
42
|
logger.info(f'loading extra data for {star}')
|
arvi/gaia_wrapper.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from io import StringIO
|
|
3
|
+
from csv import DictReader
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
import requests
|
|
6
|
+
|
|
7
|
+
from astropy.coordinates import SkyCoord
|
|
8
|
+
import pysweetcat
|
|
9
|
+
|
|
10
|
+
DATA_PATH = os.path.dirname(__file__)
|
|
11
|
+
DATA_PATH = os.path.join(DATA_PATH, 'data')
|
|
12
|
+
|
|
13
|
+
QUERY = """
|
|
14
|
+
SELECT TOP 20 gaia_source.designation,gaia_source.source_id,gaia_source.ra,gaia_source.dec,gaia_source.parallax,gaia_source.pmra,gaia_source.pmdec,gaia_source.ruwe,gaia_source.phot_g_mean_mag,gaia_source.bp_rp,gaia_source.radial_velocity,gaia_source.phot_variable_flag,gaia_source.non_single_star,gaia_source.has_xp_continuous,gaia_source.has_xp_sampled,gaia_source.has_rvs,gaia_source.has_epoch_photometry,gaia_source.has_epoch_rv,gaia_source.has_mcmc_gspphot,gaia_source.has_mcmc_msc,gaia_source.teff_gspphot,gaia_source.logg_gspphot,gaia_source.mh_gspphot,gaia_source.distance_gspphot,gaia_source.azero_gspphot,gaia_source.ag_gspphot,gaia_source.ebpminrp_gspphot
|
|
15
|
+
FROM gaiadr3.gaia_source
|
|
16
|
+
WHERE
|
|
17
|
+
CONTAINS(
|
|
18
|
+
POINT('ICRS',gaiadr3.gaia_source.ra,gaiadr3.gaia_source.dec),
|
|
19
|
+
CIRCLE(
|
|
20
|
+
'ICRS',
|
|
21
|
+
COORD1(EPOCH_PROP_POS({ra},{dec},{plx},{pmra},{pmdec},{rv},2000,2016.0)),
|
|
22
|
+
COORD2(EPOCH_PROP_POS({ra},{dec},{plx},{pmra},{pmdec},{rv},2000,2016.0)),
|
|
23
|
+
0.001388888888888889)
|
|
24
|
+
)=1
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def run_query(query):
|
|
28
|
+
url = 'https://gea.esac.esa.int/tap-server/tap/sync'
|
|
29
|
+
data = dict(query=query, request='doQuery', lang='ADQL', format='csv')
|
|
30
|
+
try:
|
|
31
|
+
response = requests.post(url, data=data, timeout=10)
|
|
32
|
+
except requests.ReadTimeout as err:
|
|
33
|
+
raise IndexError(err)
|
|
34
|
+
except requests.ConnectionError as err:
|
|
35
|
+
raise IndexError(err)
|
|
36
|
+
return response.content.decode()
|
|
37
|
+
|
|
38
|
+
def parse_csv(csv):
|
|
39
|
+
reader = DictReader(StringIO(csv))
|
|
40
|
+
return list(reader)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class gaia:
|
|
44
|
+
"""
|
|
45
|
+
A very simple wrapper around a TAP query to gaia for a given target. This
|
|
46
|
+
class simply runs a few TAP queries and stores the result as attributes.
|
|
47
|
+
|
|
48
|
+
Attributes:
|
|
49
|
+
ra (float): right ascension
|
|
50
|
+
dec (float): declination
|
|
51
|
+
coords (SkyCoord): coordinates as a SkyCoord object
|
|
52
|
+
dr3_id (int): Gaia DR3 identifier
|
|
53
|
+
plx (float): parallax
|
|
54
|
+
radial_velocity (float): radial velocity
|
|
55
|
+
"""
|
|
56
|
+
def __init__(self, star:str, simbad=None):
|
|
57
|
+
"""
|
|
58
|
+
Args:
|
|
59
|
+
star (str): The name of the star to query simbad
|
|
60
|
+
"""
|
|
61
|
+
self.star = star
|
|
62
|
+
|
|
63
|
+
if simbad is None:
|
|
64
|
+
from .simbad_wrapper import simbad as Simbad
|
|
65
|
+
simbad = Simbad(star)
|
|
66
|
+
|
|
67
|
+
ra = simbad.ra
|
|
68
|
+
dec = simbad.dec
|
|
69
|
+
plx = simbad.plx
|
|
70
|
+
pmra = simbad.pmra
|
|
71
|
+
pmdec = simbad.pmdec
|
|
72
|
+
rv = simbad.rvz_radvel
|
|
73
|
+
args = dict(ra=ra, dec=dec, plx=plx, pmra=pmra, pmdec=pmdec, rv=rv)
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
table1 = run_query(query=QUERY.format(**args))
|
|
77
|
+
results = parse_csv(table1)[0]
|
|
78
|
+
except IndexError:
|
|
79
|
+
raise ValueError(f'Gaia query for {star} failed')
|
|
80
|
+
|
|
81
|
+
self.dr3_id = int(results['source_id'])
|
|
82
|
+
|
|
83
|
+
self.ra = float(results['ra'])
|
|
84
|
+
self.dec = float(results['dec'])
|
|
85
|
+
self.pmra = float(results['pmra'])
|
|
86
|
+
self.pmdec = float(results['pmdec'])
|
|
87
|
+
self.coords = SkyCoord(self.ra, self.dec, unit='deg')
|
|
88
|
+
self.plx = float(results['parallax'])
|
|
89
|
+
self.radial_velocity = float(results['radial_velocity'])
|
|
90
|
+
|
|
91
|
+
return
|
|
92
|
+
|
|
93
|
+
def __repr__(self):
|
|
94
|
+
return f'{self.star} (DR3 id={self.dr3_id})'
|
arvi/plots.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from functools import partial, partialmethod
|
|
3
|
+
from itertools import cycle
|
|
3
4
|
|
|
5
|
+
import matplotlib.collections
|
|
4
6
|
import numpy as np
|
|
5
7
|
import matplotlib
|
|
6
8
|
import matplotlib.pyplot as plt
|
|
@@ -15,7 +17,7 @@ from . import config
|
|
|
15
17
|
|
|
16
18
|
def plot(self, ax=None, show_masked=False, instrument=None, time_offset=0,
|
|
17
19
|
remove_50000=False, tooltips=False, label=None, N_in_label=False,
|
|
18
|
-
versus_n=False, show_histogram=False, **kwargs):
|
|
20
|
+
versus_n=False, show_histogram=False, bw=False, **kwargs):
|
|
19
21
|
""" Plot the RVs
|
|
20
22
|
|
|
21
23
|
Args:
|
|
@@ -38,6 +40,8 @@ def plot(self, ax=None, show_masked=False, instrument=None, time_offset=0,
|
|
|
38
40
|
show_histogram (bool, optional)
|
|
39
41
|
Whether to show a panel with the RV histograms (per intrument).
|
|
40
42
|
Defaults to False.
|
|
43
|
+
bw (bool, optional):
|
|
44
|
+
Adapt plot to black and white. Defaults to False.
|
|
41
45
|
|
|
42
46
|
Returns:
|
|
43
47
|
Figure: the figure
|
|
@@ -58,7 +62,7 @@ def plot(self, ax=None, show_masked=False, instrument=None, time_offset=0,
|
|
|
58
62
|
ax, axh = ax
|
|
59
63
|
fig = ax.figure
|
|
60
64
|
|
|
61
|
-
|
|
65
|
+
|
|
62
66
|
kwargs.setdefault('ls', '')
|
|
63
67
|
kwargs.setdefault('capsize', 0)
|
|
64
68
|
kwargs.setdefault('ms', 4)
|
|
@@ -66,10 +70,22 @@ def plot(self, ax=None, show_masked=False, instrument=None, time_offset=0,
|
|
|
66
70
|
if remove_50000:
|
|
67
71
|
time_offset = 50000
|
|
68
72
|
|
|
69
|
-
|
|
73
|
+
strict = kwargs.pop('strict', False)
|
|
74
|
+
instruments = self._check_instrument(instrument, strict=strict)
|
|
75
|
+
|
|
76
|
+
if bw:
|
|
77
|
+
markers = cycle(('o', 'P', 's', '^', '*'))
|
|
78
|
+
else:
|
|
79
|
+
markers = cycle(('o',) * len(instruments))
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
zorders = cycle(-np.argsort([getattr(self, i).error for i in instruments])[::-1])
|
|
83
|
+
except AttributeError:
|
|
84
|
+
zorders = cycle([1] * len(instruments))
|
|
70
85
|
|
|
71
86
|
cursors = {}
|
|
72
|
-
|
|
87
|
+
containers = {}
|
|
88
|
+
for _i, inst in enumerate(instruments):
|
|
73
89
|
s = self if self._child else getattr(self, inst)
|
|
74
90
|
if s.mask.sum() == 0:
|
|
75
91
|
continue
|
|
@@ -81,37 +97,42 @@ def plot(self, ax=None, show_masked=False, instrument=None, time_offset=0,
|
|
|
81
97
|
p = p.replace('_', '.')
|
|
82
98
|
_label = f'{i}-{p}'
|
|
83
99
|
else:
|
|
84
|
-
|
|
100
|
+
if isinstance(label, list):
|
|
101
|
+
_label = label[_i]
|
|
102
|
+
else:
|
|
103
|
+
_label = label
|
|
85
104
|
|
|
86
105
|
if versus_n:
|
|
87
|
-
container = ax.errorbar(np.arange(1, s.mtime.size + 1),
|
|
88
|
-
|
|
106
|
+
container = ax.errorbar(np.arange(1, s.mtime.size + 1), s.mvrad, s.msvrad,
|
|
107
|
+
label=_label, picker=True, marker=next(markers), zorder=next(zorders),
|
|
108
|
+
**kwargs)
|
|
89
109
|
else:
|
|
90
|
-
container = ax.errorbar(s.mtime - time_offset,
|
|
91
|
-
|
|
110
|
+
container = ax.errorbar(s.mtime - time_offset, s.mvrad, s.msvrad,
|
|
111
|
+
label=_label, picker=True, marker=next(markers), zorder=next(zorders),
|
|
112
|
+
**kwargs)
|
|
113
|
+
|
|
114
|
+
containers[inst] = list(container)
|
|
92
115
|
|
|
93
116
|
if show_histogram:
|
|
94
117
|
kw = dict(histtype='step', bins='doane', orientation='horizontal')
|
|
95
118
|
hlabel = f'{s.mvrad.std():.2f} {self.units}'
|
|
96
119
|
axh.hist(s.mvrad, **kw, label=hlabel)
|
|
97
120
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
sel.
|
|
107
|
-
text
|
|
108
|
-
|
|
109
|
-
text +=
|
|
110
|
-
#
|
|
111
|
-
# text += '
|
|
112
|
-
#
|
|
113
|
-
# text += f'mask: {_s.ccf_mask[sel.index]}'
|
|
114
|
-
sel.annotation.set_text(text)
|
|
121
|
+
# cursors[inst] = crsr = mplcursors.cursor(container, multiple=False)
|
|
122
|
+
# @crsr.connect("add")
|
|
123
|
+
# def _(sel):
|
|
124
|
+
# inst = sel.artist.get_label()
|
|
125
|
+
# _s = getattr(self, inst)
|
|
126
|
+
# vrad, svrad = _s.vrad[sel.index], _s.svrad[sel.index]
|
|
127
|
+
# sel.annotation.get_bbox_patch().set(fc="white")
|
|
128
|
+
# text = f'{inst}\n'
|
|
129
|
+
# text += f'BJD: {sel.target[0]:9.5f}\n'
|
|
130
|
+
# text += f'RV: {vrad:.3f} ± {svrad:.3f}'
|
|
131
|
+
# # if fig.canvas.manager.toolmanager.get_tool('infotool').toggled:
|
|
132
|
+
# # text += '\n\n'
|
|
133
|
+
# # text += f'date: {_s.date_night[sel.index]}\n'
|
|
134
|
+
# # text += f'mask: {_s.ccf_mask[sel.index]}'
|
|
135
|
+
# sel.annotation.set_text(text)
|
|
115
136
|
|
|
116
137
|
if show_masked:
|
|
117
138
|
if versus_n:
|
|
@@ -147,9 +168,43 @@ def plot(self, ax=None, show_masked=False, instrument=None, time_offset=0,
|
|
|
147
168
|
fig.canvas.draw()
|
|
148
169
|
except ValueError:
|
|
149
170
|
pass
|
|
150
|
-
|
|
151
171
|
plt.connect('pick_event', on_pick_legend)
|
|
152
172
|
|
|
173
|
+
if tooltips:
|
|
174
|
+
annotations = []
|
|
175
|
+
def on_pick_point(event):
|
|
176
|
+
print('annotations:', annotations)
|
|
177
|
+
for text in annotations:
|
|
178
|
+
text.remove()
|
|
179
|
+
annotations.remove(text)
|
|
180
|
+
|
|
181
|
+
artist = event.artist
|
|
182
|
+
if isinstance(artist, (matplotlib.lines.Line2D, matplotlib.collections.LineCollection)):
|
|
183
|
+
print(event.ind, artist)
|
|
184
|
+
if isinstance(artist, matplotlib.lines.Line2D):
|
|
185
|
+
matching_instrument = [k for k, v in containers.items() if artist in v]
|
|
186
|
+
print(matching_instrument)
|
|
187
|
+
if len(matching_instrument) == 0:
|
|
188
|
+
return
|
|
189
|
+
inst = matching_instrument[0]
|
|
190
|
+
_s = getattr(self, inst)
|
|
191
|
+
ind = event.ind[0]
|
|
192
|
+
# print(_s.mtime[ind], _s.mvrad[ind], _s.msvrad[ind])
|
|
193
|
+
|
|
194
|
+
text = f'{inst}\n'
|
|
195
|
+
text += f'{_s.mtime[ind]:9.5f}\n'
|
|
196
|
+
text += f'RV: {_s.mvrad[ind]:.1f} ± {_s.msvrad[ind]:.1f}'
|
|
197
|
+
|
|
198
|
+
annotations.append(
|
|
199
|
+
ax.annotate(text, (_s.mtime[ind], _s.mvrad[ind]), xycoords='data',
|
|
200
|
+
xytext=(5, 10), textcoords='offset points', fontsize=9,
|
|
201
|
+
bbox={'boxstyle': 'round', 'fc': 'w'}, arrowprops=dict(arrowstyle="-"))
|
|
202
|
+
)
|
|
203
|
+
# ax.annotate(f'{inst}', (0.5, 0.5), xycoords=artist, ha='center', va='center')
|
|
204
|
+
fig.canvas.draw()
|
|
205
|
+
# print(event.ind, artist.get_label())
|
|
206
|
+
plt.connect('pick_event', on_pick_point)
|
|
207
|
+
|
|
153
208
|
|
|
154
209
|
if show_histogram:
|
|
155
210
|
axh.legend()
|
|
@@ -340,6 +395,31 @@ def gls(self, ax=None, label=None, fap=True, picker=True, instrument=None, **kwa
|
|
|
340
395
|
if label is not None:
|
|
341
396
|
ax.legend()
|
|
342
397
|
|
|
398
|
+
if ax.get_legend() is not None:
|
|
399
|
+
leg = ax.get_legend()
|
|
400
|
+
for text in leg.get_texts():
|
|
401
|
+
text.set_picker(True)
|
|
402
|
+
|
|
403
|
+
def on_pick_legend(event):
|
|
404
|
+
handles, labels = ax.get_legend_handles_labels()
|
|
405
|
+
artist = event.artist
|
|
406
|
+
if isinstance(artist, matplotlib.text.Text):
|
|
407
|
+
# print('handles:', handles)
|
|
408
|
+
# print('labels:', labels)
|
|
409
|
+
# print(artist.get_text())
|
|
410
|
+
try:
|
|
411
|
+
h = handles[labels.index(artist.get_text())]
|
|
412
|
+
alpha_text = {None:0.2, 1.0: 0.2, 0.2:1.0}[artist.get_alpha()]
|
|
413
|
+
alpha_point = {None: 0.0, 1.0: 0.0, 0.2: 1.0}[artist.get_alpha()]
|
|
414
|
+
h.set_alpha(alpha_point)
|
|
415
|
+
artist.set_alpha(alpha_text)
|
|
416
|
+
fig.canvas.draw()
|
|
417
|
+
except ValueError:
|
|
418
|
+
pass
|
|
419
|
+
|
|
420
|
+
if 'pick_event' not in fig.canvas.callbacks.callbacks:
|
|
421
|
+
plt.connect('pick_event', on_pick_legend)
|
|
422
|
+
|
|
343
423
|
|
|
344
424
|
if config.return_self:
|
|
345
425
|
return self
|