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.
Files changed (36) hide show
  1. {pspipe-0.7.2 → pspipe-0.8}/PKG-INFO +5 -3
  2. {pspipe-0.7.2 → pspipe-0.8}/pspipe/database.py +48 -4
  3. pspipe-0.8/pspipe/obsdata.py +475 -0
  4. {pspipe-0.7.2 → pspipe-0.8}/pspipe/tasks.py +64 -29
  5. {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/default_settings.toml +1 -0
  6. {pspipe-0.7.2 → pspipe-0.8}/pspipe/tools/pspipe.py +27 -3
  7. {pspipe-0.7.2 → pspipe-0.8}/pyproject.toml +6 -2
  8. pspipe-0.7.2/pspipe/obsdata.py +0 -127
  9. pspipe-0.7.2/pspipe/tests/test_settings.py +0 -32
  10. pspipe-0.7.2/pspipe/tests/test_utils.py +0 -28
  11. pspipe-0.7.2/pspipe/tools/__init__.py +0 -0
  12. {pspipe-0.7.2 → pspipe-0.8}/LICENSE +0 -0
  13. {pspipe-0.7.2 → pspipe-0.8}/README.md +0 -0
  14. {pspipe-0.7.2 → pspipe-0.8}/pspipe/__init__.py +0 -0
  15. {pspipe-0.7.2 → pspipe-0.8}/pspipe/settings.py +0 -0
  16. {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/eor_bins_a12.parset +0 -0
  17. {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/eor_bins_alo.parset +0 -0
  18. {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/eor_bins_hba.parset +0 -0
  19. {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/eor_bins_nenufar.parset +0 -0
  20. {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/flagger.parset +0 -0
  21. {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/flagger_pre_combine.parset +0 -0
  22. {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/gpr_config_a12.parset +0 -0
  23. {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/gpr_config_hba.parset +0 -0
  24. {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/gpr_config_nenufar.parset +0 -0
  25. {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/gpr_config_v.parset +0 -0
  26. {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/ps_config_a12.parset +0 -0
  27. {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/ps_config_alo.parset +0 -0
  28. {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/ps_config_hba.parset +0 -0
  29. {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/config/ps_config_nenufar.parset +0 -0
  30. {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/default_a12.toml +0 -0
  31. {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/default_alo.toml +0 -0
  32. {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/default_hba.toml +0 -0
  33. {pspipe-0.7.2 → pspipe-0.8}/pspipe/templates/default_nenufar.toml +0 -0
  34. {pspipe-0.7.2/pspipe/tests → pspipe-0.8/pspipe/tools}/__init__.py +0 -0
  35. {pspipe-0.7.2 → pspipe-0.8}/pspipe/tools/psdb.py +0 -0
  36. {pspipe-0.7.2 → pspipe-0.8}/pspipe/utils.py +0 -0
@@ -1,7 +1,8 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: pspipe
3
- Version: 0.7.2
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.1.5)
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
- return ImageRevisionMeta(file, {'obs_id': obs_id, 'img_rev': img_rev})
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
- return os.path.join(self.get_dir(), s)
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
- if not self.settings.image.lst_bins:
304
- self.img_rev.new_obs_id(obs_id)
305
- else:
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
- target_host = get_target_host(ms_files_channel[0], self.settings.worker)
325
+ file_host = get_target_host(ms_files_channel[0], self.settings.worker)
311
326
  if self.settings.image.split_even_odd:
312
- self.add_sub_task(ImagingSubTask(self.settings, obs_id, ms_files_channel, even=True, index=i),
313
- run_on_host=target_host)
314
- self.add_sub_task(ImagingSubTask(self.settings, obs_id, ms_files_channel, odd=True, index=i),
315
- run_on_host=target_host)
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
- self.add_sub_task(ImagingSubTask(self.settings, obs_id, ms_files_channel, index=i),
318
- run_on_host=target_host)
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
@@ -1,4 +1,5 @@
1
1
  data_dir = '.'
2
+ distributed_data_dirs = []
2
3
  default_settings = ''
3
4
  debug = false
4
5
  longitude = 6.57
@@ -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 obs_ids:
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
- obs_ids_set = all_tasks[task_name](s).run(obs_ids_set)
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('\nError: no obs_id to process.')
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.2"
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.1.5",
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"
@@ -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