timewise 0.5.4__py3-none-any.whl → 1.0.0a1__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.
Files changed (52) hide show
  1. timewise/__init__.py +1 -5
  2. timewise/backend/__init__.py +6 -0
  3. timewise/backend/base.py +36 -0
  4. timewise/backend/filesystem.py +80 -0
  5. timewise/chunking.py +50 -0
  6. timewise/cli.py +117 -11
  7. timewise/config.py +34 -0
  8. timewise/io/__init__.py +1 -0
  9. timewise/io/config.py +64 -0
  10. timewise/io/download.py +302 -0
  11. timewise/io/stable_tap.py +121 -0
  12. timewise/plot/__init__.py +3 -0
  13. timewise/plot/diagnostic.py +242 -0
  14. timewise/plot/lightcurve.py +112 -0
  15. timewise/plot/panstarrs.py +260 -0
  16. timewise/plot/sdss.py +109 -0
  17. timewise/process/__init__.py +2 -0
  18. timewise/process/config.py +30 -0
  19. timewise/process/interface.py +143 -0
  20. timewise/process/keys.py +10 -0
  21. timewise/process/stacking.py +310 -0
  22. timewise/process/template.yml +49 -0
  23. timewise/query/__init__.py +6 -0
  24. timewise/query/base.py +45 -0
  25. timewise/query/positional.py +40 -0
  26. timewise/tables/__init__.py +10 -0
  27. timewise/tables/allwise_p3as_mep.py +22 -0
  28. timewise/tables/base.py +9 -0
  29. timewise/tables/neowiser_p1bs_psd.py +22 -0
  30. timewise/types.py +30 -0
  31. timewise/util/backoff.py +12 -0
  32. timewise/util/csv_utils.py +12 -0
  33. timewise/util/error_threading.py +70 -0
  34. timewise/util/visits.py +33 -0
  35. timewise-1.0.0a1.dist-info/METADATA +205 -0
  36. timewise-1.0.0a1.dist-info/RECORD +39 -0
  37. timewise-1.0.0a1.dist-info/entry_points.txt +3 -0
  38. timewise/big_parent_sample.py +0 -106
  39. timewise/config_loader.py +0 -157
  40. timewise/general.py +0 -52
  41. timewise/parent_sample_base.py +0 -89
  42. timewise/point_source_utils.py +0 -68
  43. timewise/utils.py +0 -558
  44. timewise/wise_bigdata_desy_cluster.py +0 -1407
  45. timewise/wise_data_base.py +0 -2027
  46. timewise/wise_data_by_visit.py +0 -672
  47. timewise/wise_flux_conversion_correction.dat +0 -19
  48. timewise-0.5.4.dist-info/METADATA +0 -56
  49. timewise-0.5.4.dist-info/RECORD +0 -17
  50. timewise-0.5.4.dist-info/entry_points.txt +0 -3
  51. {timewise-0.5.4.dist-info → timewise-1.0.0a1.dist-info}/WHEEL +0 -0
  52. {timewise-0.5.4.dist-info → timewise-1.0.0a1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,205 @@
1
+ Metadata-Version: 2.4
2
+ Name: timewise
3
+ Version: 1.0.0a1
4
+ Summary: Download WISE infrared data for many objects and process them with AMPEL
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Author: Jannis Necker
8
+ Author-email: jannis.necker@gmail.com
9
+ Requires-Python: >=3.11,<3.12
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Provides-Extra: dev
14
+ Provides-Extra: docs
15
+ Requires-Dist: ampel-alerts (==0.10.3a5)
16
+ Requires-Dist: ampel-core (>=0.10.4.post0,<0.11.0)
17
+ Requires-Dist: ampel-photometry (>=0.10.1,<0.11.0)
18
+ Requires-Dist: ampel-plot (>=0.9.1,<0.10.0)
19
+ Requires-Dist: astropy (>=5.1,<6.0.0)
20
+ Requires-Dist: autodoc_pydantic[erdantic] (>=2.2.0,<3.0.0) ; extra == "docs"
21
+ Requires-Dist: backoff (>=2.1.2,<3.0.0)
22
+ Requires-Dist: coveralls (>=3.3.1,<4.0.0) ; extra == "dev"
23
+ Requires-Dist: jupyter[jupyter] (>=1.0.0,<2.0.0)
24
+ Requires-Dist: jupyterlab[jupyter] (>=4.0.6,<5.0.0)
25
+ Requires-Dist: matplotlib (>=3.5.3,<4.0.0)
26
+ Requires-Dist: mypy (>=1.18.2,<2.0.0) ; extra == "dev"
27
+ Requires-Dist: myst-parser (>=1,<3) ; extra == "docs"
28
+ Requires-Dist: numpy (>=1.23.2,<2.0.0)
29
+ Requires-Dist: pandas (>=1.4.3,<3.0.0)
30
+ Requires-Dist: pandas-stubs (>=2.3.2.250926,<3.0.0.0) ; extra == "dev"
31
+ Requires-Dist: pydantic (>=2.0.0,<3.0.0)
32
+ Requires-Dist: pytest (>=7.2.2,<8.0.0) ; extra == "dev"
33
+ Requires-Dist: pyvo (>=1.7.0,<2.0.0)
34
+ Requires-Dist: requests (>=2.28.1,<3.0.0)
35
+ Requires-Dist: ruff (>=0.13.0,<0.14.0) ; extra == "dev"
36
+ Requires-Dist: scikit-image (>=0.19.3,<0.22.0)
37
+ Requires-Dist: scikit-learn (>=1.3.0,<2.0.0)
38
+ Requires-Dist: scipy-stubs (>=1.16.2.0,<2.0.0.0) ; extra == "dev"
39
+ Requires-Dist: seaborn (>=0.11.2,<0.14.0)
40
+ Requires-Dist: sphinx-rtd-theme (>=1.3.0,<2.0.0) ; extra == "docs"
41
+ Requires-Dist: tqdm (>=4.64.0,<5.0.0)
42
+ Requires-Dist: typer (>=0.19.2,<0.20.0)
43
+ Requires-Dist: types-pyyaml (>=6.0.12.20250915,<7.0.0.0) ; extra == "dev"
44
+ Requires-Dist: types-requests (>=2.32.4.20250913,<3.0.0.0) ; extra == "dev"
45
+ Requires-Dist: urllib3 (>=2.5.0,<3.0.0)
46
+ Requires-Dist: virtualenv (>=20.16.3,<21.0.0)
47
+ Project-URL: Bug Tracker, https://github.com/JannisNe/timewise/issues
48
+ Project-URL: Homepage, https://github.com/JannisNe/timewise
49
+ Description-Content-Type: text/markdown
50
+
51
+ [![CI](https://github.com/JannisNe/timewise/actions/workflows/continous_integration.yml/badge.svg)](https://github.com/JannisNe/timewise/actions/workflows/continous_integration.yml)
52
+ [![Coverage Status](https://coveralls.io/repos/github/JannisNe/timewise/badge.svg?branch=main)](https://coveralls.io/github/JannisNe/timewise?branch=main)
53
+ [![PyPI version](https://badge.fury.io/py/timewise.svg)](https://badge.fury.io/py/timewise)
54
+ [![DOI](https://zenodo.org/badge/449677569.svg)](https://zenodo.org/badge/latestdoi/449677569)
55
+
56
+
57
+ ![](timewise.png)
58
+ # Infrared light curves from WISE data
59
+
60
+ This package downloads WISE data for positions on the sky and stacks single-exposure photometry per visit
61
+
62
+ ## Prerequisites
63
+
64
+ `timewise` makes use of [AMPEL](https://ampelproject.github.io/ampelastro/) and needs a running [MongoDB](https://www.mongodb.com/).
65
+
66
+ ## Installation
67
+ The package can be installed via `pip`:
68
+ ```bash
69
+ pip install timewise
70
+ ```
71
+
72
+ To tell AMPEL which modules, aka units, to use, build the corresponding configuration file:
73
+ ```bash
74
+ ampel config build -distributions ampel timewise -stop-on-errors 0 -out <path-to-ampel-config-file>
75
+ ```
76
+
77
+ ## Usage
78
+
79
+ ### Command line interface
80
+
81
+ ```
82
+ Usage: timewise [OPTIONS] COMMAND [ARGS]...
83
+
84
+ Timewsie CLI
85
+
86
+ ╭─ Options ────────────────────────────────────────────────────────────────────────────────────────────────╮
87
+ │ --log-level -l TEXT Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL) │
88
+ │ [default: INFO] │
89
+ │ --install-completion Install completion for the current shell. │
90
+ │ --show-completion Show completion for the current shell, to copy it or customize the │
91
+ │ installation. │
92
+ │ --help Show this message and exit. │
93
+ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯
94
+ ╭─ Commands ───────────────────────────────────────────────────────────────────────────────────────────────╮
95
+ │ download Download WISE photometry from IRSA │
96
+ │ prepare-ampel Prepares the AMPEL job file so AMPEL can be run manually │
97
+ │ process Processes the lightcurves using AMPEL │
98
+ │ export Write stacked lightcurves to disk │
99
+ │ run-chain Run download, process and export │
100
+ │ plot Make diagnostic plots │
101
+ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯
102
+
103
+ ```
104
+
105
+ The input is a CSV file with at least three columns:
106
+ - `orig_id`: an original identifier that **must** be an integer (for now)
107
+ - `ra`, `dec`: Right Ascension and Declination
108
+
109
+
110
+
111
+ `timewise` is configured with a YAML file. This is a sensible default which will use all single exposure photometry from AllWISE and NEOWISE:
112
+ ```yaml
113
+ download:
114
+ input_csv: <path-to-input>
115
+
116
+ backend:
117
+ type: filesystem
118
+ base_path: <path-to-working-directory>
119
+
120
+ queries:
121
+ - type: positional
122
+ radius_arcsec: 6
123
+ table:
124
+ name: allwise_p3as_mep
125
+ columns:
126
+ - ra
127
+ - dec
128
+ - mjd
129
+ - cntr_mf
130
+ - w1mpro_ep
131
+ - w1sigmpro_ep
132
+ - w2mpro_ep
133
+ - w2sigmpro_ep
134
+ - w1flux_ep
135
+ - w1sigflux_ep
136
+ - w2flux_ep
137
+ - w2sigflux_ep
138
+
139
+ - type: positional
140
+ radius_arcsec: 6
141
+ table:
142
+ name: neowiser_p1bs_psd
143
+ columns:
144
+ - ra
145
+ - dec
146
+ - mjd
147
+ - allwise_cntr
148
+ - w1mpro
149
+ - w1sigmpro
150
+ - w2mpro
151
+ - w2sigmpro
152
+ - w1flux
153
+ - w1sigflux
154
+ - w2flux
155
+ - w2sigflux
156
+
157
+ ampel:
158
+ mongo_db_name: <mongodb-name>
159
+ ```
160
+
161
+ This configuration file will be the input to all subcommands. Downloading and stacking can be run together or separate.
162
+
163
+
164
+ #### All-in-one:
165
+ Run download, stacking, and export:
166
+ ```bash
167
+ timewise run-chain <path-to-config-file> <path-to-ampel-config-file> <output-directory>
168
+ ```
169
+
170
+ #### Separate download and processing:
171
+ To only download the data:
172
+ ```bash
173
+ timewise download <path-to-config-file>
174
+ ```
175
+
176
+ To execute the stacking:
177
+ ```bash
178
+ timewise process <path-to-config-file> <path-to-ampel-config-file>
179
+ ```
180
+
181
+ #### Run AMPEL manually
182
+ Prepare an AMPEL job file for stacking the single-exposure data:
183
+ ```bash
184
+ timewise prepare-ampel <path-to-config-file>
185
+ ```
186
+ The result will contain the path to the prepared AMPEL job file that can be run with
187
+ ```bash
188
+ ampel job -config <path-to-ampel-config-file> -schema <path-to-ampel-job-file>
189
+ ```
190
+
191
+ #### Make some diagnostic plots
192
+ To check the datapoint selection and binning, take a quick look at the data:
193
+ ```bash
194
+ timewise plot <path-to-config-file> <indices-to-plot> <output-directory>
195
+ ```
196
+
197
+
198
+ ## Citation
199
+ If you use `timewise` please make sure to cite [Necker et al. A&A 695, A228 (2025)](https://www.aanda.org/articles/aa/abs/2025/03/aa51340-24/aa51340-24.html).
200
+ Additionally, you might want to include a reference to the specific version you are using: [![DOI](https://zenodo.org/badge/449677569.svg)](https://zenodo.org/badge/latestdoi/449677569)
201
+
202
+ ## Difference lightcurves
203
+ Make sure to check out `timewise-sup`, the Timewise Subtraction Pipeline:
204
+ [link](https://gitlab.desy.de/jannisnecker/timewise_sup).
205
+
@@ -0,0 +1,39 @@
1
+ timewise/__init__.py,sha256=pHLUWHD0-i3VhQGA7h59p8IgmMU_fyxItpb1wjckTmY,24
2
+ timewise/backend/__init__.py,sha256=w79nWfCw8n9g98CkHWJELmb4j9xblWC4DGZOV3_XhH4,134
3
+ timewise/backend/base.py,sha256=dHxRzu2q3uQ0wdGmDxnn-p68Tp19qChue7HMEu56wNA,1080
4
+ timewise/backend/filesystem.py,sha256=GQ4Hrb6_7Q7fKOn6QUl8bqAihAmyeZoTRxElNIwPQ1Y,2465
5
+ timewise/chunking.py,sha256=q7njvTSD84gdcvIk54SC-Ob863MsR79RPec8HS-bm4U,1668
6
+ timewise/cli.py,sha256=l6j-M2-x1LeDEAEnuDv9tadTNmVoxxMNgfyOaS-0oAw,3641
7
+ timewise/config.py,sha256=ZTSokRZMZDqBqVFV9DxvO-47yE9E9xWF48Rcjb0QG10,1044
8
+ timewise/io/__init__.py,sha256=S7vb0glKJnw6ztOlrD-0Wma2bQZ2RwpmXDLFJLKBMVo,35
9
+ timewise/io/config.py,sha256=aizLxt9l4aeWQNsvcemtQdr_fW1vLmpRSofcgA3Bgvk,2239
10
+ timewise/io/download.py,sha256=rKjeW7nh7xUtHh8llvsg_qmkx71Wzn2LggQnyHwqJOI,10719
11
+ timewise/io/stable_tap.py,sha256=jukCkBi2d7WACOo_kXTMCppzWUsN-pLVg9EDqHi3qd0,3478
12
+ timewise/plot/__init__.py,sha256=cc00UenWC_8zAkBH-Ylhs3yCF49tAqZ2Al9MfOoXYDI,120
13
+ timewise/plot/diagnostic.py,sha256=CKxOYd030CRz-JBkxW5lo0GoQ3nNpEWKh3WlxVdio20,8272
14
+ timewise/plot/lightcurve.py,sha256=oK0y6RFzv7QSrO1Qqyc1wNghsTILC9QAfIjuoA8i92I,3757
15
+ timewise/plot/panstarrs.py,sha256=X2ZULm7QT91cp4qociG0fVeI0saGLJwyKzL0141Vqis,8014
16
+ timewise/plot/sdss.py,sha256=cc1zU-4XFkqc8xH5yqCyMsDJf9w_54B_6NeRMjr9Pt8,2622
17
+ timewise/process/__init__.py,sha256=Yk-j1B1MnBuuaM6eFi43TxdWmFKbwFHvDsuQZt4yB_c,70
18
+ timewise/process/config.py,sha256=0TtlxbZs5FNEUt_tv8DKpOxjxp7OoTVunLRUOSw0zPM,831
19
+ timewise/process/interface.py,sha256=ZTUreyu_WkFTs8pOBNqFmCqgVs5OXEtPHjwpGVV1X_s,4929
20
+ timewise/process/keys.py,sha256=0TEVn-BwfzHGlScU-N8AtxgkhA2mUO0wBu4_ol_ylH4,197
21
+ timewise/process/stacking.py,sha256=X5GTkCV5TMn4JmAu8WVM0tQR6mocxNbxrMdR4OVx40Y,12256
22
+ timewise/process/template.yml,sha256=U_xKmygDl3E-viTgZEI8pQIJwWduB52SdI2X9vy61Yo,1037
23
+ timewise/query/__init__.py,sha256=1OA_FlLI7O0aIDOXHpBKMOyMvYLCd0kQVkzoqbxouyE,242
24
+ timewise/query/base.py,sha256=LzG207uIQOEE_RucEKBI4-sHR_EwyILGSlSk10HlEeU,973
25
+ timewise/query/positional.py,sha256=6fLfRdzat1jMqJAY7k7dEdnAZRiWyRKpWkbqpwzDKLo,1255
26
+ timewise/tables/__init__.py,sha256=5efKk4YEhHLA_QTMYc8CQp0nQWXG-F3EAPE6935ManU,263
27
+ timewise/tables/allwise_p3as_mep.py,sha256=NfCnTOdj_6DSFUWGmrwDyrrxIWvJFHktHWF40K_w5wQ,632
28
+ timewise/tables/base.py,sha256=thF5X8gqlmIapxJTkxgktuBwI_wSEeQVHIglL7YcC_M,222
29
+ timewise/tables/neowiser_p1bs_psd.py,sha256=LytaKMLfc-iRfMiTzlLjYgs3_1vWrUjZj7hKIW76lnI,616
30
+ timewise/types.py,sha256=MB-aqpyos1aCS58QWBas34WcwFHiHOkrDj_d9_ZxuVc,667
31
+ timewise/util/backoff.py,sha256=bU5yhsBO4U53XEPJ_32tgql9rq_Rzrv7w32nVQYHr64,272
32
+ timewise/util/csv_utils.py,sha256=5i3Jd4c58-doPs1N_hyYZ8Uc2nvuk9nGwgPNZoNrlu0,298
33
+ timewise/util/error_threading.py,sha256=uyV1Ri-wf87lpa17Xlp520B1V8DWHh3v9Mk97QrPmv0,2264
34
+ timewise/util/visits.py,sha256=3intUo1iiSovu7PB7uUrT-IAxyuOxl58aL0mKj5dAMI,1039
35
+ timewise-1.0.0a1.dist-info/METADATA,sha256=2KeU_-cjdIZePzr7sKNiu-nY97FeuBq087UrzXj_aI8,9172
36
+ timewise-1.0.0a1.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
37
+ timewise-1.0.0a1.dist-info/entry_points.txt,sha256=mYh1HsUFbV7KT8kxiGqVtR3Pk0oEk6Bd-2c5FsYVhG4,45
38
+ timewise-1.0.0a1.dist-info/licenses/LICENSE,sha256=sVoNJWiTlH-NarJx0wdsob468Pg3JE6vIIgll4lCa3E,1070
39
+ timewise-1.0.0a1.dist-info/RECORD,,
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ timewise=timewise.cli:app
3
+
@@ -1,106 +0,0 @@
1
- import gc
2
- import pickle
3
- import threading
4
- import time
5
- import logging
6
-
7
- from timewise.parent_sample_base import ParentSampleBase
8
-
9
- logger = logging.getLogger(__name__)
10
-
11
-
12
- class BigParentSampleBase(ParentSampleBase):
13
- """
14
- This should not be used. It was a bad idea. The better way would be to implement a
15
- ParentSample class that splits the sample file in separate files and then maybe use Dask or similar.
16
- """
17
-
18
- def __init__(self, base_name, keep_file_in_memory=30*60):
19
- """
20
- See doc of ParentSampleBase
21
-
22
- :param base_name: base name for storage directories
23
- :type base_name: str
24
- :param keep_file_in_memory: time in seconds to keep the parent sample file in the memory, after that gets written to a cache file on disk
25
- :type keep_file_in_memory: float
26
- """
27
- super().__init__(base_name=base_name)
28
-
29
- self._keep_df_in_memory = keep_file_in_memory
30
- self._time_when_df_was_used_last = time.time()
31
- self._df = None
32
- self._cache_file = self.cache_dir / "cache.pkl"
33
- self._lock_cache_file = False
34
-
35
- self._clean_thread = threading.Thread(target=self._periodically_drop_df_to_disk, daemon=True, name='ParentSampleCleanThread').start()
36
- self._stop_thread = False
37
-
38
- def _wait_for_unlock_cache_file(self):
39
- if self._lock_cache_file:
40
- logger.debug('cache file locked, waiting')
41
- while self._lock_cache_file:
42
- pass
43
-
44
- logger.debug('cache file unlocked')
45
-
46
- @property
47
- def df(self):
48
- self._time_when_df_was_used_last = time.time()
49
-
50
- if isinstance(self._df, type(None)):
51
-
52
- if self._cache_file.is_file():
53
- logger.debug(f'loading from {self._cache_file}')
54
- self._wait_for_unlock_cache_file()
55
- self._lock_cache_file = True
56
-
57
- with open(self._cache_file, "rb") as f:
58
- self._df = pickle.load(f)
59
-
60
- self._lock_cache_file = False
61
-
62
- else:
63
- logger.debug(f'No file {self._cache_file}')
64
-
65
- return self._df
66
-
67
- @df.setter
68
- def df(self, value):
69
- self._time_when_df_was_used_last = time.time()
70
- self._df = value
71
-
72
- def _periodically_drop_df_to_disk(self):
73
- logger.debug(f'starting cleaning thread for parent sample')
74
-
75
- while True:
76
- if self._stop_thread:
77
- break
78
-
79
- if (
80
- ((time.time() - self._time_when_df_was_used_last) > self._keep_df_in_memory)
81
- and not isinstance(self._df, type(None))
82
- ):
83
- logger.debug(f'writing DataFrame to {self._cache_file}')
84
- self._wait_for_unlock_cache_file()
85
- self._lock_cache_file = True
86
-
87
- with open(self._cache_file, "wb") as f:
88
- pickle.dump(self._df, f)
89
-
90
- self._lock_cache_file = False
91
- self._df = None
92
- gc.collect()
93
-
94
- time.sleep(self._keep_df_in_memory / 2)
95
-
96
- logger.debug('stopped clean thread')
97
-
98
- def __del__(self):
99
- if hasattr(self, "_cache_file") and self._cache_file.is_file():
100
- logger.debug(f'removing {self._cache_file}')
101
- self._cache_file.unlink()
102
-
103
- if hasattr(self, "clean_thread"):
104
- logger.debug(f'stopping clean thread')
105
- self._stop_thread = True
106
- self._clean_thread.join()
timewise/config_loader.py DELETED
@@ -1,157 +0,0 @@
1
- import logging
2
- import yaml
3
- import json
4
- import inspect
5
- from pydantic import BaseModel, validator
6
- import pandas as pd
7
- import importlib
8
- from pathlib import Path
9
-
10
- from timewise.parent_sample_base import ParentSampleBase
11
- from timewise.wise_data_base import WISEDataBase
12
-
13
-
14
- logger = logging.getLogger(__name__)
15
-
16
-
17
- class TimewiseConfig(BaseModel):
18
-
19
- wise_data: WISEDataBase
20
- timewise_instructions: list[dict] = list()
21
-
22
- class Config:
23
- arbitrary_types_allowed = True
24
-
25
- @validator("timewise_instructions")
26
- def validate_instructions(cls, v: list[dict], values: dict):
27
- # get the WiseData class
28
- wise_data = values["wise_data"]
29
- wise_data_class_name = type(wise_data).__name__
30
- # collect its members
31
- members = inspect.getmembers(wise_data)
32
- # loop through the methods and the corresponding arguments that wre given in the instructions
33
- for instructions in v:
34
- for method, arguments in instructions.items():
35
- found = False
36
- # check each member for a fit
37
- for member_name, member in members:
38
- if member_name == method:
39
- found = True
40
- # get the call signature of the member and see if it fits the given arguments
41
- signature = inspect.signature(member)
42
- param_list = list(signature.parameters)
43
- # check if the member is a normal method, i.e. if the first arguments is 'self'
44
- is_method = param_list[0] == "self"
45
- _arguments = arguments or dict()
46
- try:
47
- if is_method:
48
- signature.bind(WISEDataBase, **_arguments)
49
- else:
50
- signature.bind(**_arguments)
51
- except TypeError as e:
52
- raise ValueError(f"{wise_data_class_name}.{method}: {e}!")
53
-
54
- if not found:
55
- raise ValueError(f"{wise_data_class_name} does not have a method {method}!")
56
-
57
- return v
58
-
59
- def run_config(self):
60
- logger.info("running config")
61
- for instructions in self.timewise_instructions:
62
- for method, arguments in instructions.items():
63
- _arguments = arguments or dict()
64
- logger.info(f"running {method} with arguments {_arguments}")
65
- self.wise_data.__getattribute__(method)(**_arguments)
66
- logger.info("successfully ran config")
67
-
68
-
69
- class TimewiseConfigLoader(BaseModel):
70
-
71
- base_name: str
72
- filename: str = None
73
- class_module: str = "timewise"
74
- class_name: str = "WiseDataByVisit"
75
- min_sep_arcsec: float = 6.
76
- n_chunks: int = 1
77
- default_keymap: dict = {k: k for k in ["ra", "dec", "id"]}
78
- timewise_instructions: list[dict] = list()
79
-
80
- @validator("filename")
81
- def validate_file(cls, v: str):
82
- if v is not None:
83
- if not Path(v).is_file():
84
- raise ValueError(f"No file {v}!")
85
- return v
86
-
87
- @validator("class_module")
88
- def validate_class_module(cls, v: str):
89
- try:
90
- importlib.import_module(v)
91
- except (ImportError, ModuleNotFoundError):
92
- raise ValueError(f"Could not import {v}")
93
- return v
94
-
95
- @validator("class_name")
96
- def validate_class_name(cls, v: str, values: dict):
97
- if "class_module" not in values:
98
- raise ValueError("class_module must be given before class_name!")
99
- class_module = values["class_module"]
100
- try:
101
- getattr(importlib.import_module(class_module), v)
102
- except (ImportError, AttributeError):
103
- raise ValueError(f"Could not find {v} in {class_module}")
104
- return v
105
-
106
- @validator("default_keymap")
107
- def validate_keymap(cls, v: dict):
108
- for k in ["ra", "dec", "id"]:
109
- if k not in v:
110
- raise ValueError(f"Keymap is missing key {k}!")
111
- return v
112
-
113
- def parse_config(self):
114
-
115
- logger.info(f"Parsing config")
116
- logger.debug(json.dumps(self.dict(), indent=4))
117
-
118
- _default_keymap = self.default_keymap
119
- _base_name = self.base_name
120
- _filename = self.filename
121
- _class_name = self.class_name
122
-
123
- class DynamicParentSample(ParentSampleBase):
124
- default_keymap = _default_keymap
125
-
126
- def __init__(self):
127
- super().__init__(_base_name)
128
- self.df = pd.read_csv(_filename)
129
-
130
- for k, v in self.default_keymap.items():
131
- if v not in self.df.columns:
132
- raise KeyError(f"Can not map '{v}' to '{k}': '{v}' not in table columns! Adjust keymap")
133
-
134
- wise_data_config = {
135
- "base_name": _base_name,
136
- "parent_sample_class": DynamicParentSample,
137
- "n_chunks": self.n_chunks,
138
- "min_sep_arcsec": self.min_sep_arcsec
139
- }
140
- wise_data_class = getattr(importlib.import_module(self.class_module), self.class_name)
141
- wise_data = wise_data_class(**wise_data_config)
142
-
143
- return TimewiseConfig(wise_data=wise_data, timewise_instructions=self.timewise_instructions)
144
-
145
- @classmethod
146
- def read_yaml(cls, filename):
147
- logger.debug(f"reading {filename}")
148
- with open(filename, "r") as f:
149
- config_dict = yaml.safe_load(f)
150
- logger.debug(f"config: {json.dumps(config_dict, indent=4)}")
151
- return cls(**config_dict)
152
-
153
- @classmethod
154
- def run_yaml(cls, filename):
155
- logger.info(f"running {filename}")
156
- config = cls.read_yaml(filename).parse_config()
157
- config.run_config()
timewise/general.py DELETED
@@ -1,52 +0,0 @@
1
- import logging
2
- import os
3
- from pathlib import Path
4
-
5
-
6
- # Setting up the Logger
7
- main_logger = logging.getLogger('timewise')
8
- logger_format = logging.Formatter(
9
- '%(levelname)s:%(name)s:%(funcName)s - [%(threadName)s] - %(asctime)s: \n\t%(message)s', "%H:%M:%S"
10
- )
11
- stream_handler = logging.StreamHandler()
12
- stream_handler.setFormatter(logger_format)
13
- main_logger.addHandler(stream_handler)
14
- main_logger.propagate = False # do not propagate to root logger
15
-
16
- logger = logging.getLogger(__name__)
17
-
18
-
19
- def get_directories() -> dict[str, Path | None]:
20
- # Setting up data directory
21
- DATA_DIR_KEY = 'TIMEWISE_DATA'
22
- if DATA_DIR_KEY in os.environ:
23
- data_dir = Path(os.environ[DATA_DIR_KEY]).expanduser()
24
- else:
25
- logger.warning(f'{DATA_DIR_KEY} not set! Using home directory.')
26
- data_dir = Path('~/').expanduser()
27
-
28
- BIGDATA_DIR_KEY = 'TIMEWISE_BIGDATA'
29
- if BIGDATA_DIR_KEY in os.environ:
30
- bigdata_dir = Path(os.environ[BIGDATA_DIR_KEY]).expanduser()
31
- logger.info(f"Using bigdata directory {bigdata_dir}")
32
- else:
33
- bigdata_dir = None
34
- logger.info(f"No bigdata directory set.")
35
-
36
- output_dir = data_dir / 'output'
37
- plots_dir = output_dir / 'plots'
38
- cache_dir = data_dir / 'cache'
39
-
40
- return {
41
- 'data_dir': data_dir,
42
- 'bigdata_dir': bigdata_dir,
43
- 'output_dir': output_dir,
44
- 'plots_dir': plots_dir,
45
- 'cache_dir': cache_dir
46
- }
47
-
48
-
49
- def backoff_hndlr(details):
50
- logger.info("Backing off {wait:0.1f} seconds after {tries} tries "
51
- "calling function {target} with args {args} and kwargs "
52
- "{kwargs}".format(**details))
@@ -1,89 +0,0 @@
1
- import abc, os
2
- import pandas as pd
3
- import numpy as np
4
- import logging
5
-
6
- from timewise.general import get_directories
7
- from timewise.utils import plot_sdss_cutout, plot_panstarrs_cutout
8
-
9
-
10
- logger = logging.getLogger(__name__)
11
-
12
-
13
- class ParentSampleBase(abc.ABC):
14
- """
15
- Base class for parent sample.
16
- Any subclass must implement
17
-
18
- - `ParentSample.df`: A `pandas.DataFrame` consisting of minimum three columns: two columns holding the sky positions of each object in the form of right ascension and declination and one row with a unique identifier.
19
- - `ParentSample.default_keymap`: a dictionary, mapping the column in `ParentSample.df` to 'ra', 'dec' and 'id'
20
-
21
- :param base_name: determining the location of any data in the `timewise` data directory.
22
- """
23
-
24
- df = pd.DataFrame()
25
- default_keymap = dict()
26
-
27
- def __init__(self, base_name):
28
- # set up directories
29
- d = get_directories()
30
- self.cache_dir = d["cache_dir"] / base_name
31
- self.plots_dir = d["plots_dir"] / base_name
32
-
33
- for d in [self.cache_dir, self.plots_dir]:
34
- d.parent.mkdir(parents=True, exist_ok=True)
35
-
36
- self.local_sample_copy = self.cache_dir / 'sample.csv'
37
-
38
- def plot_cutout(self, ind, arcsec=20, interactive=False, **kwargs):
39
- """
40
- Plot the coutout images in all filters around the position of object with index i
41
-
42
- :param ind: the index in the sample
43
- :type ind: int or list-like
44
- :param arcsec: the radius of the cutout
45
- :type arcsec: float
46
- :param interactive: interactive mode
47
- :type interactive: bool
48
- :param kwargs: any additional kwargs will be passed to `matplotlib.pyplot.subplots()`
49
- :return: figure and axes if `interactive=True`
50
- """
51
- sel = self.df.loc[np.atleast_1d(ind)]
52
- ra, dec = sel[self.default_keymap["ra"]], sel[self.default_keymap["dec"]]
53
- title = [r[self.default_keymap["id"]] for i, r in sel.iterrows()]
54
-
55
- fn = kwargs.pop(
56
- "fn",
57
- [self.plots_dir / f"{i}_{r[self.default_keymap['id']]}.pdf"
58
- for i, r in sel.iterrows()]
59
- )
60
- self.plots_dir.mkdir(parents=True, exist_ok=True)
61
-
62
- logger.debug(f"\nRA: {ra}\nDEC: {dec}\nTITLE: {title}\nFN: {fn}")
63
- ou = list()
64
-
65
- ras = np.atleast_1d(ra)
66
- decs = np.atleast_1d(dec)
67
- title = np.atleast_1d(title) if title else [None] * len(ras)
68
- fn = np.atleast_1d(fn) if fn else [None] * len(ras)
69
-
70
- for _ra, _dec, _title, _fn in zip(ras, decs, title, fn):
71
- ou.append(self._plot_cutout(_ra, _dec, arcsec, interactive, title=_title, fn=_fn, **kwargs))
72
-
73
- if len(ou) == 1:
74
- ou = ou[0]
75
- return ou
76
-
77
- @staticmethod
78
- def _plot_cutout(ra, dec, arcsec, interactive, which="sdss", **kwargs):
79
- if which == "sdss":
80
- return plot_sdss_cutout(ra, dec, arcsec=arcsec, interactive=interactive, **kwargs)
81
- elif which == "panstarrs":
82
- return plot_panstarrs_cutout(ra, dec, arcsec=arcsec, interactive=interactive, **kwargs)
83
- else:
84
- raise ValueError(f"{which} not an implemented survey! Choose one of 'sdss' or 'panstarrs'.")
85
-
86
- def save_local(self):
87
- logger.debug(f"saving under {self.local_sample_copy}")
88
- self.local_sample_copy.parent.mkdir(parents=True, exist_ok=True)
89
- self.df.to_csv(self.local_sample_copy)