timewise 0.5.4__tar.gz → 1.0.0a1__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.
- timewise-1.0.0a1/PKG-INFO +205 -0
- timewise-1.0.0a1/README.md +154 -0
- timewise-1.0.0a1/pyproject.toml +96 -0
- timewise-1.0.0a1/timewise/__init__.py +1 -0
- timewise-1.0.0a1/timewise/backend/__init__.py +6 -0
- timewise-1.0.0a1/timewise/backend/base.py +36 -0
- timewise-1.0.0a1/timewise/backend/filesystem.py +80 -0
- timewise-1.0.0a1/timewise/chunking.py +50 -0
- timewise-1.0.0a1/timewise/cli.py +124 -0
- timewise-1.0.0a1/timewise/config.py +34 -0
- timewise-1.0.0a1/timewise/io/__init__.py +1 -0
- timewise-1.0.0a1/timewise/io/config.py +64 -0
- timewise-1.0.0a1/timewise/io/download.py +302 -0
- timewise-1.0.0a1/timewise/io/stable_tap.py +121 -0
- timewise-1.0.0a1/timewise/plot/__init__.py +3 -0
- timewise-1.0.0a1/timewise/plot/diagnostic.py +242 -0
- timewise-1.0.0a1/timewise/plot/lightcurve.py +112 -0
- timewise-1.0.0a1/timewise/plot/panstarrs.py +260 -0
- timewise-1.0.0a1/timewise/plot/sdss.py +109 -0
- timewise-1.0.0a1/timewise/process/__init__.py +2 -0
- timewise-1.0.0a1/timewise/process/config.py +30 -0
- timewise-1.0.0a1/timewise/process/interface.py +143 -0
- timewise-1.0.0a1/timewise/process/keys.py +10 -0
- timewise-1.0.0a1/timewise/process/stacking.py +310 -0
- timewise-1.0.0a1/timewise/process/template.yml +49 -0
- timewise-1.0.0a1/timewise/query/__init__.py +6 -0
- timewise-1.0.0a1/timewise/query/base.py +45 -0
- timewise-1.0.0a1/timewise/query/positional.py +40 -0
- timewise-1.0.0a1/timewise/tables/__init__.py +10 -0
- timewise-1.0.0a1/timewise/tables/allwise_p3as_mep.py +22 -0
- timewise-1.0.0a1/timewise/tables/base.py +9 -0
- timewise-1.0.0a1/timewise/tables/neowiser_p1bs_psd.py +22 -0
- timewise-1.0.0a1/timewise/types.py +30 -0
- timewise-1.0.0a1/timewise/util/backoff.py +12 -0
- timewise-1.0.0a1/timewise/util/csv_utils.py +12 -0
- timewise-1.0.0a1/timewise/util/error_threading.py +70 -0
- timewise-1.0.0a1/timewise/util/visits.py +33 -0
- timewise-0.5.4/PKG-INFO +0 -56
- timewise-0.5.4/README.md +0 -14
- timewise-0.5.4/pyproject.toml +0 -50
- timewise-0.5.4/timewise/__init__.py +0 -5
- timewise-0.5.4/timewise/big_parent_sample.py +0 -106
- timewise-0.5.4/timewise/cli.py +0 -18
- timewise-0.5.4/timewise/config_loader.py +0 -157
- timewise-0.5.4/timewise/general.py +0 -52
- timewise-0.5.4/timewise/parent_sample_base.py +0 -89
- timewise-0.5.4/timewise/point_source_utils.py +0 -68
- timewise-0.5.4/timewise/utils.py +0 -558
- timewise-0.5.4/timewise/wise_bigdata_desy_cluster.py +0 -1407
- timewise-0.5.4/timewise/wise_data_base.py +0 -2027
- timewise-0.5.4/timewise/wise_data_by_visit.py +0 -672
- timewise-0.5.4/timewise/wise_flux_conversion_correction.dat +0 -19
- {timewise-0.5.4 → timewise-1.0.0a1}/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,154 @@
|
|
|
1
|
+
[](https://github.com/JannisNe/timewise/actions/workflows/continous_integration.yml)
|
|
2
|
+
[](https://coveralls.io/github/JannisNe/timewise?branch=main)
|
|
3
|
+
[](https://badge.fury.io/py/timewise)
|
|
4
|
+
[](https://zenodo.org/badge/latestdoi/449677569)
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
# Infrared light curves from WISE data
|
|
9
|
+
|
|
10
|
+
This package downloads WISE data for positions on the sky and stacks single-exposure photometry per visit
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
`timewise` makes use of [AMPEL](https://ampelproject.github.io/ampelastro/) and needs a running [MongoDB](https://www.mongodb.com/).
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
The package can be installed via `pip`:
|
|
18
|
+
```bash
|
|
19
|
+
pip install timewise
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
To tell AMPEL which modules, aka units, to use, build the corresponding configuration file:
|
|
23
|
+
```bash
|
|
24
|
+
ampel config build -distributions ampel timewise -stop-on-errors 0 -out <path-to-ampel-config-file>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
### Command line interface
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
Usage: timewise [OPTIONS] COMMAND [ARGS]...
|
|
33
|
+
|
|
34
|
+
Timewsie CLI
|
|
35
|
+
|
|
36
|
+
╭─ Options ────────────────────────────────────────────────────────────────────────────────────────────────╮
|
|
37
|
+
│ --log-level -l TEXT Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL) │
|
|
38
|
+
│ [default: INFO] │
|
|
39
|
+
│ --install-completion Install completion for the current shell. │
|
|
40
|
+
│ --show-completion Show completion for the current shell, to copy it or customize the │
|
|
41
|
+
│ installation. │
|
|
42
|
+
│ --help Show this message and exit. │
|
|
43
|
+
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
|
44
|
+
╭─ Commands ───────────────────────────────────────────────────────────────────────────────────────────────╮
|
|
45
|
+
│ download Download WISE photometry from IRSA │
|
|
46
|
+
│ prepare-ampel Prepares the AMPEL job file so AMPEL can be run manually │
|
|
47
|
+
│ process Processes the lightcurves using AMPEL │
|
|
48
|
+
│ export Write stacked lightcurves to disk │
|
|
49
|
+
│ run-chain Run download, process and export │
|
|
50
|
+
│ plot Make diagnostic plots │
|
|
51
|
+
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The input is a CSV file with at least three columns:
|
|
56
|
+
- `orig_id`: an original identifier that **must** be an integer (for now)
|
|
57
|
+
- `ra`, `dec`: Right Ascension and Declination
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
`timewise` is configured with a YAML file. This is a sensible default which will use all single exposure photometry from AllWISE and NEOWISE:
|
|
62
|
+
```yaml
|
|
63
|
+
download:
|
|
64
|
+
input_csv: <path-to-input>
|
|
65
|
+
|
|
66
|
+
backend:
|
|
67
|
+
type: filesystem
|
|
68
|
+
base_path: <path-to-working-directory>
|
|
69
|
+
|
|
70
|
+
queries:
|
|
71
|
+
- type: positional
|
|
72
|
+
radius_arcsec: 6
|
|
73
|
+
table:
|
|
74
|
+
name: allwise_p3as_mep
|
|
75
|
+
columns:
|
|
76
|
+
- ra
|
|
77
|
+
- dec
|
|
78
|
+
- mjd
|
|
79
|
+
- cntr_mf
|
|
80
|
+
- w1mpro_ep
|
|
81
|
+
- w1sigmpro_ep
|
|
82
|
+
- w2mpro_ep
|
|
83
|
+
- w2sigmpro_ep
|
|
84
|
+
- w1flux_ep
|
|
85
|
+
- w1sigflux_ep
|
|
86
|
+
- w2flux_ep
|
|
87
|
+
- w2sigflux_ep
|
|
88
|
+
|
|
89
|
+
- type: positional
|
|
90
|
+
radius_arcsec: 6
|
|
91
|
+
table:
|
|
92
|
+
name: neowiser_p1bs_psd
|
|
93
|
+
columns:
|
|
94
|
+
- ra
|
|
95
|
+
- dec
|
|
96
|
+
- mjd
|
|
97
|
+
- allwise_cntr
|
|
98
|
+
- w1mpro
|
|
99
|
+
- w1sigmpro
|
|
100
|
+
- w2mpro
|
|
101
|
+
- w2sigmpro
|
|
102
|
+
- w1flux
|
|
103
|
+
- w1sigflux
|
|
104
|
+
- w2flux
|
|
105
|
+
- w2sigflux
|
|
106
|
+
|
|
107
|
+
ampel:
|
|
108
|
+
mongo_db_name: <mongodb-name>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
This configuration file will be the input to all subcommands. Downloading and stacking can be run together or separate.
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
#### All-in-one:
|
|
115
|
+
Run download, stacking, and export:
|
|
116
|
+
```bash
|
|
117
|
+
timewise run-chain <path-to-config-file> <path-to-ampel-config-file> <output-directory>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
#### Separate download and processing:
|
|
121
|
+
To only download the data:
|
|
122
|
+
```bash
|
|
123
|
+
timewise download <path-to-config-file>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
To execute the stacking:
|
|
127
|
+
```bash
|
|
128
|
+
timewise process <path-to-config-file> <path-to-ampel-config-file>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
#### Run AMPEL manually
|
|
132
|
+
Prepare an AMPEL job file for stacking the single-exposure data:
|
|
133
|
+
```bash
|
|
134
|
+
timewise prepare-ampel <path-to-config-file>
|
|
135
|
+
```
|
|
136
|
+
The result will contain the path to the prepared AMPEL job file that can be run with
|
|
137
|
+
```bash
|
|
138
|
+
ampel job -config <path-to-ampel-config-file> -schema <path-to-ampel-job-file>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
#### Make some diagnostic plots
|
|
142
|
+
To check the datapoint selection and binning, take a quick look at the data:
|
|
143
|
+
```bash
|
|
144
|
+
timewise plot <path-to-config-file> <indices-to-plot> <output-directory>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
## Citation
|
|
149
|
+
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).
|
|
150
|
+
Additionally, you might want to include a reference to the specific version you are using: [](https://zenodo.org/badge/latestdoi/449677569)
|
|
151
|
+
|
|
152
|
+
## Difference lightcurves
|
|
153
|
+
Make sure to check out `timewise-sup`, the Timewise Subtraction Pipeline:
|
|
154
|
+
[link](https://gitlab.desy.de/jannisnecker/timewise_sup).
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["poetry-core>=2.0.0"]
|
|
3
|
+
build-backend = "poetry.core.masonry.api"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "timewise"
|
|
7
|
+
version = "1.0.0a1"
|
|
8
|
+
description = "Download WISE infrared data for many objects and process them with AMPEL"
|
|
9
|
+
authors = [
|
|
10
|
+
{ name = "Jannis Necker", email = "jannis.necker@gmail.com" },
|
|
11
|
+
]
|
|
12
|
+
license = { text = "MIT" }
|
|
13
|
+
readme = "README.md"
|
|
14
|
+
requires-python = ">=3.11,<3.12"
|
|
15
|
+
dependencies = [
|
|
16
|
+
"tqdm>=4.64.0,<5.0.0",
|
|
17
|
+
"requests>=2.28.1,<3.0.0",
|
|
18
|
+
"pandas>=1.4.3,<3.0.0",
|
|
19
|
+
"numpy>=1.23.2,<2.0.0",
|
|
20
|
+
"pyvo>=1.7.0,<2.0.0",
|
|
21
|
+
"astropy>=5.1,<6.0.0",
|
|
22
|
+
"matplotlib>=3.5.3,<4.0.0",
|
|
23
|
+
"scikit-image>=0.19.3,<0.22.0",
|
|
24
|
+
"backoff>=2.1.2,<3.0.0",
|
|
25
|
+
"virtualenv>=20.16.3,<21.0.0",
|
|
26
|
+
"seaborn>=0.11.2,<0.14.0",
|
|
27
|
+
"pydantic>=2.0.0,<3.0.0",
|
|
28
|
+
"scikit-learn>=1.3.0,<2.0.0",
|
|
29
|
+
"jupyterlab[jupyter]>=4.0.6,<5.0.0",
|
|
30
|
+
"jupyter[jupyter]>=1.0.0,<2.0.0",
|
|
31
|
+
"ampel-alerts (==0.10.3a5)",
|
|
32
|
+
"typer (>=0.19.2,<0.20.0)",
|
|
33
|
+
"ampel-photometry (>=0.10.1,<0.11.0)",
|
|
34
|
+
"ampel-plot (>=0.9.1,<0.10.0)",
|
|
35
|
+
"ampel-core (>=0.10.4.post0,<0.11.0)",
|
|
36
|
+
"urllib3 (>=2.5.0,<3.0.0)",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.scripts]
|
|
40
|
+
timewise = "timewise.cli:app"
|
|
41
|
+
|
|
42
|
+
[project.urls]
|
|
43
|
+
Homepage = "https://github.com/JannisNe/timewise"
|
|
44
|
+
"Bug Tracker" = "https://github.com/JannisNe/timewise/issues"
|
|
45
|
+
|
|
46
|
+
[project.optional-dependencies]
|
|
47
|
+
dev = [
|
|
48
|
+
"coveralls>=3.3.1,<4.0.0",
|
|
49
|
+
"pytest>=7.2.2,<8.0.0",
|
|
50
|
+
"ruff>=0.13.0,<0.14.0",
|
|
51
|
+
"mypy (>=1.18.2,<2.0.0)",
|
|
52
|
+
"pandas-stubs (>=2.3.2.250926,<3.0.0.0)",
|
|
53
|
+
"scipy-stubs (>=1.16.2.0,<2.0.0.0)",
|
|
54
|
+
"types-pyyaml (>=6.0.12.20250915,<7.0.0.0)",
|
|
55
|
+
"types-requests (>=2.32.4.20250913,<3.0.0.0)",
|
|
56
|
+
]
|
|
57
|
+
docs = [
|
|
58
|
+
"myst-parser>=1,<3",
|
|
59
|
+
"sphinx-rtd-theme>=1.3.0,<2.0.0",
|
|
60
|
+
"autodoc_pydantic[erdantic]>=2.2.0,<3.0.0"
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
[tool.coverage.run]
|
|
64
|
+
source = ["timewise", "ampel"]
|
|
65
|
+
omit = ["timewise/v0"]
|
|
66
|
+
parallel = true
|
|
67
|
+
concurrency = ["thread", "multiprocessing"]
|
|
68
|
+
|
|
69
|
+
[tool.pytest]
|
|
70
|
+
testpaths = "tests"
|
|
71
|
+
pythonpath = "."
|
|
72
|
+
|
|
73
|
+
# Per-module ignores
|
|
74
|
+
[tool.mypy]
|
|
75
|
+
python_version = "3.11"
|
|
76
|
+
explicit_package_bases = true
|
|
77
|
+
install_types = false
|
|
78
|
+
non_interactive = false
|
|
79
|
+
mypy_path = ["."]
|
|
80
|
+
packages = ["timewise", "ampel"]
|
|
81
|
+
|
|
82
|
+
[[tool.mypy.overrides]]
|
|
83
|
+
module = [
|
|
84
|
+
"astropy.*",
|
|
85
|
+
"pyvo.*",
|
|
86
|
+
"SciServer.*",
|
|
87
|
+
"sklearn.*"
|
|
88
|
+
]
|
|
89
|
+
ignore_missing_imports = true
|
|
90
|
+
|
|
91
|
+
[tool.ruff]
|
|
92
|
+
src = ["timewise", "ampel"]
|
|
93
|
+
exclude = ["tests", "SciScript-Python"]
|
|
94
|
+
|
|
95
|
+
[tool.ruff.lint]
|
|
96
|
+
ignore = ["F401"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.0.0a1"
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
from typing import Any
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
from astropy.table import Table
|
|
5
|
+
from ..types import TaskID
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Backend(abc.ABC, BaseModel):
|
|
9
|
+
type: str
|
|
10
|
+
base_path: Any
|
|
11
|
+
"""
|
|
12
|
+
Abstract persistence backend for jobs, results, and markers.
|
|
13
|
+
Works with generic TaskIDs so it can be reused across Downloader/Processor.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
# --- metadata ---
|
|
17
|
+
@abc.abstractmethod
|
|
18
|
+
def meta_exists(self, task: TaskID) -> bool: ...
|
|
19
|
+
@abc.abstractmethod
|
|
20
|
+
def save_meta(self, task: TaskID, meta: dict[str, Any]) -> None: ...
|
|
21
|
+
@abc.abstractmethod
|
|
22
|
+
def load_meta(self, task: TaskID) -> dict[str, Any] | None: ...
|
|
23
|
+
|
|
24
|
+
# --- Markers ---
|
|
25
|
+
@abc.abstractmethod
|
|
26
|
+
def mark_done(self, task: TaskID) -> None: ...
|
|
27
|
+
@abc.abstractmethod
|
|
28
|
+
def is_done(self, task: TaskID) -> bool: ...
|
|
29
|
+
|
|
30
|
+
# --- Data ---
|
|
31
|
+
@abc.abstractmethod
|
|
32
|
+
def save_data(self, task: TaskID, content: Table) -> None: ...
|
|
33
|
+
@abc.abstractmethod
|
|
34
|
+
def load_data(self, task: TaskID) -> Table: ...
|
|
35
|
+
@abc.abstractmethod
|
|
36
|
+
def data_exists(self, task: TaskID) -> bool: ...
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any, Literal
|
|
5
|
+
from astropy.table import Table
|
|
6
|
+
|
|
7
|
+
from .base import Backend
|
|
8
|
+
from ..types import TaskID
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class FileSystemBackend(Backend):
|
|
15
|
+
type: Literal["filesystem"] = "filesystem"
|
|
16
|
+
base_path: Path
|
|
17
|
+
|
|
18
|
+
# ----------------------------
|
|
19
|
+
# Helpers for paths
|
|
20
|
+
# ----------------------------
|
|
21
|
+
def _meta_path(self, task: TaskID) -> Path:
|
|
22
|
+
return self.base_path / f"{task}.meta.json"
|
|
23
|
+
|
|
24
|
+
def _marker_path(self, task: TaskID) -> Path:
|
|
25
|
+
return self.base_path / f"{task}.ok"
|
|
26
|
+
|
|
27
|
+
def _data_path(self, task: TaskID) -> Path:
|
|
28
|
+
return self.base_path / f"{task}.fits"
|
|
29
|
+
|
|
30
|
+
# ----------------------------
|
|
31
|
+
# Metadata
|
|
32
|
+
# ----------------------------
|
|
33
|
+
def save_meta(self, task: TaskID, meta: dict[str, Any]) -> None:
|
|
34
|
+
path = self._meta_path(task)
|
|
35
|
+
tmp = path.with_suffix(".tmp")
|
|
36
|
+
tmp.parent.mkdir(parents=True, exist_ok=True)
|
|
37
|
+
logger.debug(f"writing {path}")
|
|
38
|
+
tmp.write_text(json.dumps(meta, indent=2))
|
|
39
|
+
tmp.replace(path)
|
|
40
|
+
|
|
41
|
+
def load_meta(self, task: TaskID) -> dict[str, Any] | None:
|
|
42
|
+
path = self._meta_path(task)
|
|
43
|
+
if not path.exists():
|
|
44
|
+
return None
|
|
45
|
+
return json.loads(path.read_text())
|
|
46
|
+
|
|
47
|
+
def meta_exists(self, task: TaskID) -> bool:
|
|
48
|
+
return self._meta_path(task).exists()
|
|
49
|
+
|
|
50
|
+
# ----------------------------
|
|
51
|
+
# Markers
|
|
52
|
+
# ----------------------------
|
|
53
|
+
def mark_done(self, task: TaskID) -> None:
|
|
54
|
+
mp = self._marker_path(task)
|
|
55
|
+
mp.parent.mkdir(parents=True, exist_ok=True)
|
|
56
|
+
logger.debug(f"writing {mp}")
|
|
57
|
+
mp.write_text("done")
|
|
58
|
+
|
|
59
|
+
def is_done(self, task: TaskID) -> bool:
|
|
60
|
+
return self._marker_path(task).exists()
|
|
61
|
+
|
|
62
|
+
# ----------------------------
|
|
63
|
+
# Data
|
|
64
|
+
# ----------------------------
|
|
65
|
+
def save_data(self, task: TaskID, content: Table) -> None:
|
|
66
|
+
path = self._data_path(task)
|
|
67
|
+
tmp = path.with_suffix(".tmp")
|
|
68
|
+
tmp.parent.mkdir(parents=True, exist_ok=True)
|
|
69
|
+
logger.debug(f"writing {path}")
|
|
70
|
+
content.write(tmp, format="fits")
|
|
71
|
+
tmp.replace(path)
|
|
72
|
+
|
|
73
|
+
def load_data(self, task: TaskID) -> Table:
|
|
74
|
+
path = self._data_path(task)
|
|
75
|
+
if not path.exists():
|
|
76
|
+
raise FileNotFoundError(path)
|
|
77
|
+
return Table.read(path, format="fits")
|
|
78
|
+
|
|
79
|
+
def data_exists(self, task: TaskID) -> bool:
|
|
80
|
+
return self._data_path(task).exists()
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from typing import Iterator
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import numpy as np
|
|
4
|
+
from numpy import typing as npt
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import logging
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Chunk:
|
|
12
|
+
def __init__(
|
|
13
|
+
self, chunk_id: int, indices: npt.ArrayLike, row_indices: npt.ArrayLike
|
|
14
|
+
):
|
|
15
|
+
self.chunk_id = chunk_id
|
|
16
|
+
self.indices = indices
|
|
17
|
+
self.row_numbers = row_indices
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Chunker:
|
|
21
|
+
def __init__(self, input_csv: Path, chunk_size: int):
|
|
22
|
+
self.input_csv = input_csv
|
|
23
|
+
self.chunk_size = chunk_size
|
|
24
|
+
self._n_rows = self._count_rows()
|
|
25
|
+
logger.debug(f"found {self._n_rows} rows in {self.input_csv}")
|
|
26
|
+
|
|
27
|
+
def _count_rows(self) -> int:
|
|
28
|
+
chunk = 1024 * 1024 # Process 1 MB at a time.
|
|
29
|
+
f = np.memmap(self.input_csv)
|
|
30
|
+
num_newlines = sum(
|
|
31
|
+
np.sum(f[i : i + chunk] == ord("\n")) for i in range(0, len(f), chunk)
|
|
32
|
+
)
|
|
33
|
+
del f
|
|
34
|
+
return num_newlines - 1 # one header row
|
|
35
|
+
|
|
36
|
+
def __len__(self) -> int:
|
|
37
|
+
return int(np.ceil(self._n_rows / self.chunk_size))
|
|
38
|
+
|
|
39
|
+
def __iter__(self) -> Iterator[Chunk]:
|
|
40
|
+
for chunk_id in range(len(self)):
|
|
41
|
+
yield self.get_chunk(chunk_id)
|
|
42
|
+
|
|
43
|
+
def get_chunk(self, chunk_id: int) -> Chunk:
|
|
44
|
+
if chunk_id >= len(self):
|
|
45
|
+
raise IndexError(f"Invalid chunk_id {chunk_id}")
|
|
46
|
+
start = chunk_id * self.chunk_size
|
|
47
|
+
stop = min(start + self.chunk_size, self._n_rows)
|
|
48
|
+
indices = pd.read_csv(self.input_csv, skiprows=start, nrows=stop - start).index
|
|
49
|
+
logger.debug(f"chunk {chunk_id}: from {start} to {stop}")
|
|
50
|
+
return Chunk(chunk_id, indices, np.arange(start=start, stop=stop))
|