arvi 0.2.2__tar.gz → 0.2.4__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.
Files changed (66) hide show
  1. {arvi-0.2.2/arvi.egg-info → arvi-0.2.4}/PKG-INFO +2 -2
  2. {arvi-0.2.2 → arvi-0.2.4}/README.md +1 -1
  3. {arvi-0.2.2 → arvi-0.2.4}/arvi/__init__.py +7 -19
  4. {arvi-0.2.2 → arvi-0.2.4}/arvi/binning.py +2 -1
  5. {arvi-0.2.2 → arvi-0.2.4}/arvi/config.py +15 -4
  6. {arvi-0.2.2 → arvi-0.2.4}/arvi/dace_wrapper.py +16 -6
  7. {arvi-0.2.2 → arvi-0.2.4}/arvi/exofop_wrapper.py +2 -1
  8. {arvi-0.2.2 → arvi-0.2.4}/arvi/extra_data.py +3 -3
  9. {arvi-0.2.2 → arvi-0.2.4}/arvi/instrument_specific.py +46 -36
  10. {arvi-0.2.2 → arvi-0.2.4}/arvi/plots.py +95 -7
  11. arvi-0.2.4/arvi/reports.py +204 -0
  12. arvi-0.2.4/arvi/setup_logger.py +24 -0
  13. {arvi-0.2.2 → arvi-0.2.4}/arvi/simbad_wrapper.py +2 -3
  14. {arvi-0.2.2 → arvi-0.2.4}/arvi/timeseries.py +10 -6
  15. {arvi-0.2.2 → arvi-0.2.4}/arvi/utils.py +3 -1
  16. {arvi-0.2.2 → arvi-0.2.4/arvi.egg-info}/PKG-INFO +2 -2
  17. {arvi-0.2.2 → arvi-0.2.4}/arvi.egg-info/SOURCES.txt +1 -0
  18. {arvi-0.2.2 → arvi-0.2.4}/docs/API.md +5 -1
  19. {arvi-0.2.2 → arvi-0.2.4}/docs/detailed.ipynb +13 -19
  20. {arvi-0.2.2 → arvi-0.2.4}/mkdocs.yml +22 -2
  21. arvi-0.2.4/tests/test_config.py +22 -0
  22. arvi-0.2.4/tests/test_import_object.py +33 -0
  23. arvi-0.2.2/arvi/reports.py +0 -130
  24. arvi-0.2.2/arvi/setup_logger.py +0 -20
  25. arvi-0.2.2/tests/test_import_object.py +0 -17
  26. {arvi-0.2.2 → arvi-0.2.4}/.github/dependabot.yml +0 -0
  27. {arvi-0.2.2 → arvi-0.2.4}/.github/workflows/docs-gh-pages.yml +0 -0
  28. {arvi-0.2.2 → arvi-0.2.4}/.github/workflows/install.yml +0 -0
  29. {arvi-0.2.2 → arvi-0.2.4}/.github/workflows/python-publish.yml +0 -0
  30. {arvi-0.2.2 → arvi-0.2.4}/.gitignore +0 -0
  31. {arvi-0.2.2 → arvi-0.2.4}/LICENSE +0 -0
  32. {arvi-0.2.2 → arvi-0.2.4}/arvi/HZ.py +0 -0
  33. {arvi-0.2.2 → arvi-0.2.4}/arvi/ariadne_wrapper.py +0 -0
  34. {arvi-0.2.2 → arvi-0.2.4}/arvi/berv.py +0 -0
  35. {arvi-0.2.2 → arvi-0.2.4}/arvi/data/extra/HD86226_PFS1.rdb +0 -0
  36. {arvi-0.2.2 → arvi-0.2.4}/arvi/data/extra/HD86226_PFS2.rdb +0 -0
  37. {arvi-0.2.2 → arvi-0.2.4}/arvi/data/extra/metadata.json +0 -0
  38. {arvi-0.2.2 → arvi-0.2.4}/arvi/data/info.svg +0 -0
  39. {arvi-0.2.2 → arvi-0.2.4}/arvi/data/obs_affected_ADC_issues.dat +0 -0
  40. {arvi-0.2.2 → arvi-0.2.4}/arvi/data/obs_affected_blue_cryostat_issues.dat +0 -0
  41. {arvi-0.2.2 → arvi-0.2.4}/arvi/gaia_wrapper.py +0 -0
  42. {arvi-0.2.2 → arvi-0.2.4}/arvi/headers.py +0 -0
  43. {arvi-0.2.2 → arvi-0.2.4}/arvi/kima_wrapper.py +0 -0
  44. {arvi-0.2.2 → arvi-0.2.4}/arvi/lbl_wrapper.py +0 -0
  45. {arvi-0.2.2 → arvi-0.2.4}/arvi/nasaexo_wrapper.py +0 -0
  46. {arvi-0.2.2 → arvi-0.2.4}/arvi/programs.py +0 -0
  47. {arvi-0.2.2 → arvi-0.2.4}/arvi/spectra.py +0 -0
  48. {arvi-0.2.2 → arvi-0.2.4}/arvi/stats.py +0 -0
  49. {arvi-0.2.2 → arvi-0.2.4}/arvi/stellar.py +0 -0
  50. {arvi-0.2.2 → arvi-0.2.4}/arvi/translations.py +0 -0
  51. {arvi-0.2.2 → arvi-0.2.4}/arvi.egg-info/dependency_links.txt +0 -0
  52. {arvi-0.2.2 → arvi-0.2.4}/arvi.egg-info/requires.txt +0 -0
  53. {arvi-0.2.2 → arvi-0.2.4}/arvi.egg-info/top_level.txt +0 -0
  54. {arvi-0.2.2 → arvi-0.2.4}/docs/downloading_data.md +0 -0
  55. {arvi-0.2.2 → arvi-0.2.4}/docs/index.md +0 -0
  56. {arvi-0.2.2 → arvi-0.2.4}/docs/logo/detective.png +0 -0
  57. {arvi-0.2.2 → arvi-0.2.4}/docs/logo/logo.png +0 -0
  58. {arvi-0.2.2 → arvi-0.2.4}/docs/stylesheets/extra.css +0 -0
  59. {arvi-0.2.2 → arvi-0.2.4}/pyproject.toml +0 -0
  60. {arvi-0.2.2 → arvi-0.2.4}/setup.cfg +0 -0
  61. {arvi-0.2.2 → arvi-0.2.4}/setup.py +0 -0
  62. {arvi-0.2.2 → arvi-0.2.4}/tests/HD10700-Bcor_ESPRESSO18.rdb +0 -0
  63. {arvi-0.2.2 → arvi-0.2.4}/tests/test_binning.py +0 -0
  64. {arvi-0.2.2 → arvi-0.2.4}/tests/test_create_RV.py +0 -0
  65. {arvi-0.2.2 → arvi-0.2.4}/tests/test_simbad.py +0 -0
  66. {arvi-0.2.2 → arvi-0.2.4}/tests/test_stats.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arvi
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: The Automated RV Inspector
5
5
  Author-email: João Faria <joao.faria@unige.ch>
6
6
  License: MIT
@@ -23,7 +23,7 @@ Requires-Dist: kepmodel
23
23
  Dynamic: license-file
24
24
 
25
25
  <p align="center">
26
- <img width = "140" src="https://github.com/j-faria/arvi/blob/main/docs/logo/logo.png?raw=true"/>
26
+ <img width = "140" src="https://raw.githubusercontent.com/j-faria/arvi/refs/heads/main/docs/logo/logo.png"/>
27
27
  </p>
28
28
 
29
29
  This package sits alongside [DACE](https://dace.unige.ch/) to help with the
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img width = "140" src="https://github.com/j-faria/arvi/blob/main/docs/logo/logo.png?raw=true"/>
2
+ <img width = "140" src="https://raw.githubusercontent.com/j-faria/arvi/refs/heads/main/docs/logo/logo.png"/>
3
3
  </p>
4
4
 
5
5
  This package sits alongside [DACE](https://dace.unige.ch/) to help with the
@@ -1,4 +1,4 @@
1
- __all__ = ['RV']
1
+ __all__ = ['RV', 'config', 'simbad', 'gaia']
2
2
 
3
3
  from importlib.metadata import version, PackageNotFoundError
4
4
  try:
@@ -8,17 +8,15 @@ except PackageNotFoundError:
8
8
  pass
9
9
 
10
10
  from .config import config
11
- from .timeseries import RV
12
-
13
11
  from .simbad_wrapper import simbad
12
+ from .gaia_wrapper import gaia
14
13
 
15
-
16
- ## OLD
17
- # # the __getattr__ function is always called twice, so we need this
18
- # # to only build and return the RV object on the second time
19
- # _ran_once = False
14
+ from .timeseries import RV
20
15
 
21
16
  def __getattr__(name: str):
17
+ if not config.fancy_import:
18
+ raise AttributeError
19
+
22
20
  if name in (
23
21
  '_ipython_canary_method_should_not_exist_',
24
22
  '_ipython_display_',
@@ -31,15 +29,5 @@ def __getattr__(name: str):
31
29
  globals()[name] = RV(name)
32
30
  return globals()[name]
33
31
  except ValueError as e:
34
- raise ImportError(e) from None
35
- # raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
36
-
37
- ## OLD
38
- # # can't do it any other way :(
39
- # global _ran_once
32
+ raise AttributeError(e)
40
33
 
41
- # if _ran_once:
42
- # _ran_once = False
43
- # return RV(name)
44
- # else:
45
- # _ran_once = True
@@ -1,6 +1,6 @@
1
1
  import numpy as np
2
2
 
3
- from .setup_logger import logger
3
+ from .setup_logger import setup_logger
4
4
 
5
5
  ###############################################################################
6
6
  # the following is mostly a copy of the scipy implementation of
@@ -390,6 +390,7 @@ def binRV(time, rv, err=None, stat='wmean', tstat='wmean', estat='addquad',
390
390
 
391
391
 
392
392
  def bin_ccf_mask(time, ccf_mask):
393
+ logger = setup_logger()
393
394
  indices = binRV(time, None, binning_indices=True)
394
395
  indices = np.r_[indices, time.size]
395
396
  bmask = []
@@ -8,6 +8,7 @@ def get_config_path():
8
8
 
9
9
  def get_config():
10
10
  config = configparser.ConfigParser()
11
+ config.add_section('config')
11
12
  if (path := get_config_path()).exists():
12
13
  config.read(path)
13
14
  return config
@@ -31,6 +32,10 @@ class config:
31
32
  'check_internet': False,
32
33
  # make all DACE requests without using a .dacerc file
33
34
  'request_as_public': False,
35
+ # enable from arvi import star_name
36
+ 'fancy_import': True,
37
+ # use the 'dark_background' matplotlib theme
38
+ 'dark_plots': False,
34
39
  # debug
35
40
  'debug': False,
36
41
  }
@@ -43,10 +48,16 @@ class config:
43
48
  # return {'return_self': 'help!'}
44
49
  return {}
45
50
 
46
- if self.__user_config.has_option('config', name):
47
- self.__conf[name] = self.__user_config.get('config', name)
51
+ try:
52
+ if self.__user_config.has_option('config', name):
53
+ value = self.__user_config.get('config', name)
54
+ value = True if value == 'True' else value
55
+ value = False if value == 'False' else value
56
+ self.__conf[name] = value
48
57
 
49
- return self.__conf[name]
58
+ return self.__conf[name]
59
+ except KeyError:
60
+ raise KeyError(f"unknown config option '{name}'")
50
61
 
51
62
  def __setattr__(self, name, value):
52
63
  if name in config.__setters:
@@ -54,7 +65,7 @@ class config:
54
65
  else:
55
66
  if 'config' not in self.__user_config:
56
67
  self.__user_config.add_section('config')
57
- self.__user_config.set('config', name, value)
68
+ self.__user_config.set('config', name, str(value))
58
69
  save_config(self.__user_config)
59
70
  # raise NameError(f"unknown configuration name '{name}'")
60
71
 
@@ -5,11 +5,13 @@ import collections
5
5
  from functools import lru_cache
6
6
  from itertools import islice
7
7
  import numpy as np
8
- from .setup_logger import logger
8
+
9
+ from .setup_logger import setup_logger
9
10
  from .utils import create_directory, all_logging_disabled, stdout_disabled, tqdm
10
11
 
11
12
 
12
13
  def load_spectroscopy(user=None):
14
+ logger = setup_logger()
13
15
  with all_logging_disabled():
14
16
  from dace_query.spectroscopy import SpectroscopyClass, Spectroscopy as default_Spectroscopy
15
17
  from dace_query import DaceClass
@@ -45,8 +47,10 @@ def load_spectroscopy(user=None):
45
47
  logger.warning('requesting DACE data as public (no .dacerc file found)')
46
48
  return default_Spectroscopy
47
49
 
48
- @lru_cache()
49
- def get_dace_id(star, verbose=True):
50
+
51
+ @lru_cache(maxsize=1024)
52
+ def get_dace_id(star, verbose=True, raise_error=False):
53
+ logger = setup_logger()
50
54
  filters = {"obj_id_catname": {"equal": [star]}}
51
55
  try:
52
56
  with all_logging_disabled():
@@ -55,9 +59,13 @@ def get_dace_id(star, verbose=True):
55
59
  except KeyError:
56
60
  if verbose:
57
61
  logger.error(f"Could not find DACE ID for {star}")
62
+ if not raise_error:
63
+ return None
58
64
  raise ValueError from None
59
65
 
66
+
60
67
  def get_arrays(result, latest_pipeline=True, ESPRESSO_mode='HR11', NIRPS_mode='HE', verbose=True):
68
+ logger = setup_logger()
61
69
  arrays = []
62
70
  instruments = [str(i) for i in result.keys()]
63
71
 
@@ -66,7 +74,6 @@ def get_arrays(result, latest_pipeline=True, ESPRESSO_mode='HR11', NIRPS_mode='H
66
74
 
67
75
  # select ESPRESSO mode, which is defined at the level of the pipeline
68
76
  if 'ESPRESSO' in inst:
69
-
70
77
  find_mode = [ESPRESSO_mode in pipe for pipe in pipelines]
71
78
  # the mode was not found
72
79
  if not any(find_mode):
@@ -160,12 +167,12 @@ def get_observations_from_instrument(star, instrument, user=None, main_id=None,
160
167
 
161
168
  found_dace_id = False
162
169
  try:
163
- dace_id = get_dace_id(star, verbose=verbose)
170
+ dace_id = get_dace_id(star, verbose=verbose, raise_error=True)
164
171
  found_dace_id = True
165
172
  except ValueError as e:
166
173
  if main_id is not None:
167
174
  try:
168
- dace_id = get_dace_id(main_id, verbose=verbose)
175
+ dace_id = get_dace_id(main_id, verbose=verbose, raise_error=True)
169
176
  found_dace_id = True
170
177
  except ValueError:
171
178
  pass
@@ -259,6 +266,7 @@ def get_observations_from_instrument(star, instrument, user=None, main_id=None,
259
266
  return r
260
267
 
261
268
  def get_observations(star, instrument=None, user=None, main_id=None, verbose=True):
269
+ logger = setup_logger()
262
270
  if instrument is None:
263
271
  Spectroscopy = load_spectroscopy(user)
264
272
 
@@ -444,6 +452,7 @@ def extract_fits(output_directory, filename=None):
444
452
 
445
453
 
446
454
  def do_symlink_filetype(type, raw_files, output_directory, clobber=False, top_level=None, verbose=True):
455
+ logger = setup_logger()
447
456
  terminations = {
448
457
  'CCF': '_CCF_A.fits',
449
458
  'S1D': '_S1D_A.fits',
@@ -489,6 +498,7 @@ def do_symlink_filetype(type, raw_files, output_directory, clobber=False, top_le
489
498
  def do_download_filetype(type, raw_files, output_directory, clobber=False, user=None,
490
499
  verbose=True, chunk_size=20, parallel_limit=30):
491
500
  """ Download CCFs / S1Ds / S2Ds from DACE """
501
+ logger = setup_logger()
492
502
  raw_files = np.atleast_1d(raw_files)
493
503
 
494
504
  create_directory(output_directory)
@@ -4,9 +4,10 @@ import time
4
4
  import importlib.resources as resources
5
5
  import numpy as np
6
6
 
7
- from .setup_logger import logger
7
+ from .setup_logger import setup_logger
8
8
 
9
9
  def get_toi_list(verbose=True):
10
+ logger = setup_logger()
10
11
  toi_list = resources.files('arvi') / 'data' / 'exofop_toi_list.csv'
11
12
  now = time.time()
12
13
  download = not toi_list.exists() or toi_list.stat().st_mtime < now - 48 * 60 * 60
@@ -3,8 +3,7 @@ from glob import glob
3
3
  import json
4
4
 
5
5
  from numpy import full
6
- from .setup_logger import logger
7
- from . import timeseries
6
+ from .setup_logger import setup_logger
8
7
 
9
8
  refs = {
10
9
  'HD86226': 'Teske et al. 2020 (AJ, 160, 2)'
@@ -12,7 +11,8 @@ refs = {
12
11
 
13
12
  def get_extra_data(star, instrument=None, path=None, verbose=True,
14
13
  check_for_kms=True):
15
-
14
+ from . import timeseries
15
+ logger = setup_logger()
16
16
  if path is None:
17
17
  path = os.path.dirname(__file__)
18
18
  path = os.path.join(path, 'data', 'extra')
@@ -1,7 +1,7 @@
1
1
  import os, sys
2
2
  import numpy as np
3
3
 
4
- from .setup_logger import logger
4
+ from .setup_logger import setup_logger
5
5
  from .utils import ESPRESSO_ADC_issues, ESPRESSO_cryostat_issues
6
6
 
7
7
 
@@ -27,6 +27,7 @@ ESPRESSO_technical_intervention = 58665
27
27
 
28
28
  def divide_ESPRESSO(self):
29
29
  """ Split ESPRESSO data into separate sub ESP18 and ESP19 subsets """
30
+ logger = setup_logger()
30
31
  if self._check_instrument('ESPRESSO', strict=False) is None:
31
32
  return
32
33
  if 'ESPRESSO18' in self.instruments and 'ESPRESSO19' in self.instruments:
@@ -64,6 +65,7 @@ def divide_ESPRESSO(self):
64
65
 
65
66
  def divide_HARPS(self):
66
67
  """ Split HARPS data into separate sub HARPS03 and HARPS15 subsets """
68
+ logger = setup_logger()
67
69
  if self._check_instrument('HARPS', strict=False) is None:
68
70
  return
69
71
  if 'HARPS03' in self.instruments and 'HARPS15' in self.instruments:
@@ -100,6 +102,7 @@ def divide_HARPS(self):
100
102
 
101
103
 
102
104
  def check(self, instrument):
105
+ logger = setup_logger()
103
106
  instruments = self._check_instrument(instrument)
104
107
  if instruments is None:
105
108
  if self.verbose:
@@ -118,6 +121,7 @@ def HARPS_commissioning(self, mask=True, plot=True):
118
121
  plot (bool, optional):
119
122
  Whether to plot the masked points.
120
123
  """
124
+ logger = setup_logger()
121
125
  if check(self, 'HARPS') is None:
122
126
  return
123
127
 
@@ -149,6 +153,7 @@ def HARPS_fiber_commissioning(self, mask=True, plot=True):
149
153
  plot (bool, optional):
150
154
  Whether to plot the masked points.
151
155
  """
156
+ logger = setup_logger()
152
157
  if check(self, 'HARPS') is None:
153
158
  return
154
159
 
@@ -182,6 +187,7 @@ def ADC_issues(self, mask=True, plot=True, check_headers=False):
182
187
  check_headers (bool, optional):
183
188
  Whether to (double-)check the headers for missing/zero keywords.
184
189
  """
190
+ logger = setup_logger()
185
191
  instruments = self._check_instrument('ESPRESSO')
186
192
 
187
193
  if instruments is None:
@@ -225,6 +231,7 @@ def blue_cryostat_issues(self, mask=True, plot=True):
225
231
  mask (bool, optional): Whether to mask out the points.
226
232
  plot (bool, optional): Whether to plot the masked points.
227
233
  """
234
+ logger = setup_logger()
228
235
  instruments = self._check_instrument('ESPRESSO')
229
236
 
230
237
  if instruments is None:
@@ -259,6 +266,7 @@ def qc_scired_issues(self, plot=False, **kwargs):
259
266
  Args:
260
267
  plot (bool, optional): Whether to plot the masked points.
261
268
  """
269
+ logger = setup_logger()
262
270
  from .headers import get_headers
263
271
 
264
272
  instruments = self._check_instrument('ESPRESSO')
@@ -299,38 +307,40 @@ def qc_scired_issues(self, plot=False, **kwargs):
299
307
  return affected
300
308
 
301
309
 
302
- def known_issues(self, mask=True, plot=False, **kwargs):
303
- """ Identify and optionally mask known instrumental issues.
304
-
305
- Args:
306
- mask (bool, optional): Whether to mask out the points.
307
- plot (bool, optional): Whether to plot the masked points.
308
- """
309
- try:
310
- adc = ADC_issues(self, mask, plot, **kwargs)
311
- except IndexError:
312
- logger.error('are the data binned? cannot proceed to mask these points...')
313
-
314
- try:
315
- cryostat = blue_cryostat_issues(self, mask, plot)
316
- except IndexError:
317
- logger.error('are the data binned? cannot proceed to mask these points...')
318
-
319
- try:
320
- harps_comm = HARPS_commissioning(self, mask, plot)
321
- except IndexError:
322
- logger.error('are the data binned? cannot proceed to mask these points...')
323
-
324
- try:
325
- harps_fibers = HARPS_fiber_commissioning(self, mask, plot)
326
- except IndexError:
327
- logger.error('are the data binned? cannot proceed to mask these points...')
328
-
329
- # if None in (adc, cryostat, harps_comm, harps_fibers):
330
- # return
331
-
332
- try:
333
- # return adc | cryostat
334
- return np.logical_or.reduce((adc, cryostat, harps_comm, harps_fibers))
335
- except UnboundLocalError:
336
- return
310
+ class ISSUES:
311
+ def known_issues(self, mask=True, plot=False, **kwargs):
312
+ """ Identify and optionally mask known instrumental issues.
313
+
314
+ Args:
315
+ mask (bool, optional): Whether to mask out the points.
316
+ plot (bool, optional): Whether to plot the masked points.
317
+ """
318
+ logger = setup_logger()
319
+ try:
320
+ adc = ADC_issues(self, mask, plot, **kwargs)
321
+ except IndexError:
322
+ logger.error('are the data binned? cannot proceed to mask these points...')
323
+
324
+ try:
325
+ cryostat = blue_cryostat_issues(self, mask, plot)
326
+ except IndexError:
327
+ logger.error('are the data binned? cannot proceed to mask these points...')
328
+
329
+ try:
330
+ harps_comm = HARPS_commissioning(self, mask, plot)
331
+ except IndexError:
332
+ logger.error('are the data binned? cannot proceed to mask these points...')
333
+
334
+ try:
335
+ harps_fibers = HARPS_fiber_commissioning(self, mask, plot)
336
+ except IndexError:
337
+ logger.error('are the data binned? cannot proceed to mask these points...')
338
+
339
+ # if None in (adc, cryostat, harps_comm, harps_fibers):
340
+ # return
341
+
342
+ try:
343
+ # return adc | cryostat
344
+ return np.logical_or.reduce((adc, cryostat, harps_comm, harps_fibers))
345
+ except UnboundLocalError:
346
+ return
@@ -5,7 +5,7 @@ import numpy as np
5
5
 
6
6
  from astropy.timeseries import LombScargle
7
7
 
8
- from .setup_logger import logger
8
+ from .setup_logger import setup_logger
9
9
  from .config import config
10
10
  from .stats import wmean
11
11
 
@@ -13,10 +13,12 @@ from .utils import lazy_import
13
13
  plt = lazy_import('matplotlib.pyplot')
14
14
 
15
15
 
16
- def plot_fast(func):
16
+ def plot_settings(func):
17
17
  @wraps(func)
18
18
  def wrapper(*args, **kwargs):
19
- with plt.style.context('fast'):
19
+ # with plt.style.context('fast'):
20
+ theme = 'dark_background' if config.dark_plots else 'fast'
21
+ with plt.style.context(theme):
20
22
  return func(*args, **kwargs)
21
23
  return wrapper
22
24
 
@@ -135,7 +137,7 @@ def clickable_legend(fig, ax, leg):
135
137
  pass
136
138
  return on_pick_legend
137
139
 
138
- # @plot_fast
140
+ @plot_settings
139
141
  def plot(self, ax=None, show_masked=False, instrument=None, time_offset=0,
140
142
  remove_50000=False, tooltips=True, show_title=False, show_legend=True, label=None,
141
143
  jitter=None, N_in_label=False, versus_n=False, show_histogram=False, bw=False, **kwargs):
@@ -172,6 +174,7 @@ def plot(self, ax=None, show_masked=False, instrument=None, time_offset=0,
172
174
  Figure: the figure
173
175
  Axes: the axis
174
176
  """
177
+ logger = setup_logger()
175
178
  if self.N == 0:
176
179
  if self.verbose:
177
180
  logger.error('no data to plot')
@@ -402,10 +405,11 @@ def plot(self, ax=None, show_masked=False, instrument=None, time_offset=0,
402
405
  return fig, ax
403
406
 
404
407
 
405
- @plot_fast
408
+ # @plot_fast
406
409
  def plot_quantity(self, quantity, ax=None, show_masked=False, instrument=None,
407
410
  time_offset=0, remove_50000=False, tooltips=False, show_legend=True,
408
411
  N_in_label=False, **kwargs):
412
+ logger = setup_logger()
409
413
  if self.N == 0:
410
414
  if self.verbose:
411
415
  logger.error('no data to plot')
@@ -504,7 +508,88 @@ plot_rhk = partialmethod(plot_quantity, quantity='rhk')
504
508
  plot_berv = partialmethod(plot_quantity, quantity='berv')
505
509
 
506
510
 
507
- @plot_fast
511
+ def plot_xy(self, x, y, ax=None, instrument=None, show_legend=True, **kwargs):
512
+ logger = setup_logger()
513
+ if self.N == 0:
514
+ if self.verbose:
515
+ logger.error('no data to plot')
516
+ return
517
+
518
+ if ax is None:
519
+ fig, ax = plt.subplots(1, 1, constrained_layout=True)
520
+ else:
521
+ fig = ax.figure
522
+
523
+ kwargs.setdefault('marker', 'o')
524
+ kwargs.setdefault('ls', '')
525
+ kwargs.setdefault('capsize', 0)
526
+ kwargs.setdefault('ms', 4)
527
+
528
+ instruments = self._check_instrument(instrument)
529
+
530
+ for inst in instruments:
531
+ s = self if self._child else getattr(self, inst)
532
+ label = inst
533
+
534
+ missing = False
535
+ try:
536
+ xdata = getattr(s, x).copy()
537
+ except AttributeError:
538
+ missing = True
539
+ try:
540
+ e_xdata = getattr(s, x + '_err').copy()
541
+ except AttributeError:
542
+ e_xdata = np.zeros_like(xdata)
543
+
544
+ try:
545
+ ydata = getattr(s, y).copy()
546
+ except AttributeError:
547
+ missing = True
548
+ try:
549
+ e_ydata = getattr(s, y + '_err').copy()
550
+ except AttributeError:
551
+ e_ydata = np.zeros_like(ydata)
552
+
553
+ if missing:
554
+ lines, *_ = ax.errorbar([], [], [],
555
+ label=label, picker=True, **kwargs)
556
+ continue
557
+
558
+ ax.errorbar(xdata[s.mask], ydata[s.mask], e_xdata[s.mask], e_ydata[s.mask],
559
+ label=label, **kwargs)
560
+
561
+ # if show_masked:
562
+ # ax.errorbar(self.time[~self.mask] - time_offset,
563
+ # getattr(self, quantity)[~self.mask],
564
+ # getattr(self, quantity + '_err')[~self.mask],
565
+ # label='masked', fmt='x', ms=10, color='k', zorder=-2)
566
+
567
+ if show_legend:
568
+ leg = ax.legend()
569
+ on_pick_legend = clickable_legend(fig, ax, leg)
570
+ plt.connect('pick_event', on_pick_legend)
571
+
572
+ ax.minorticks_on()
573
+
574
+ delta = 'Δ' if self._did_adjust_means else ''
575
+
576
+ # ylabel = {
577
+ # quantity.lower(): quantity,
578
+ # 'fwhm': f'{delta}FWHM [{self.units}]',
579
+ # 'bispan': f'{delta}BIS [{self.units}]',
580
+ # 'rhk': r"$\log$ R'$_{HK}$",
581
+ # 'berv': 'BERV [km/s]',
582
+ # }
583
+
584
+ # ax.set_ylabel(ylabel[quantity.lower()])
585
+
586
+ if config.return_self:
587
+ return self
588
+ else:
589
+ return fig, ax
590
+
591
+
592
+ # @plot_fast
508
593
  def gls(self, ax=None, label=None, instrument=None,
509
594
  fap=True, fap_method='baluev', adjust_means=config.adjust_means_gls,
510
595
  picker=True, **kwargs):
@@ -531,6 +616,7 @@ def gls(self, ax=None, label=None, instrument=None,
531
616
  Whether to adjust (subtract) the weighted means of each instrument.
532
617
  Default is `config.adjust_means_gls`.
533
618
  """
619
+ logger = setup_logger()
534
620
  if self.N == 0:
535
621
  if self.verbose:
536
622
  logger.error('no data to compute gls')
@@ -692,7 +778,7 @@ def gls_quantity(self, quantity, ax=None, instrument=None,
692
778
  Whether to adjust (subtract) the weighted means of each instrument.
693
779
  Default is `config.adjust_means_gls`.
694
780
  """
695
-
781
+ logger = setup_logger()
696
782
  if not hasattr(self, quantity):
697
783
  if self.verbose:
698
784
  logger.error(f"cannot find '{quantity}' attribute")
@@ -812,6 +898,8 @@ def window_function(self, ax1=None, ax2=None, instrument=None, crosshair=False,
812
898
  crosshair (bool):
813
899
  If True, a crosshair will be drawn on the plot.
814
900
  """
901
+ logger = setup_logger()
902
+
815
903
  if self.N == 0:
816
904
  if self.verbose:
817
905
  logger.error('no data to compute window function')