pspipe 0.7.2__tar.gz → 0.8__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.
- {pspipe-0.7.2 → pspipe-0.8}/PKG-INFO +5 -3
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/database.py +48 -4
- pspipe-0.8/pspipe/obsdata.py +475 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/tasks.py +64 -29
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/default_settings.toml +1 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/tools/pspipe.py +27 -3
- {pspipe-0.7.2 → pspipe-0.8}/pyproject.toml +6 -2
- pspipe-0.7.2/pspipe/obsdata.py +0 -127
- pspipe-0.7.2/pspipe/tests/test_settings.py +0 -32
- pspipe-0.7.2/pspipe/tests/test_utils.py +0 -28
- pspipe-0.7.2/pspipe/tools/__init__.py +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/LICENSE +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/README.md +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/__init__.py +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/settings.py +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/eor_bins_a12.parset +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/eor_bins_alo.parset +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/eor_bins_hba.parset +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/eor_bins_nenufar.parset +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/flagger.parset +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/flagger_pre_combine.parset +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/gpr_config_a12.parset +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/gpr_config_hba.parset +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/gpr_config_nenufar.parset +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/gpr_config_v.parset +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/ps_config_a12.parset +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/ps_config_alo.parset +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/ps_config_hba.parset +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/ps_config_nenufar.parset +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/default_a12.toml +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/default_alo.toml +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/default_hba.toml +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/default_nenufar.toml +0 -0
- {pspipe-0.7.2/pspipe/tests → pspipe-0.8/pspipe/tools}/__init__.py +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/tools/psdb.py +0 -0
- {pspipe-0.7.2 → pspipe-0.8}/pspipe/utils.py +0 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pspipe
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8
|
|
4
4
|
Summary: Power spectra pipeline for Cosmic Dawn, Epoch of Reionization radio interferometric experiments
|
|
5
|
+
License-File: LICENSE
|
|
5
6
|
Author: Florent Mertens
|
|
6
7
|
Author-email: florent.mertens@gmail.com
|
|
7
8
|
Requires-Python: >=3.10,<4.0
|
|
@@ -10,10 +11,11 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
10
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
11
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
12
13
|
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
13
15
|
Provides-Extra: ml-gpr
|
|
14
16
|
Requires-Dist: astropy (>6.0)
|
|
15
17
|
Requires-Dist: click (>=8.0)
|
|
16
|
-
Requires-Dist: libpipe (>=0.
|
|
18
|
+
Requires-Dist: libpipe (>=0.6)
|
|
17
19
|
Requires-Dist: ps-eor (>=0.8)
|
|
18
20
|
Requires-Dist: ps-eor[ml-gpr] (>=0.8) ; extra == "ml-gpr"
|
|
19
21
|
Requires-Dist: python-casacore (>=3.0)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# Author: F. Mertens
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
|
+
import hashlib
|
|
6
7
|
import glob
|
|
7
8
|
import json
|
|
8
9
|
import fnmatch
|
|
@@ -137,6 +138,41 @@ class ImageRevision(object):
|
|
|
137
138
|
self.settings.image.name = img_rev_name
|
|
138
139
|
self.name = self.settings.image.name
|
|
139
140
|
|
|
141
|
+
def get_distributed_data_root(self, obs_id, persist=True):
|
|
142
|
+
"""
|
|
143
|
+
Return the root directory used for FITS products for this obs_id.
|
|
144
|
+
|
|
145
|
+
- Reads from global meta (img_list.json) if present.
|
|
146
|
+
- Otherwise computes deterministic root from settings.distributed_data_dirs
|
|
147
|
+
(or falls back to settings.data_dir).
|
|
148
|
+
- If persist=True, writes the chosen root into global meta.
|
|
149
|
+
"""
|
|
150
|
+
meta = self.get_meta(obs_id, lst_idx=None)
|
|
151
|
+
|
|
152
|
+
if meta is not None:
|
|
153
|
+
root = meta.get('data_dir_root')
|
|
154
|
+
if root:
|
|
155
|
+
return root
|
|
156
|
+
|
|
157
|
+
dirs = self.settings.get('distributed_data_dirs')
|
|
158
|
+
if dirs:
|
|
159
|
+
dirs = list(dirs)
|
|
160
|
+
h = int(hashlib.sha1(obs_id.encode('utf-8')).hexdigest(), 16)
|
|
161
|
+
root = dirs[h % len(dirs)]
|
|
162
|
+
else:
|
|
163
|
+
root = self.settings.get_path('data_dir')
|
|
164
|
+
|
|
165
|
+
if not persist:
|
|
166
|
+
return root
|
|
167
|
+
|
|
168
|
+
# Persist in global meta
|
|
169
|
+
if meta is None:
|
|
170
|
+
meta = self.new_obs_id(obs_id, lst_idx=None) # create empty global meta
|
|
171
|
+
|
|
172
|
+
meta['data_dir_root'] = root
|
|
173
|
+
meta.save()
|
|
174
|
+
return root
|
|
175
|
+
|
|
140
176
|
def create(self):
|
|
141
177
|
os.makedirs(self.get_path(), exist_ok=True)
|
|
142
178
|
self.settings.duplicate(self.get_settings_path())
|
|
@@ -185,6 +221,10 @@ class ImageRevision(object):
|
|
|
185
221
|
def get_image_path(self, obs_id):
|
|
186
222
|
return os.path.join(self.get_path(), 'images', obs_id)
|
|
187
223
|
|
|
224
|
+
def get_image_data_path(self, obs_id, persist=True):
|
|
225
|
+
root = self.get_distributed_data_root(obs_id, persist=persist)
|
|
226
|
+
return os.path.join(root, self.settings.image.name, 'images', obs_id)
|
|
227
|
+
|
|
188
228
|
def get_meta_path(self, obs_id, lst_idx=None):
|
|
189
229
|
lst_str = f'_t{int(lst_idx):03}' if lst_idx is not None else ''
|
|
190
230
|
return os.path.join(self.get_image_path(obs_id), f'img_list{lst_str}.json')
|
|
@@ -205,8 +245,9 @@ class ImageRevision(object):
|
|
|
205
245
|
def has_obs_id(self, obs_id):
|
|
206
246
|
return obs_id in self.list_obs_ids()
|
|
207
247
|
|
|
208
|
-
def new_obs_id(self, obs_id, lst_idx=None):
|
|
209
|
-
img_meta = ImageRevisionMeta.new(self.get_meta_path(obs_id, lst_idx=lst_idx), obs_id, self.settings.image.name
|
|
248
|
+
def new_obs_id(self, obs_id, lst_idx=None, data_dir_root=None):
|
|
249
|
+
img_meta = ImageRevisionMeta.new(self.get_meta_path(obs_id, lst_idx=lst_idx), obs_id, self.settings.image.name,
|
|
250
|
+
data_dir_root=data_dir_root)
|
|
210
251
|
os.makedirs(os.path.dirname(self.get_meta_path(obs_id, lst_idx=lst_idx)), exist_ok=True)
|
|
211
252
|
img_meta.save()
|
|
212
253
|
|
|
@@ -381,8 +422,11 @@ class ObsIDMeta(AbstractJsonMeta):
|
|
|
381
422
|
class ImageRevisionMeta(AbstractJsonMeta):
|
|
382
423
|
|
|
383
424
|
@staticmethod
|
|
384
|
-
def new(file, obs_id, img_rev):
|
|
385
|
-
|
|
425
|
+
def new(file, obs_id, img_rev, data_dir_root=None):
|
|
426
|
+
d = {'obs_id': obs_id, 'img_rev': img_rev}
|
|
427
|
+
if data_dir_root:
|
|
428
|
+
d['data_dir_root'] = data_dir_root
|
|
429
|
+
return ImageRevisionMeta(file, d)
|
|
386
430
|
|
|
387
431
|
def get_fits(self, stokes_or_psf, even=False, odd=False):
|
|
388
432
|
assert (even != odd) or (even is False)
|
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
# Direct access to the data
|
|
2
|
+
#
|
|
3
|
+
# Author: F. Mertens
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from ps_eor import pspec, datacube, flagger
|
|
10
|
+
|
|
11
|
+
from . import database, utils
|
|
12
|
+
|
|
13
|
+
# Lazy loaded:
|
|
14
|
+
# from ps_eor import ml_gpr
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ObsData(object):
|
|
18
|
+
|
|
19
|
+
def __init__(self, vis_rev, obs_id, lst_idx=None):
|
|
20
|
+
self.vis_rev = vis_rev
|
|
21
|
+
self.obs_id = obs_id
|
|
22
|
+
self.lst_idx = lst_idx
|
|
23
|
+
self.settings = self.vis_rev.settings
|
|
24
|
+
self.flag = None
|
|
25
|
+
self.uvrange = None
|
|
26
|
+
self.nfreqs = None
|
|
27
|
+
self.data = {}
|
|
28
|
+
self.all_stokes = {}
|
|
29
|
+
for stokes in database.all_stokes:
|
|
30
|
+
self.all_stokes[stokes.lower()] = stokes
|
|
31
|
+
self.all_stokes[stokes.lower() + '_even'] = 'even_%s' % stokes
|
|
32
|
+
self.all_stokes[stokes.lower() + '_odd'] = 'odd_%s' % stokes
|
|
33
|
+
self.all_stokes[stokes.lower() + '_dt'] = 'dt_%s' % stokes
|
|
34
|
+
self._ps_builder = None
|
|
35
|
+
|
|
36
|
+
def __getattr__(self, key):
|
|
37
|
+
assert key in self.all_stokes.keys()
|
|
38
|
+
return self.get_stokes(self.all_stokes[key])
|
|
39
|
+
|
|
40
|
+
def _invalidate_cache(self):
|
|
41
|
+
self.data = {}
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def ps_builder(self):
|
|
45
|
+
"""Power-spectrum builder, loaded on first use."""
|
|
46
|
+
if self._ps_builder is None:
|
|
47
|
+
self._ps_builder = pspec.PowerSpectraBuilder(
|
|
48
|
+
self.settings.power_spectra.get_path('ps_config'),
|
|
49
|
+
self.settings.power_spectra.get_path('eor_bin_list'))
|
|
50
|
+
return self._ps_builder
|
|
51
|
+
|
|
52
|
+
@ps_builder.setter
|
|
53
|
+
def ps_builder(self, builder):
|
|
54
|
+
self._ps_builder = builder
|
|
55
|
+
|
|
56
|
+
def _get_first_stokes(self, candidates):
|
|
57
|
+
for stokes in candidates:
|
|
58
|
+
if self.has_stokes(stokes):
|
|
59
|
+
return stokes, self.get_stokes(stokes)
|
|
60
|
+
return None, None
|
|
61
|
+
|
|
62
|
+
def get_stokes_i_like(self):
|
|
63
|
+
return self._get_first_stokes(['I', 'XX', 'YY'])[1]
|
|
64
|
+
|
|
65
|
+
def get_stokes_v_like(self):
|
|
66
|
+
return self._get_first_stokes(
|
|
67
|
+
['V', 'XY', 'YX', 'XX', 'YY', 'I'])[1]
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def mjd_mid(self):
|
|
71
|
+
"""Midpoint of the observation in MJD."""
|
|
72
|
+
cube = self.get_stokes_i_like()
|
|
73
|
+
if cube is None:
|
|
74
|
+
raise ValueError(f'No Stokes-I-like data available for {self.obs_id}')
|
|
75
|
+
return cube.meta.obs_mjd + cube.meta.total_time / 172800.
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def lst(self):
|
|
79
|
+
"""Mean local sidereal time of the observation in hours."""
|
|
80
|
+
return utils.get_lst(self.mjd_mid, self.settings.longitude)
|
|
81
|
+
|
|
82
|
+
def set_flag(self, flag):
|
|
83
|
+
self.flag = flag
|
|
84
|
+
self._invalidate_cache()
|
|
85
|
+
|
|
86
|
+
def filter_uvrange(self, umin, umax):
|
|
87
|
+
self.uvrange = [umin, umax]
|
|
88
|
+
self._invalidate_cache()
|
|
89
|
+
|
|
90
|
+
def average_freqs(self, nfreqs):
|
|
91
|
+
self.nfreqs = nfreqs
|
|
92
|
+
self._invalidate_cache()
|
|
93
|
+
|
|
94
|
+
def do_flag(self, flagger_file=None, verbose_flagging=True,
|
|
95
|
+
plot_flagging=True, flagger_runner=None):
|
|
96
|
+
if flagger_runner is None:
|
|
97
|
+
if flagger_file is None:
|
|
98
|
+
flagger_file = self.settings.power_spectra.get_path('flagger')
|
|
99
|
+
flagger_runner = flagger.FlaggerRunner.load(flagger_file)
|
|
100
|
+
|
|
101
|
+
flagger_runner.verbose = verbose_flagging
|
|
102
|
+
i_stokes, i_cube = self._get_first_stokes(['I', 'XX', 'YY'])
|
|
103
|
+
filtered_i, _ = flagger_runner.run(
|
|
104
|
+
i_cube, self.get_stokes_v_like())
|
|
105
|
+
if plot_flagging:
|
|
106
|
+
flagger_runner.plot()
|
|
107
|
+
|
|
108
|
+
self.flag = flagger_runner.flag
|
|
109
|
+
self._invalidate_cache()
|
|
110
|
+
self.data[i_stokes] = filtered_i
|
|
111
|
+
|
|
112
|
+
def get_h5_file(self, stokes):
|
|
113
|
+
return self.vis_rev.get_h5_file(self.obs_id, stokes, lst_idx=self.lst_idx)
|
|
114
|
+
|
|
115
|
+
def get_stokes(self, stokes):
|
|
116
|
+
assert self.has_stokes(stokes)
|
|
117
|
+
|
|
118
|
+
if stokes not in self.data:
|
|
119
|
+
path = self.get_h5_file(stokes)
|
|
120
|
+
cube = datacube.CartDataCube.load(path)
|
|
121
|
+
if self.uvrange is not None:
|
|
122
|
+
cube.filter_uvrange(self.uvrange[0], self.uvrange[1])
|
|
123
|
+
if self.nfreqs is not None:
|
|
124
|
+
cube = cube.average_freqs(self.nfreqs)
|
|
125
|
+
if self.flag is not None:
|
|
126
|
+
cube = self.flag.apply(cube)
|
|
127
|
+
self.data[stokes] = cube
|
|
128
|
+
return self.data[stokes]
|
|
129
|
+
|
|
130
|
+
def has_stokes(self, stokes):
|
|
131
|
+
return stokes in self.data or os.path.exists(self.get_h5_file(stokes))
|
|
132
|
+
|
|
133
|
+
def get_ml_gpr_path(self, eor_bin, umin=50, umax=250, name='', sampler_method='mcmc'):
|
|
134
|
+
return os.path.join(os.path.dirname(self.get_h5_file('I')), name, f'eor{eor_bin}_u{umin}-{umax}')
|
|
135
|
+
|
|
136
|
+
def get_ml_gpr_res(self, eor_bin, umin=50, umax=250, name='', sampler_method='mcmc'):
|
|
137
|
+
from ps_eor import ml_gpr
|
|
138
|
+
|
|
139
|
+
path = self.get_ml_gpr_path(eor_bin, umin=umin, umax=umax, name=name, sampler_method=sampler_method)
|
|
140
|
+
return ml_gpr.MLGPRResult.load(path, 'ml_gpr_res_I', sampler_method=sampler_method)
|
|
141
|
+
|
|
142
|
+
def get_ml_gpr_inj_path(self, eor_bin, var_21_over_noise, x1, x2, umin=50, umax=250, name=''):
|
|
143
|
+
d = f'inj_eor{eor_bin}_u{umin}-{umax}_a{var_21_over_noise:.3f}_x1{x1:.3f}_x2{x2:.3f}'
|
|
144
|
+
return os.path.join(os.path.dirname(self.get_h5_file('I')), name, d)
|
|
145
|
+
|
|
146
|
+
def get_ml_gpr_inj_res(self, eor_bin, var_21_over_noise, x1, x2, umin=50, umax=250, name='', sampler_method='mcmc'):
|
|
147
|
+
from ps_eor import ml_gpr
|
|
148
|
+
|
|
149
|
+
path = self.get_ml_gpr_inj_path(eor_bin, var_21_over_noise=var_21_over_noise, x1=x1, x2=x2, umin=umin, umax=umax, name=name)
|
|
150
|
+
data_inj = datacube.CartDataCube.load(os.path.join(path, 'c_injected.h5'))
|
|
151
|
+
return ml_gpr.MLGPRInjResult.load(path, 'ml_gpr_res_I', data_inj, sampler_method=sampler_method)
|
|
152
|
+
|
|
153
|
+
def get_ps_gen(self, **kargs):
|
|
154
|
+
return self.ps_builder.get(self.get_stokes_i_like(), **kargs)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class ObsDataGroup(object):
|
|
158
|
+
"""A group of observations exposed through a combined ObsData-like view."""
|
|
159
|
+
|
|
160
|
+
def __init__(self, observations, name, umin, umax, weighting_mode='full',
|
|
161
|
+
observation_weights=None, index=None, lst_min=None, lst_max=None):
|
|
162
|
+
self.observations = tuple(observations)
|
|
163
|
+
if not self.observations:
|
|
164
|
+
raise ValueError('observations must not be empty')
|
|
165
|
+
|
|
166
|
+
self.name = name
|
|
167
|
+
self.obs_id = name
|
|
168
|
+
self.index = index
|
|
169
|
+
self.lst_min = lst_min
|
|
170
|
+
self.lst_max = lst_max
|
|
171
|
+
self.umin = umin
|
|
172
|
+
self.umax = umax
|
|
173
|
+
self.weighting_mode = weighting_mode
|
|
174
|
+
self._stokes_cache = {}
|
|
175
|
+
|
|
176
|
+
obs_ids = [obs.obs_id for obs in self.observations]
|
|
177
|
+
if len(set(obs_ids)) != len(obs_ids):
|
|
178
|
+
raise ValueError('observations must have unique obs_id values')
|
|
179
|
+
|
|
180
|
+
if observation_weights is None:
|
|
181
|
+
self.observation_weights = dict.fromkeys(obs_ids, 1.)
|
|
182
|
+
else:
|
|
183
|
+
try:
|
|
184
|
+
self.observation_weights = {
|
|
185
|
+
obs_id: float(weight)
|
|
186
|
+
for obs_id, weight in dict(observation_weights).items()}
|
|
187
|
+
except (TypeError, ValueError):
|
|
188
|
+
raise ValueError(
|
|
189
|
+
'observation_weights must contain real scalar values')
|
|
190
|
+
missing = set(obs_ids) - set(self.observation_weights)
|
|
191
|
+
unknown = set(self.observation_weights) - set(obs_ids)
|
|
192
|
+
if missing or unknown:
|
|
193
|
+
raise ValueError(
|
|
194
|
+
'observation_weights must contain exactly the group obs_ids')
|
|
195
|
+
if any(
|
|
196
|
+
not np.isfinite(weight) or weight < 0
|
|
197
|
+
for weight in self.observation_weights.values()):
|
|
198
|
+
raise ValueError(
|
|
199
|
+
'observation_weights must be finite and non-negative')
|
|
200
|
+
|
|
201
|
+
self.all_stokes = dict(self.observations[0].all_stokes)
|
|
202
|
+
|
|
203
|
+
def __len__(self):
|
|
204
|
+
return len(self.observations)
|
|
205
|
+
|
|
206
|
+
def __getattr__(self, key):
|
|
207
|
+
all_stokes = self.__dict__.get('all_stokes', {})
|
|
208
|
+
if key not in all_stokes:
|
|
209
|
+
raise AttributeError(
|
|
210
|
+
f'{type(self).__name__} has no attribute {key!r}')
|
|
211
|
+
return self.get_stokes(all_stokes[key])
|
|
212
|
+
|
|
213
|
+
@property
|
|
214
|
+
def obs_ids(self):
|
|
215
|
+
return np.array([obs.obs_id for obs in self.observations])
|
|
216
|
+
|
|
217
|
+
@property
|
|
218
|
+
def lst(self):
|
|
219
|
+
if self.lst_min is not None and self.lst_max is not None:
|
|
220
|
+
return .5 * (self.lst_min + self.lst_max)
|
|
221
|
+
return np.mean(self.lsts)
|
|
222
|
+
|
|
223
|
+
@property
|
|
224
|
+
def lsts(self):
|
|
225
|
+
return np.array([obs.lst for obs in self.observations])
|
|
226
|
+
|
|
227
|
+
@property
|
|
228
|
+
def mjd_mid(self):
|
|
229
|
+
return np.array([obs.mjd_mid for obs in self.observations])
|
|
230
|
+
|
|
231
|
+
@property
|
|
232
|
+
def total_time(self):
|
|
233
|
+
return sum(
|
|
234
|
+
obs.get_stokes_i_like().meta.total_time
|
|
235
|
+
for obs in self.observations)
|
|
236
|
+
|
|
237
|
+
def clear_cache(self):
|
|
238
|
+
"""Discard combined Stokes cubes cached by this group."""
|
|
239
|
+
self._stokes_cache = {}
|
|
240
|
+
|
|
241
|
+
def has_stokes(self, stokes):
|
|
242
|
+
"""Return whether every observation provides ``stokes``."""
|
|
243
|
+
return all(obs.has_stokes(stokes) for obs in self.observations)
|
|
244
|
+
|
|
245
|
+
def get_observation_cubes(self, stokes):
|
|
246
|
+
"""Return the individual cubes for ``stokes`` in group order."""
|
|
247
|
+
if not self.has_stokes(stokes):
|
|
248
|
+
raise ValueError(
|
|
249
|
+
f'Stokes {stokes!r} is not available in every observation')
|
|
250
|
+
return tuple(obs.get_stokes(stokes) for obs in self.observations)
|
|
251
|
+
|
|
252
|
+
def get_stokes(self, stokes):
|
|
253
|
+
"""Return the lazily combined cube for ``stokes``."""
|
|
254
|
+
if stokes not in self._stokes_cache:
|
|
255
|
+
combiner = datacube.DataCubeCombiner(
|
|
256
|
+
self.umin, self.umax, weighting_mode=self.weighting_mode)
|
|
257
|
+
|
|
258
|
+
for obs, cube in zip(
|
|
259
|
+
self.observations,
|
|
260
|
+
self.get_observation_cubes(stokes)):
|
|
261
|
+
cube = cube.copy()
|
|
262
|
+
if cube.weights is None:
|
|
263
|
+
raise ValueError(
|
|
264
|
+
f'Stokes {stokes!r} for {obs.obs_id} has no weights')
|
|
265
|
+
cube.weights.uv_scale *= self.observation_weights[obs.obs_id]
|
|
266
|
+
combiner.add(cube, obs.obs_id)
|
|
267
|
+
|
|
268
|
+
self._stokes_cache[stokes] = combiner.get()
|
|
269
|
+
|
|
270
|
+
return self._stokes_cache[stokes]
|
|
271
|
+
|
|
272
|
+
def get_stokes_i_like(self):
|
|
273
|
+
for stokes in ['I', 'XX', 'YY']:
|
|
274
|
+
if self.has_stokes(stokes):
|
|
275
|
+
return self.get_stokes(stokes)
|
|
276
|
+
|
|
277
|
+
def get_stokes_v_like(self):
|
|
278
|
+
for stokes in ['V', 'XY', 'YX', 'XX', 'YY', 'I']:
|
|
279
|
+
if self.has_stokes(stokes):
|
|
280
|
+
return self.get_stokes(stokes)
|
|
281
|
+
|
|
282
|
+
def get_ps_gen(self, **kargs):
|
|
283
|
+
return self.observations[0].ps_builder.get(
|
|
284
|
+
self.get_stokes_i_like(), **kargs)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
class ObsDataCollection(object):
|
|
288
|
+
"""Observation groups indexed by contiguous LST bins."""
|
|
289
|
+
|
|
290
|
+
def __init__(self, lst_bins, groups):
|
|
291
|
+
self.lst_bins = np.asarray(lst_bins, dtype=float)
|
|
292
|
+
if self.lst_bins.ndim != 1 or self.lst_bins.size < 2:
|
|
293
|
+
raise ValueError('lst_bins must be a 1D array with at least two edges')
|
|
294
|
+
if not np.all(np.diff(self.lst_bins) > 0):
|
|
295
|
+
raise ValueError('lst_bins must be strictly increasing')
|
|
296
|
+
|
|
297
|
+
self.groups = tuple(groups)
|
|
298
|
+
indices = [group.index for group in self.groups]
|
|
299
|
+
if any(index is None for index in indices):
|
|
300
|
+
raise ValueError('collection groups must have a bin index')
|
|
301
|
+
if len(set(indices)) != len(indices):
|
|
302
|
+
raise ValueError('collection groups must have unique bin indices')
|
|
303
|
+
if any(
|
|
304
|
+
index < 0 or index >= len(self.lst_bins) - 1
|
|
305
|
+
for index in indices):
|
|
306
|
+
raise ValueError('collection group index outside lst_bins')
|
|
307
|
+
|
|
308
|
+
def __iter__(self):
|
|
309
|
+
return iter(self.groups)
|
|
310
|
+
|
|
311
|
+
def __len__(self):
|
|
312
|
+
return len(self.groups)
|
|
313
|
+
|
|
314
|
+
@property
|
|
315
|
+
def observations(self):
|
|
316
|
+
return tuple(
|
|
317
|
+
obs for group in self.groups for obs in group.observations)
|
|
318
|
+
|
|
319
|
+
@property
|
|
320
|
+
def obs_ids(self):
|
|
321
|
+
return np.array([obs.obs_id for obs in self.observations])
|
|
322
|
+
|
|
323
|
+
def get_bin(self, index):
|
|
324
|
+
"""Return the populated group for bin ``index``, or ``None``."""
|
|
325
|
+
for group in self.groups:
|
|
326
|
+
if group.index == index:
|
|
327
|
+
return group
|
|
328
|
+
return None
|
|
329
|
+
|
|
330
|
+
def select_lst(self, lst):
|
|
331
|
+
"""Return the populated group containing ``lst``, or ``None``."""
|
|
332
|
+
index = np.searchsorted(self.lst_bins, lst, side='right') - 1
|
|
333
|
+
if index < 0 or index >= len(self.lst_bins) - 1:
|
|
334
|
+
return None
|
|
335
|
+
return self.get_bin(index)
|
|
336
|
+
|
|
337
|
+
@classmethod
|
|
338
|
+
def from_revision(cls, vis_rev, obs_ids, lst_bins, *, umin, umax,
|
|
339
|
+
required_stokes=('I', 'V', 'dt_I'),
|
|
340
|
+
excluded_obs_ids=None, flagger_parset=None,
|
|
341
|
+
weighting_mode='full', observation_weights=None,
|
|
342
|
+
verbose=True):
|
|
343
|
+
"""Load observations from a revision and group them by their LST."""
|
|
344
|
+
lst_bins = np.asarray(lst_bins, dtype=float)
|
|
345
|
+
if lst_bins.ndim != 1 or lst_bins.size < 2:
|
|
346
|
+
raise ValueError('lst_bins must be a 1D array with at least two edges')
|
|
347
|
+
if not np.all(np.diff(lst_bins) > 0):
|
|
348
|
+
raise ValueError('lst_bins must be strictly increasing')
|
|
349
|
+
|
|
350
|
+
excluded_obs_ids = set(
|
|
351
|
+
() if excluded_obs_ids is None else excluded_obs_ids)
|
|
352
|
+
grouped = [[] for _ in range(len(lst_bins) - 1)]
|
|
353
|
+
flagger_runner = None
|
|
354
|
+
|
|
355
|
+
for obs_id in obs_ids:
|
|
356
|
+
if obs_id in excluded_obs_ids:
|
|
357
|
+
continue
|
|
358
|
+
|
|
359
|
+
obs = vis_rev.get_data(obs_id)
|
|
360
|
+
missing = [
|
|
361
|
+
stokes for stokes in required_stokes
|
|
362
|
+
if not obs.has_stokes(stokes)]
|
|
363
|
+
if missing:
|
|
364
|
+
if verbose:
|
|
365
|
+
print(f'Warning: {obs_id} missing Stokes {missing}')
|
|
366
|
+
continue
|
|
367
|
+
|
|
368
|
+
obs.filter_uvrange(umin, umax)
|
|
369
|
+
if flagger_parset is not None:
|
|
370
|
+
if flagger_runner is None:
|
|
371
|
+
flagger_runner = flagger.FlaggerRunner.load(
|
|
372
|
+
flagger_parset)
|
|
373
|
+
obs.do_flag(
|
|
374
|
+
flagger_parset, False, False,
|
|
375
|
+
flagger_runner=flagger_runner)
|
|
376
|
+
|
|
377
|
+
index = np.searchsorted(lst_bins, obs.lst, side='right') - 1
|
|
378
|
+
if 0 <= index < len(grouped):
|
|
379
|
+
grouped[index].append(obs)
|
|
380
|
+
|
|
381
|
+
groups = []
|
|
382
|
+
for index, observations in enumerate(grouped):
|
|
383
|
+
if not observations:
|
|
384
|
+
continue
|
|
385
|
+
|
|
386
|
+
weights = None
|
|
387
|
+
if observation_weights is not None:
|
|
388
|
+
weights = {
|
|
389
|
+
obs.obs_id: observation_weights[obs.obs_id]
|
|
390
|
+
for obs in observations}
|
|
391
|
+
|
|
392
|
+
groups.append(ObsDataGroup(
|
|
393
|
+
observations,
|
|
394
|
+
f'lst_{.5 * (lst_bins[index] + lst_bins[index + 1]):.2f}',
|
|
395
|
+
umin,
|
|
396
|
+
umax,
|
|
397
|
+
weighting_mode=weighting_mode,
|
|
398
|
+
observation_weights=weights,
|
|
399
|
+
index=index,
|
|
400
|
+
lst_min=lst_bins[index],
|
|
401
|
+
lst_max=lst_bins[index + 1],
|
|
402
|
+
))
|
|
403
|
+
|
|
404
|
+
return cls(lst_bins, groups)
|
|
405
|
+
|
|
406
|
+
@classmethod
|
|
407
|
+
def from_revision_interleaved(
|
|
408
|
+
cls, vis_rev, obs_ids, lst_bins, *, umin, umax,
|
|
409
|
+
required_stokes=('I', 'V', 'dt_I'),
|
|
410
|
+
excluded_obs_ids=None, flagger_parset=None,
|
|
411
|
+
weighting_mode='full', observation_weights=None,
|
|
412
|
+
verbose=True):
|
|
413
|
+
"""Load observations into groups of alternating uniform LST bins.
|
|
414
|
+
|
|
415
|
+
The first group contains bins 0, 2, 4, ... and the second contains
|
|
416
|
+
bins 1, 3, 5, .... Bin parity follows ``lst_bins`` even when a bin is
|
|
417
|
+
empty. The result is suitable for constructing two disjoint datasets
|
|
418
|
+
with similar LST coverage.
|
|
419
|
+
|
|
420
|
+
Returns:
|
|
421
|
+
``(even_group, odd_group)`` as two :class:`ObsDataGroup` objects.
|
|
422
|
+
"""
|
|
423
|
+
lst_bins = np.asarray(lst_bins, dtype=float)
|
|
424
|
+
if lst_bins.ndim != 1 or lst_bins.size < 3:
|
|
425
|
+
raise ValueError(
|
|
426
|
+
'lst_bins must be a 1D array with at least three edges')
|
|
427
|
+
widths = np.diff(lst_bins)
|
|
428
|
+
if not np.all(widths > 0):
|
|
429
|
+
raise ValueError('lst_bins must be strictly increasing')
|
|
430
|
+
if not np.allclose(widths, widths[0]):
|
|
431
|
+
raise ValueError(
|
|
432
|
+
'interleaved LST groups require uniformly spaced bins')
|
|
433
|
+
|
|
434
|
+
collection = cls.from_revision(
|
|
435
|
+
vis_rev,
|
|
436
|
+
obs_ids,
|
|
437
|
+
lst_bins,
|
|
438
|
+
umin=umin,
|
|
439
|
+
umax=umax,
|
|
440
|
+
required_stokes=required_stokes,
|
|
441
|
+
excluded_obs_ids=excluded_obs_ids,
|
|
442
|
+
flagger_parset=flagger_parset,
|
|
443
|
+
weighting_mode=weighting_mode,
|
|
444
|
+
observation_weights=observation_weights,
|
|
445
|
+
verbose=verbose,
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
interleaved = []
|
|
449
|
+
for parity, name in enumerate(('lst_even', 'lst_odd')):
|
|
450
|
+
source_groups = [
|
|
451
|
+
group for group in collection.groups
|
|
452
|
+
if group.index % 2 == parity]
|
|
453
|
+
observations = [
|
|
454
|
+
obs
|
|
455
|
+
for group in source_groups
|
|
456
|
+
for obs in group.observations]
|
|
457
|
+
if not observations:
|
|
458
|
+
raise ValueError(
|
|
459
|
+
f'no observations found in {name.replace("_", " ")}')
|
|
460
|
+
|
|
461
|
+
weights = {
|
|
462
|
+
obs.obs_id: group.observation_weights[obs.obs_id]
|
|
463
|
+
for group in source_groups
|
|
464
|
+
for obs in group.observations}
|
|
465
|
+
interleaved.append(ObsDataGroup(
|
|
466
|
+
observations,
|
|
467
|
+
name,
|
|
468
|
+
umin,
|
|
469
|
+
umax,
|
|
470
|
+
weighting_mode=weighting_mode,
|
|
471
|
+
observation_weights=weights,
|
|
472
|
+
index=parity,
|
|
473
|
+
))
|
|
474
|
+
|
|
475
|
+
return tuple(interleaved)
|
|
@@ -107,6 +107,8 @@ class AbstractTask(object):
|
|
|
107
107
|
def execute(self):
|
|
108
108
|
self.worker.execute()
|
|
109
109
|
|
|
110
|
+
return self.get_report().ok
|
|
111
|
+
|
|
110
112
|
def execute_single_local(self, cmd, log_file):
|
|
111
113
|
w = worker.WorkerPool([worker.localhost_shortname], name=self.name,
|
|
112
114
|
env_source_file=self.settings.worker.env_file)
|
|
@@ -115,6 +117,9 @@ class AbstractTask(object):
|
|
|
115
117
|
print(cmd)
|
|
116
118
|
w.execute()
|
|
117
119
|
|
|
120
|
+
def get_report(self):
|
|
121
|
+
return worker.WorkerPoolReport(self.worker.tasks)
|
|
122
|
+
|
|
118
123
|
|
|
119
124
|
class ImagingSubTask(AbstractSubTask):
|
|
120
125
|
|
|
@@ -133,6 +138,9 @@ class ImagingSubTask(AbstractSubTask):
|
|
|
133
138
|
def get_dir(self):
|
|
134
139
|
return self.img_rev.get_image_path(self.obs_id)
|
|
135
140
|
|
|
141
|
+
def get_image_data_dir(self):
|
|
142
|
+
return self.img_rev.get_image_data_path(self.obs_id, persist=True)
|
|
143
|
+
|
|
136
144
|
def get_sb(self):
|
|
137
145
|
res = re.search(r'_SB(\d+)', os.path.basename(self.inp_ms_files[0]))
|
|
138
146
|
if res is not None:
|
|
@@ -149,10 +157,16 @@ class ImagingSubTask(AbstractSubTask):
|
|
|
149
157
|
eo_str = database.even_odd_str(self.even, self.odd)
|
|
150
158
|
w_str = utils.alphanum(img.weight)
|
|
151
159
|
s = f'{self.obs_id}{eo_str}-SB{self.get_sb()}-UV{img.umin}_{img.umax}_{w_str}'
|
|
152
|
-
|
|
160
|
+
|
|
161
|
+
out_prefix = os.path.join(self.get_image_data_dir(), s)
|
|
162
|
+
os.makedirs(os.path.dirname(out_prefix), exist_ok=True)
|
|
163
|
+
|
|
164
|
+
return out_prefix
|
|
153
165
|
|
|
154
166
|
def register_files(self, files, lst_idx=None, img_name='image'):
|
|
155
167
|
meta = self.img_rev.get_meta(self.obs_id, lst_idx=lst_idx)
|
|
168
|
+
if meta is None:
|
|
169
|
+
meta = self.img_rev.new_obs_id(self.obs_id, lst_idx=lst_idx)
|
|
156
170
|
meta.add_fits(files, even=self.even, odd=self.odd, img_name=img_name)
|
|
157
171
|
meta.save()
|
|
158
172
|
|
|
@@ -300,26 +314,28 @@ class ImagingTask(AbstractTask):
|
|
|
300
314
|
print(f'Error: Obs ID {obs_id} has no MS files registered.')
|
|
301
315
|
return []
|
|
302
316
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
317
|
+
data_dir_root = self.img_rev.get_distributed_data_root(obs_id, persist=False)
|
|
318
|
+
|
|
319
|
+
self.img_rev.new_obs_id(obs_id, data_dir_root=data_dir_root)
|
|
320
|
+
if self.settings.image.lst_bins:
|
|
306
321
|
for i in self.img_rev.get_lst_bins(obs_id).keys():
|
|
307
322
|
self.img_rev.new_obs_id(obs_id, lst_idx=i)
|
|
308
323
|
|
|
309
324
|
for i, ms_files_channel in enumerate(ms_files):
|
|
310
|
-
|
|
325
|
+
file_host = get_target_host(ms_files_channel[0], self.settings.worker)
|
|
311
326
|
if self.settings.image.split_even_odd:
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
327
|
+
t = ImagingSubTask(self.settings, obs_id, ms_files_channel, even=True, index=i)
|
|
328
|
+
self.add_sub_task(t, run_on_host=file_host)
|
|
329
|
+
t = ImagingSubTask(self.settings, obs_id, ms_files_channel, odd=True, index=i)
|
|
330
|
+
self.add_sub_task(t, run_on_host=file_host)
|
|
316
331
|
else:
|
|
317
|
-
|
|
318
|
-
|
|
332
|
+
t = ImagingSubTask(self.settings, obs_id, ms_files_channel, index=i)
|
|
333
|
+
self.add_sub_task(t, run_on_host=file_host)
|
|
319
334
|
|
|
320
335
|
all_obs_id_out.append(obs_id)
|
|
321
336
|
|
|
322
|
-
self.execute()
|
|
337
|
+
if not self.execute():
|
|
338
|
+
return []
|
|
323
339
|
|
|
324
340
|
return all_obs_id_out
|
|
325
341
|
|
|
@@ -443,7 +459,8 @@ class GenVisCubeTask(AbstractTask):
|
|
|
443
459
|
|
|
444
460
|
all_obs_id_out.append(obs_id)
|
|
445
461
|
|
|
446
|
-
self.execute()
|
|
462
|
+
if not self.execute():
|
|
463
|
+
return []
|
|
447
464
|
|
|
448
465
|
if has_even.intersection(has_odd):
|
|
449
466
|
self.init_for_new_task('EvenOddToSumDiff')
|
|
@@ -458,7 +475,8 @@ class GenVisCubeTask(AbstractTask):
|
|
|
458
475
|
if meta.has_stokes(stokes, odd=True) and meta.has_stokes(stokes, even=True):
|
|
459
476
|
self.add_sub_task(EvenOddToSumDiffSubTask(self.settings, obs_id, stokes, lst_idx))
|
|
460
477
|
|
|
461
|
-
self.execute()
|
|
478
|
+
if not self.execute():
|
|
479
|
+
return []
|
|
462
480
|
|
|
463
481
|
return all_obs_id_out
|
|
464
482
|
|
|
@@ -535,7 +553,9 @@ class ConcatMSTask(AbstractTask):
|
|
|
535
553
|
self.add_sub_task(ConcatMSSubTask(self.settings, msfiles_flat, obs_id_out))
|
|
536
554
|
all_obs_id_out.append(obs_id_out)
|
|
537
555
|
|
|
538
|
-
self.execute()
|
|
556
|
+
if not self.execute():
|
|
557
|
+
return []
|
|
558
|
+
|
|
539
559
|
return all_obs_id_out
|
|
540
560
|
|
|
541
561
|
|
|
@@ -645,7 +665,8 @@ class MergeMSTask(AbstractTask):
|
|
|
645
665
|
self.add_sub_task(MergeMSSubTask(self.settings, ms_files, obs_id_out))
|
|
646
666
|
all_obs_id_out.append(obs_id_out)
|
|
647
667
|
|
|
648
|
-
self.execute()
|
|
668
|
+
if not self.execute():
|
|
669
|
+
return []
|
|
649
670
|
|
|
650
671
|
return all_obs_id_out
|
|
651
672
|
|
|
@@ -695,7 +716,8 @@ class AOFlaggerTask(AbstractTask):
|
|
|
695
716
|
self.add_sub_task(AOFlaggerSubTask(self.settings, ms_file))
|
|
696
717
|
all_obs_id_out.append(obs_id)
|
|
697
718
|
|
|
698
|
-
self.execute()
|
|
719
|
+
if not self.execute():
|
|
720
|
+
return []
|
|
699
721
|
|
|
700
722
|
return all_obs_id_out
|
|
701
723
|
|
|
@@ -741,7 +763,8 @@ class DP3Task(AbstractTask):
|
|
|
741
763
|
self.add_sub_task(DP3SubTask(self.settings, ms_file))
|
|
742
764
|
all_obs_id_out.append(obs_id)
|
|
743
765
|
|
|
744
|
-
self.execute()
|
|
766
|
+
if not self.execute():
|
|
767
|
+
return []
|
|
745
768
|
|
|
746
769
|
return all_obs_id_out
|
|
747
770
|
|
|
@@ -800,7 +823,8 @@ class MakePSTask(AbstractTask):
|
|
|
800
823
|
for lst_idx in all_lst_idx:
|
|
801
824
|
self.add_sub_task(MakePSSubTask(self.settings, obs_id, lst_idx))
|
|
802
825
|
|
|
803
|
-
self.execute()
|
|
826
|
+
if not self.execute():
|
|
827
|
+
return []
|
|
804
828
|
|
|
805
829
|
return obs_ids
|
|
806
830
|
|
|
@@ -867,7 +891,8 @@ class RunGPRTask(AbstractTask):
|
|
|
867
891
|
for lst_idx in all_lst_idx:
|
|
868
892
|
self.add_sub_task(RunGPRSubTask(self.settings, obs_id, lst_idx))
|
|
869
893
|
|
|
870
|
-
self.execute()
|
|
894
|
+
if not self.execute():
|
|
895
|
+
return []
|
|
871
896
|
|
|
872
897
|
return obs_ids
|
|
873
898
|
|
|
@@ -936,7 +961,8 @@ class RunMLGPRTask(AbstractTask):
|
|
|
936
961
|
for lst_idx in all_lst_idx:
|
|
937
962
|
self.add_sub_task(RunMLGPRSubTask(self.settings, obs_id, lst_idx))
|
|
938
963
|
|
|
939
|
-
self.execute()
|
|
964
|
+
if not self.execute():
|
|
965
|
+
return []
|
|
940
966
|
|
|
941
967
|
return obs_ids
|
|
942
968
|
|
|
@@ -1027,7 +1053,8 @@ class RunMLGPRInjTask(AbstractTask):
|
|
|
1027
1053
|
|
|
1028
1054
|
self.add_sub_task(RunMLGPRInjSubTask(self.settings, obs_id, 0, 0, 0, lst_idx))
|
|
1029
1055
|
|
|
1030
|
-
self.execute()
|
|
1056
|
+
if not self.execute():
|
|
1057
|
+
return []
|
|
1031
1058
|
|
|
1032
1059
|
return obs_ids
|
|
1033
1060
|
|
|
@@ -1193,7 +1220,9 @@ class VisToSphTask(AbstractTask):
|
|
|
1193
1220
|
|
|
1194
1221
|
self.add_sub_task(RunFlaggerSubTask(self.settings, obs_id))
|
|
1195
1222
|
|
|
1196
|
-
self.execute()
|
|
1223
|
+
if not self.execute():
|
|
1224
|
+
return []
|
|
1225
|
+
|
|
1197
1226
|
self.init_for_new_task('VisToSph S2')
|
|
1198
1227
|
new_obs_ids = []
|
|
1199
1228
|
|
|
@@ -1213,7 +1242,8 @@ class VisToSphTask(AbstractTask):
|
|
|
1213
1242
|
|
|
1214
1243
|
new_obs_ids.append(obs_id_out)
|
|
1215
1244
|
|
|
1216
|
-
self.execute()
|
|
1245
|
+
if not self.execute():
|
|
1246
|
+
return []
|
|
1217
1247
|
|
|
1218
1248
|
return new_obs_ids
|
|
1219
1249
|
|
|
@@ -1263,7 +1293,8 @@ class ApplyFlagTask(AbstractTask):
|
|
|
1263
1293
|
|
|
1264
1294
|
self.add_sub_task(ApplyFlagSubTask(self.settings, ms_file, h5_file))
|
|
1265
1295
|
|
|
1266
|
-
self.execute()
|
|
1296
|
+
if not self.execute():
|
|
1297
|
+
return []
|
|
1267
1298
|
|
|
1268
1299
|
return obs_ids
|
|
1269
1300
|
|
|
@@ -1298,7 +1329,8 @@ class RestoreFlagTask(AbstractTask):
|
|
|
1298
1329
|
|
|
1299
1330
|
self.add_sub_task(ApplyFlagSubTask(self.settings, ms_file, h5_file))
|
|
1300
1331
|
|
|
1301
|
-
self.execute()
|
|
1332
|
+
if not self.execute():
|
|
1333
|
+
return []
|
|
1302
1334
|
|
|
1303
1335
|
return obs_ids
|
|
1304
1336
|
|
|
@@ -1353,7 +1385,8 @@ class DelayFlagger(AbstractTask):
|
|
|
1353
1385
|
for ms_file in np.array(ms_files).flatten():
|
|
1354
1386
|
self.add_sub_task(DelayFlaggerSubTask(self.settings, ms_file))
|
|
1355
1387
|
|
|
1356
|
-
self.execute()
|
|
1388
|
+
if not self.execute():
|
|
1389
|
+
return []
|
|
1357
1390
|
|
|
1358
1391
|
return obs_ids
|
|
1359
1392
|
|
|
@@ -1402,7 +1435,8 @@ class VisFlagger(AbstractTask):
|
|
|
1402
1435
|
for ms_file in np.array(ms_files).flatten():
|
|
1403
1436
|
self.add_sub_task(VisFlaggerSubTask(self.settings, ms_file))
|
|
1404
1437
|
|
|
1405
|
-
self.execute()
|
|
1438
|
+
if not self.execute():
|
|
1439
|
+
return []
|
|
1406
1440
|
|
|
1407
1441
|
return obs_ids
|
|
1408
1442
|
|
|
@@ -1461,6 +1495,7 @@ class SSINSTask(AbstractTask):
|
|
|
1461
1495
|
for ms_file in np.array(ms_files).flatten():
|
|
1462
1496
|
self.add_sub_task(SSINSSubTask(self.settings, ms_file))
|
|
1463
1497
|
|
|
1464
|
-
self.execute()
|
|
1498
|
+
if not self.execute():
|
|
1499
|
+
return []
|
|
1465
1500
|
|
|
1466
1501
|
return obs_ids
|
|
@@ -13,6 +13,25 @@ from .psdb import load_settings
|
|
|
13
13
|
tasks_help = '\n'.join([f' {name.ljust(17)}{desc}' for name, desc in tasks.get_all_tasks_descriptions().items()])
|
|
14
14
|
|
|
15
15
|
|
|
16
|
+
def print_report(report):
|
|
17
|
+
print("\n=== Summary of completed tasks ===")
|
|
18
|
+
print(f"Successful tasks (rc=0): {report.n_success_rc0} / {report.n_total}")
|
|
19
|
+
print(f"Completed with non-zero rc: {report.n_success_nonzero} / {report.n_total}")
|
|
20
|
+
print(f"Failed tasks (exception/kill): {report.n_failed} / {report.n_total}")
|
|
21
|
+
|
|
22
|
+
if report.success_nonzero:
|
|
23
|
+
print("\nTasks completed with non-zero return code:")
|
|
24
|
+
for t in report.success_nonzero:
|
|
25
|
+
print(f" - {t.name} | host={report.host_of(t)} | rc={t.returncode}")
|
|
26
|
+
|
|
27
|
+
if report.failed:
|
|
28
|
+
print("\nFailed tasks:")
|
|
29
|
+
for t in report.failed:
|
|
30
|
+
print(f" - {t.name} | status={t.status.name} | host={report.host_of(t)} | rc={t.returncode}")
|
|
31
|
+
|
|
32
|
+
print("==========================\n")
|
|
33
|
+
|
|
34
|
+
|
|
16
35
|
@click.command(epilog='\b\nAvailable tasks:\n' + tasks_help)
|
|
17
36
|
@click.version_option(__version__)
|
|
18
37
|
@click.argument('tasks_str', metavar='TASKS')
|
|
@@ -39,12 +58,17 @@ def main(tasks_str, settings_file, obs_ids, config_modifier):
|
|
|
39
58
|
|
|
40
59
|
obs_ids_set = database.Database(s).filter_obs_ids(obs_ids)
|
|
41
60
|
|
|
42
|
-
if not
|
|
61
|
+
if not obs_ids_set:
|
|
43
62
|
print(f'Error: No obs_ids matching {obs_ids} in database.')
|
|
44
63
|
sys.exit(1)
|
|
45
64
|
|
|
46
65
|
for task_name in tasks_str.split(','):
|
|
47
|
-
|
|
66
|
+
t = all_tasks[task_name](s)
|
|
67
|
+
obs_ids_set = t.run(obs_ids_set)
|
|
68
|
+
report = t.get_report()
|
|
69
|
+
|
|
48
70
|
if not obs_ids_set:
|
|
49
|
-
print('
|
|
71
|
+
print(f'Task {task_name} finished with errors')
|
|
72
|
+
print_report(report)
|
|
50
73
|
sys.exit(1)
|
|
74
|
+
print_report(report)
|
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pspipe"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.8"
|
|
8
8
|
description = "Power spectra pipeline for Cosmic Dawn, Epoch of Reionization radio interferometric experiments"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
@@ -20,7 +20,7 @@ dependencies = [
|
|
|
20
20
|
"tables>=3.2",
|
|
21
21
|
"tabulate>=0.8",
|
|
22
22
|
"ps-eor>=0.8",
|
|
23
|
-
"libpipe>=0.
|
|
23
|
+
"libpipe>=0.6",
|
|
24
24
|
"python-casacore>=3.0",
|
|
25
25
|
]
|
|
26
26
|
|
|
@@ -34,3 +34,7 @@ pspipe = "pspipe.tools.pspipe:main"
|
|
|
34
34
|
|
|
35
35
|
[tool.poetry.group.dev.dependencies]
|
|
36
36
|
pytest = "^8.0"
|
|
37
|
+
|
|
38
|
+
[tool.pytest.ini_options]
|
|
39
|
+
testpaths = ["tests"]
|
|
40
|
+
addopts = "-ra"
|
pspipe-0.7.2/pspipe/obsdata.py
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
# Direct access to the data
|
|
2
|
-
#
|
|
3
|
-
# Author: F. Mertens
|
|
4
|
-
|
|
5
|
-
import os
|
|
6
|
-
|
|
7
|
-
from ps_eor import pspec, datacube, flagger
|
|
8
|
-
|
|
9
|
-
from . import database
|
|
10
|
-
|
|
11
|
-
# Lazy loaded:
|
|
12
|
-
# from ps_eor import fgfit
|
|
13
|
-
# from ps_eor import ml_gpr
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class ObsData(object):
|
|
17
|
-
|
|
18
|
-
def __init__(self, vis_rev, obs_id, lst_idx=None):
|
|
19
|
-
self.vis_rev = vis_rev
|
|
20
|
-
self.obs_id = obs_id
|
|
21
|
-
self.lst_idx = lst_idx
|
|
22
|
-
self.settings = self.vis_rev.settings
|
|
23
|
-
self.flag = None
|
|
24
|
-
self.uvrange = None
|
|
25
|
-
self.nfreqs = None
|
|
26
|
-
self.data = {}
|
|
27
|
-
self.all_stokes = {}
|
|
28
|
-
for stokes in database.all_stokes:
|
|
29
|
-
self.all_stokes[stokes.lower()] = stokes
|
|
30
|
-
self.all_stokes[stokes.lower() + '_even'] = 'even_%s' % stokes
|
|
31
|
-
self.all_stokes[stokes.lower() + '_odd'] = 'odd_%s' % stokes
|
|
32
|
-
self.all_stokes[stokes.lower() + '_dt'] = 'dt_%s' % stokes
|
|
33
|
-
self.ps_builder = pspec.PowerSpectraBuilder(self.settings.power_spectra.get_path('ps_config'),
|
|
34
|
-
self.settings.power_spectra.get_path('eor_bin_list'))
|
|
35
|
-
|
|
36
|
-
def __getattr__(self, key):
|
|
37
|
-
assert key in self.all_stokes.keys()
|
|
38
|
-
return self.get_stokes(self.all_stokes[key])
|
|
39
|
-
|
|
40
|
-
def _invalidate_cache(self):
|
|
41
|
-
self.data = {}
|
|
42
|
-
|
|
43
|
-
def get_stokes_i_like(self):
|
|
44
|
-
for s in ['I', 'XX', 'YY']:
|
|
45
|
-
if self.has_stokes(s):
|
|
46
|
-
return self.get_stokes(s)
|
|
47
|
-
|
|
48
|
-
def get_stokes_v_like(self):
|
|
49
|
-
for s in ['V', 'XY', 'YX', 'XX', 'YY', 'I']:
|
|
50
|
-
if self.has_stokes(s):
|
|
51
|
-
return self.get_stokes(s)
|
|
52
|
-
|
|
53
|
-
def set_flag(self, flag):
|
|
54
|
-
self.flag = flag
|
|
55
|
-
self._invalidate_cache()
|
|
56
|
-
|
|
57
|
-
def filter_uvrange(self, umin, umax):
|
|
58
|
-
self.uvrange = [umin, umax]
|
|
59
|
-
self._invalidate_cache()
|
|
60
|
-
|
|
61
|
-
def average_freqs(self, nfreqs):
|
|
62
|
-
self.nfreqs = nfreqs
|
|
63
|
-
self._invalidate_cache()
|
|
64
|
-
|
|
65
|
-
def do_flag(self, flagger_file=None, verbose_flagging=True, plot_flagging=True):
|
|
66
|
-
if flagger_file is None:
|
|
67
|
-
flagger_file = self.settings.power_spectra.get_path('flagger')
|
|
68
|
-
flagger_runner = flagger.FlaggerRunner.load(flagger_file)
|
|
69
|
-
flagger_runner.verbose = verbose_flagging
|
|
70
|
-
flagger_runner.run(self.get_stokes_i_like(), self.get_stokes_v_like())
|
|
71
|
-
if plot_flagging:
|
|
72
|
-
flagger_runner.plot()
|
|
73
|
-
self.set_flag(flagger_runner.flag)
|
|
74
|
-
|
|
75
|
-
def get_h5_file(self, stokes):
|
|
76
|
-
return self.vis_rev.get_h5_file(self.obs_id, stokes, lst_idx=self.lst_idx)
|
|
77
|
-
|
|
78
|
-
def get_stokes(self, stokes):
|
|
79
|
-
assert self.has_stokes(stokes)
|
|
80
|
-
|
|
81
|
-
if stokes not in self.data:
|
|
82
|
-
path = self.get_h5_file(stokes)
|
|
83
|
-
cube = datacube.CartDataCube.load(path)
|
|
84
|
-
if self.uvrange is not None:
|
|
85
|
-
cube.filter_uvrange(self.uvrange[0], self.uvrange[1])
|
|
86
|
-
if self.nfreqs is not None:
|
|
87
|
-
cube = cube.average_freqs(self.nfreqs)
|
|
88
|
-
if self.flag is not None:
|
|
89
|
-
cube = self.flag.apply(cube)
|
|
90
|
-
self.data[stokes] = cube
|
|
91
|
-
return self.data[stokes]
|
|
92
|
-
|
|
93
|
-
def has_stokes(self, stokes):
|
|
94
|
-
return stokes in self.data or os.path.exists(self.get_h5_file(stokes))
|
|
95
|
-
|
|
96
|
-
def get_gpr_res(self, eor_bin, umin=50, umax=250, stokes='I', name=''):
|
|
97
|
-
from ps_eor import fgfit
|
|
98
|
-
|
|
99
|
-
path = os.path.join(os.path.dirname(self.get_h5_file(stokes)), name, f'eor{eor_bin}_u{umin}-{umax}')
|
|
100
|
-
i_res = fgfit.GprForegroundResult.load(path, f'gpr_res_{stokes}')
|
|
101
|
-
i_noise = datacube.CartDataCube.load(os.path.join(path, 'gpr_res_I.noise.h5'))
|
|
102
|
-
i_res.noise = i_noise
|
|
103
|
-
|
|
104
|
-
return i_res
|
|
105
|
-
|
|
106
|
-
def get_ml_gpr_path(self, eor_bin, umin=50, umax=250, name='', sampler_method='mcmc'):
|
|
107
|
-
return os.path.join(os.path.dirname(self.get_h5_file('I')), name, f'eor{eor_bin}_u{umin}-{umax}')
|
|
108
|
-
|
|
109
|
-
def get_ml_gpr_res(self, eor_bin, umin=50, umax=250, name='', sampler_method='mcmc'):
|
|
110
|
-
from ps_eor import ml_gpr
|
|
111
|
-
|
|
112
|
-
path = self.get_ml_gpr_path(eor_bin, umin=umin, umax=umax, name=name, sampler_method=sampler_method)
|
|
113
|
-
return ml_gpr.MLGPRResult.load(path, 'ml_gpr_res_I', sampler_method=sampler_method)
|
|
114
|
-
|
|
115
|
-
def get_ml_gpr_inj_path(self, eor_bin, var_21_over_noise, x1, x2, umin=50, umax=250, name=''):
|
|
116
|
-
d = f'inj_eor{eor_bin}_u{umin}-{umax}_a{var_21_over_noise:.3f}_x1{x1:.3f}_x2{x2:.3f}'
|
|
117
|
-
return os.path.join(os.path.dirname(self.get_h5_file('I')), name, d)
|
|
118
|
-
|
|
119
|
-
def get_ml_gpr_inj_res(self, eor_bin, var_21_over_noise, x1, x2, umin=50, umax=250, name='', sampler_method='mcmc'):
|
|
120
|
-
from ps_eor import ml_gpr
|
|
121
|
-
|
|
122
|
-
path = self.get_ml_gpr_inj_path(eor_bin, var_21_over_noise=var_21_over_noise, x1=x1, x2=x2, umin=umin, umax=umax, name=name)
|
|
123
|
-
data_inj = datacube.CartDataCube.load(os.path.join(path, 'c_injected.h5'))
|
|
124
|
-
return ml_gpr.MLGPRInjResult.load(path, 'ml_gpr_res_I', data_inj, sampler_method=sampler_method)
|
|
125
|
-
|
|
126
|
-
def get_ps_gen(self, **kargs):
|
|
127
|
-
return self.ps_builder.get(self.get_stokes_i_like(), **kargs)
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
from pspipe import settings
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
def test_validate(tmp_path):
|
|
5
|
-
s = settings.Settings.get_defaults()
|
|
6
|
-
|
|
7
|
-
f = (tmp_path / 'f')
|
|
8
|
-
f.touch()
|
|
9
|
-
|
|
10
|
-
s.vis_to_sph.pre_flag = str(f)
|
|
11
|
-
s.combine.pre_flag = str(f)
|
|
12
|
-
s.power_spectra.eor_bin_list = str(f)
|
|
13
|
-
s.power_spectra.ps_config = str(f)
|
|
14
|
-
s.power_spectra.flagger = str(f)
|
|
15
|
-
s.gpr.config_i = str(f)
|
|
16
|
-
s.gpr.config_v = str(f)
|
|
17
|
-
|
|
18
|
-
for m in s.keys():
|
|
19
|
-
if hasattr(s, f'validate_{m}'):
|
|
20
|
-
assert s.validate(m), m
|
|
21
|
-
|
|
22
|
-
s.gpr.config_v = 'kks'
|
|
23
|
-
assert not s.validate('gpr')
|
|
24
|
-
|
|
25
|
-
s.vis_cube.win_fct = 'hann'
|
|
26
|
-
assert s.validate('vis_cube')
|
|
27
|
-
|
|
28
|
-
s.vis_cube.win_fct = 'aksks'
|
|
29
|
-
assert not s.validate('vis_cube')
|
|
30
|
-
|
|
31
|
-
s.image.lst_bins = [0, 4, 1]
|
|
32
|
-
assert s.validate('image')
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
from pspipe import utils
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
def test_alphanum():
|
|
5
|
-
assert utils.alphanum('|sd dd 12.s ') == 'sddd12s'
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def test_all_same():
|
|
9
|
-
assert utils.all_same([1, 1, 1])
|
|
10
|
-
assert not utils.all_same([1, 1, 2])
|
|
11
|
-
assert not utils.all_same([1, 2, 1])
|
|
12
|
-
assert utils.all_same(['a', 'a', 'a'])
|
|
13
|
-
assert not utils.all_same(['a', 'a', 'a', 1])
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def test_all_in_other():
|
|
17
|
-
assert utils.all_in_other([1, 2], list(range(10)))
|
|
18
|
-
assert not utils.all_in_other([1, 2, 10], list(range(10)))
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def test_is_in_lst_bin():
|
|
22
|
-
assert utils.is_in_lst_bin(2, 1, 3)
|
|
23
|
-
assert utils.is_in_lst_bin(2, 2, 3)
|
|
24
|
-
assert not utils.is_in_lst_bin(2, 3, 3)
|
|
25
|
-
assert not utils.is_in_lst_bin(3, 2, 3)
|
|
26
|
-
assert not utils.is_in_lst_bin(3, 21, 3)
|
|
27
|
-
assert utils.is_in_lst_bin(2, 21, 3)
|
|
28
|
-
assert utils.is_in_lst_bin(21, 21, 3)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|