timewise 0.5.3__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.
- timewise/__init__.py +1 -5
- timewise/backend/__init__.py +6 -0
- timewise/backend/base.py +36 -0
- timewise/backend/filesystem.py +80 -0
- timewise/chunking.py +50 -0
- timewise/cli.py +117 -11
- timewise/config.py +34 -0
- timewise/io/__init__.py +1 -0
- timewise/io/config.py +64 -0
- timewise/io/download.py +302 -0
- timewise/io/stable_tap.py +121 -0
- timewise/plot/__init__.py +3 -0
- timewise/plot/diagnostic.py +242 -0
- timewise/plot/lightcurve.py +112 -0
- timewise/plot/panstarrs.py +260 -0
- timewise/plot/sdss.py +109 -0
- timewise/process/__init__.py +2 -0
- timewise/process/config.py +30 -0
- timewise/process/interface.py +143 -0
- timewise/process/keys.py +10 -0
- timewise/process/stacking.py +310 -0
- timewise/process/template.yml +49 -0
- timewise/query/__init__.py +6 -0
- timewise/query/base.py +45 -0
- timewise/query/positional.py +40 -0
- timewise/tables/__init__.py +10 -0
- timewise/tables/allwise_p3as_mep.py +22 -0
- timewise/tables/base.py +9 -0
- timewise/tables/neowiser_p1bs_psd.py +22 -0
- timewise/types.py +30 -0
- timewise/util/backoff.py +12 -0
- timewise/util/csv_utils.py +12 -0
- timewise/util/error_threading.py +70 -0
- timewise/util/visits.py +33 -0
- timewise-1.0.0a1.dist-info/METADATA +205 -0
- timewise-1.0.0a1.dist-info/RECORD +39 -0
- {timewise-0.5.3.dist-info → timewise-1.0.0a1.dist-info}/WHEEL +1 -1
- timewise-1.0.0a1.dist-info/entry_points.txt +3 -0
- timewise/big_parent_sample.py +0 -106
- timewise/config_loader.py +0 -157
- timewise/general.py +0 -52
- timewise/parent_sample_base.py +0 -89
- timewise/point_source_utils.py +0 -68
- timewise/utils.py +0 -558
- timewise/wise_bigdata_desy_cluster.py +0 -1407
- timewise/wise_data_base.py +0 -2027
- timewise/wise_data_by_visit.py +0 -672
- timewise/wise_flux_conversion_correction.dat +0 -19
- timewise-0.5.3.dist-info/METADATA +0 -55
- timewise-0.5.3.dist-info/RECORD +0 -17
- timewise-0.5.3.dist-info/entry_points.txt +0 -3
- {timewise-0.5.3.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
|
+
[](https://github.com/JannisNe/timewise/actions/workflows/continous_integration.yml)
|
|
52
|
+
[](https://coveralls.io/github/JannisNe/timewise?branch=main)
|
|
53
|
+
[](https://badge.fury.io/py/timewise)
|
|
54
|
+
[](https://zenodo.org/badge/latestdoi/449677569)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+

|
|
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: [](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,,
|
timewise/big_parent_sample.py
DELETED
|
@@ -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))
|
timewise/parent_sample_base.py
DELETED
|
@@ -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)
|