xradio 0.0.41__py3-none-any.whl → 0.0.42__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. xradio/_utils/coord_math.py +100 -0
  2. xradio/_utils/list_and_array.py +49 -4
  3. xradio/_utils/schema.py +36 -16
  4. xradio/image/_util/_casacore/xds_from_casacore.py +5 -5
  5. xradio/image/_util/_casacore/xds_to_casacore.py +12 -11
  6. xradio/image/_util/_fits/xds_from_fits.py +18 -17
  7. xradio/image/_util/_zarr/zarr_low_level.py +29 -12
  8. xradio/image/_util/common.py +1 -1
  9. xradio/image/_util/image_factory.py +1 -1
  10. xradio/{correlated_data → measurement_set}/__init__.py +7 -4
  11. xradio/measurement_set/_utils/__init__.py +5 -0
  12. xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/_tables/load_main_table.py +1 -1
  13. xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/_tables/read.py +1 -1
  14. xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/conversion.py +78 -35
  15. xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/create_antenna_xds.py +62 -37
  16. xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/create_field_and_source_xds.py +109 -22
  17. xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/msv4_sub_xdss.py +47 -13
  18. xradio/{correlated_data → measurement_set}/_utils/_utils/xds_helper.py +1 -1
  19. xradio/{correlated_data/_utils/ms.py → measurement_set/_utils/msv2.py} +4 -4
  20. xradio/{correlated_data → measurement_set}/convert_msv2_to_processing_set.py +2 -2
  21. xradio/{correlated_data → measurement_set}/load_processing_set.py +5 -5
  22. xradio/measurement_set/measurement_set_xds.py +83 -0
  23. xradio/{correlated_data → measurement_set}/open_processing_set.py +9 -16
  24. xradio/measurement_set/processing_set.py +777 -0
  25. xradio/{correlated_data → measurement_set}/schema.py +1101 -610
  26. xradio/schema/check.py +42 -22
  27. xradio/schema/dataclass.py +56 -6
  28. xradio/sphinx/__init__.py +12 -0
  29. xradio/sphinx/schema_table.py +351 -0
  30. {xradio-0.0.41.dist-info → xradio-0.0.42.dist-info}/METADATA +9 -6
  31. xradio-0.0.42.dist-info/RECORD +76 -0
  32. {xradio-0.0.41.dist-info → xradio-0.0.42.dist-info}/WHEEL +1 -1
  33. xradio/_utils/common.py +0 -101
  34. xradio/correlated_data/_utils/__init__.py +0 -5
  35. xradio/correlated_data/correlated_xds.py +0 -13
  36. xradio/correlated_data/processing_set.py +0 -301
  37. xradio/correlated_data/test__processing_set.py +0 -74
  38. xradio-0.0.41.dist-info/RECORD +0 -75
  39. /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/_tables/load.py +0 -0
  40. /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/_tables/read_main_table.py +0 -0
  41. /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/_tables/read_subtables.py +0 -0
  42. /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/_tables/table_query.py +0 -0
  43. /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/_tables/write.py +0 -0
  44. /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/_tables/write_exp_api.py +0 -0
  45. /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/chunks.py +0 -0
  46. /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/descr.py +0 -0
  47. /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/msv2_msv3.py +0 -0
  48. /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/msv2_to_msv4_meta.py +0 -0
  49. /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/msv4_info_dicts.py +0 -0
  50. /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/optimised_functions.py +0 -0
  51. /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/partition_queries.py +0 -0
  52. /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/partitions.py +0 -0
  53. /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/subtables.py +0 -0
  54. /xradio/{correlated_data → measurement_set}/_utils/_utils/cds.py +0 -0
  55. /xradio/{correlated_data → measurement_set}/_utils/_utils/partition_attrs.py +0 -0
  56. /xradio/{correlated_data → measurement_set}/_utils/_utils/stokes_types.py +0 -0
  57. /xradio/{correlated_data → measurement_set}/_utils/_zarr/encoding.py +0 -0
  58. /xradio/{correlated_data → measurement_set}/_utils/_zarr/read.py +0 -0
  59. /xradio/{correlated_data → measurement_set}/_utils/_zarr/write.py +0 -0
  60. /xradio/{correlated_data → measurement_set}/_utils/zarr.py +0 -0
  61. {xradio-0.0.41.dist-info → xradio-0.0.42.dist-info}/LICENSE.txt +0 -0
  62. {xradio-0.0.41.dist-info → xradio-0.0.42.dist-info}/top_level.txt +0 -0
@@ -6,6 +6,7 @@ from typing import Tuple, Union
6
6
  import numpy as np
7
7
  import xarray as xr
8
8
 
9
+ from xradio._utils.coord_math import convert_to_si_units
9
10
  from xradio._utils.schema import (
10
11
  column_description_casacore_to_msv4_measure,
11
12
  convert_generic_xds_to_xradio_schema,
@@ -144,7 +145,7 @@ def create_weather_xds(in_file: str, ant_xds_station_name_ids: xr.DataArray):
144
145
  }
145
146
  weather_xds = weather_xds.assign_coords(coords)
146
147
 
147
- dims_station_time = ["station_name", "time"]
148
+ dims_station_time = ["station_name", "time_weather"]
148
149
  to_new_data_variables = {
149
150
  "H20": ["H2O", dims_station_time],
150
151
  "IONOS_ELECTRON": ["IONOS_ELECTRON", dims_station_time],
@@ -157,13 +158,23 @@ def create_weather_xds(in_file: str, ant_xds_station_name_ids: xr.DataArray):
157
158
  }
158
159
 
159
160
  to_new_coords = {
160
- "TIME": ["time", ["time"]],
161
+ "TIME": ["time_weather", ["time_weather"]],
161
162
  }
162
163
 
163
164
  weather_xds = convert_generic_xds_to_xradio_schema(
164
165
  generic_weather_xds, weather_xds, to_new_data_variables, to_new_coords
165
166
  )
166
167
 
168
+ # TODO: option to interpolate to main time
169
+
170
+ # PRESSURE: hPa in MSv2 specs and some MSs => Pa
171
+ weather_xds = convert_to_si_units(weather_xds)
172
+
173
+ # correct expected types (for example "IONOS_ELECTRON", "PRESSURE" can be float32)
174
+ for data_var in weather_xds:
175
+ if weather_xds.data_vars[data_var].dtype != np.float64:
176
+ weather_xds[data_var] = weather_xds[data_var].astype(np.float64)
177
+
167
178
  return weather_xds
168
179
 
169
180
 
@@ -199,9 +210,8 @@ def correct_generic_pointing_xds(
199
210
 
200
211
  correct_pointing_xds = generic_pointing_xds.copy()
201
212
 
202
- for key in generic_pointing_xds:
203
- if key in to_new_data_variables:
204
- data_var_name = to_new_data_variables[key]
213
+ for data_var_name in generic_pointing_xds:
214
+ if data_var_name in to_new_data_variables:
205
215
  # Corrects dim sizes of "empty cell" variables, such as empty DIRECTION, TARGET, etc.
206
216
  if (
207
217
  "dim_2" in generic_pointing_xds.sizes
@@ -296,6 +306,8 @@ def create_pointing_xds(
296
306
  size = generic_pointing_xds.sizes["n_polynomial"]
297
307
  if size == 1:
298
308
  generic_pointing_xds = generic_pointing_xds.sel({"n_polynomial": 0})
309
+ elif size == 0:
310
+ generic_pointing_xds = generic_pointing_xds.drop_dims("n_polynomial")
299
311
 
300
312
  time_ant_dims = ["time", "antenna_name"]
301
313
  time_ant_dir_dims = time_ant_dims + ["local_sky_dir_label"]
@@ -341,8 +353,8 @@ def prepare_generic_sys_cal_xds(generic_sys_cal_xds: xr.Dataset) -> xr.Dataset:
341
353
  This function performs various prepareation steps, such as:
342
354
  - filter out dimensions not neeed for an individual MSv4 (SPW, FEED),
343
355
  - drop variables loaded from columns with all items set to empty array,
344
- - transpose the dimensions frequency,receptor,
345
- - fix dimension names when needed.
356
+ - transpose the dimensions frequency,receptor
357
+ - fix dimension names (and order) when needed.
346
358
 
347
359
  Parameters
348
360
  ----------
@@ -374,15 +386,38 @@ def prepare_generic_sys_cal_xds(generic_sys_cal_xds: xr.Dataset) -> xr.Dataset:
374
386
  "receptor" in generic_sys_cal_xds.sizes
375
387
  and "frequency" in generic_sys_cal_xds.sizes
376
388
  ):
389
+ # dim_3 can be created for example when the T*_SPECTRUM have varying # channels!
390
+ # more generaly, could transpose with ... to avoid errors with additional spurious dimensions
391
+ if "dim_3" in generic_sys_cal_xds.dims:
392
+ generic_sys_cal_xds = generic_sys_cal_xds.drop_dims("dim_3")
377
393
  # From MSv2 tables we get (...,frequency, receptor)
378
394
  # -> transpose to (...,receptor,frequency) ready for MSv4 sys_cal_xds
379
395
  generic_sys_cal_xds = generic_sys_cal_xds.transpose(
380
396
  "ANTENNA_ID", "TIME", "receptor", "frequency"
381
397
  )
382
- else:
398
+ elif (
399
+ "frequency" in generic_sys_cal_xds.sizes
400
+ and not "dim_3" in generic_sys_cal_xds.sizes
401
+ ):
383
402
  # because order is (...,frequency,receptor), when frequency is missing
384
403
  # receptor can get wrongly labeled as frequency
385
404
  generic_sys_cal_xds = generic_sys_cal_xds.rename_dims({"frequency": "receptor"})
405
+ elif (
406
+ "frequency" not in generic_sys_cal_xds.sizes
407
+ and "receptor" in generic_sys_cal_xds.sizes
408
+ and "dim_3" in generic_sys_cal_xds.sizes
409
+ ):
410
+ # different *_SPECTRUM array sizes + some empty arrays can create an additional spurious
411
+ # generic dimension, which should have been "receptor"
412
+ generic_sys_cal_xds = generic_sys_cal_xds.rename_dims({"receptor": "frequency"})
413
+ generic_sys_cal_xds = generic_sys_cal_xds.rename_dims({"dim_3": "receptor"})
414
+ generic_sys_cal_xds = generic_sys_cal_xds.transpose(
415
+ "ANTENNA_ID", "TIME", "receptor", "frequency"
416
+ )
417
+ else:
418
+ raise RuntimeError(
419
+ "Cannot understand the arrangement of dimensions of {generic_sys_cal_xds=}"
420
+ )
386
421
 
387
422
  return generic_sys_cal_xds
388
423
 
@@ -462,7 +497,7 @@ def create_system_calibration_xds(
462
497
  "frequency": ["frequency_cal", ["frequency_cal"]],
463
498
  }
464
499
 
465
- sys_cal_xds = xr.Dataset(attrs={"type": "sys_cal"})
500
+ sys_cal_xds = xr.Dataset(attrs={"type": "system_calibration"})
466
501
  coords = {
467
502
  "antenna_name": ant_xds_name_ids.sel(
468
503
  antenna_id=generic_sys_cal_xds["ANTENNA_ID"]
@@ -483,8 +518,7 @@ def create_system_calibration_xds(
483
518
  frequency_measure = {
484
519
  "type": main_xds_frequency.attrs["type"],
485
520
  "units": main_xds_frequency.attrs["units"],
486
- "frame": main_xds_frequency.attrs["frame"],
487
- "reference_value": main_xds_frequency.attrs["reference_frequency"],
521
+ "observer": main_xds_frequency.attrs["observer"],
488
522
  }
489
523
  sys_cal_xds.coords["frequency_cal"].attrs.update(frequency_measure)
490
524
 
@@ -499,8 +533,8 @@ def create_system_calibration_xds(
499
533
  time_coord_attrs = {
500
534
  "type": "time",
501
535
  "units": ["s"],
502
- "scale": "UTC",
503
- "format": "UNIX",
536
+ "scale": "utc",
537
+ "format": "unix",
504
538
  }
505
539
  # If interpolating time, rename time_cal => time
506
540
  time_coord = {"time": ("time_cal", sys_cal_interp_time.data)}
@@ -7,7 +7,7 @@ import xarray as xr
7
7
 
8
8
  from .cds import CASAVisSet
9
9
  from .stokes_types import stokes_types
10
- from ...._utils.common import get_pad_value
10
+ from xradio._utils.list_and_array import get_pad_value
11
11
 
12
12
 
13
13
  def make_coords(
@@ -2,14 +2,14 @@ import os
2
2
  import toolviper.utils.logger as logger
3
3
  from typing import List, Tuple, Union
4
4
 
5
- from ._utils.cds import CASAVisSet
6
- from ._ms.partitions import (
5
+ from xradio.measurement_set._utils._utils.cds import CASAVisSet
6
+ from xradio.measurement_set._utils._msv2.partitions import (
7
7
  finalize_partitions,
8
8
  read_ms_ddi_partitions,
9
9
  read_ms_scan_subscan_partitions,
10
10
  )
11
- from ._ms.subtables import read_ms_subtables
12
- from ._utils.xds_helper import vis_xds_packager_cds
11
+ from xradio.measurement_set._utils._msv2.subtables import read_ms_subtables
12
+ from xradio.measurement_set._utils._utils.xds_helper import vis_xds_packager_cds
13
13
 
14
14
 
15
15
  def read_ms(
@@ -4,8 +4,8 @@ from typing import Dict, Union
4
4
 
5
5
  import dask
6
6
 
7
- from xradio.correlated_data._utils._ms.partition_queries import create_partitions
8
- from xradio.correlated_data._utils._ms.conversion import convert_and_write_partition
7
+ from xradio.measurement_set._utils._msv2.partition_queries import create_partitions
8
+ from xradio.measurement_set._utils._msv2.conversion import convert_and_write_partition
9
9
 
10
10
 
11
11
  def convert_msv2_to_processing_set(
@@ -1,5 +1,5 @@
1
1
  import os
2
- from xradio.correlated_data import ProcessingSet
2
+ from xradio.measurement_set import ProcessingSet
3
3
  from typing import Dict, Union
4
4
 
5
5
 
@@ -43,10 +43,10 @@ def load_processing_set(
43
43
  ps = ProcessingSet()
44
44
  for ms_name, ms_xds_isel in sel_parms.items():
45
45
  ms_store = os.path.join(ps_store, ms_name)
46
- ms_main_store = os.path.join(ms_store, "MAIN")
46
+ correlated_store = os.path.join(ms_store, "correlated_xds")
47
47
 
48
48
  xds = _open_dataset(
49
- ms_main_store,
49
+ correlated_store,
50
50
  file_system,
51
51
  ms_xds_isel,
52
52
  data_variables,
@@ -55,7 +55,7 @@ def load_processing_set(
55
55
  data_groups = xds.attrs["data_groups"]
56
56
 
57
57
  if load_sub_datasets:
58
- from xradio.correlated_data.open_processing_set import _open_sub_xds
58
+ from xradio.measurement_set.open_processing_set import _open_sub_xds
59
59
 
60
60
  sub_xds_dict, field_and_source_xds_dict = _open_sub_xds(
61
61
  ms_store, file_system=file_system, load=True, data_groups=data_groups
@@ -76,7 +76,7 @@ def load_processing_set(
76
76
  return ps
77
77
 
78
78
 
79
- class processing_set_iterator:
79
+ class ProcessingSetIterator:
80
80
  def __init__(
81
81
  self,
82
82
  sel_parms: dict,
@@ -0,0 +1,83 @@
1
+ import pandas as pd
2
+ from xradio._utils.list_and_array import to_list
3
+ import xarray as xr
4
+ import numbers
5
+ import os
6
+
7
+
8
+ class MeasurementSetXds(xr.Dataset):
9
+ __slots__ = ()
10
+
11
+ def __init__(self, xds):
12
+ super().__init__(xds.data_vars, xds.coords, xds.attrs)
13
+
14
+ def to_store(self, store, **kwargs):
15
+ """
16
+ Write the MeasurementSetXds to a Zarr store.
17
+ Does not write to cloud storage yet.
18
+
19
+ Args:
20
+ store (str): The path to the Zarr store.
21
+ **kwargs: Additional keyword arguments to be passed to `xarray.Dataset.to_zarr`. See https://docs.xarray.dev/en/latest/generated/xarray.Dataset.to_zarr.html for more information.
22
+
23
+ Returns:
24
+ None
25
+ """
26
+
27
+ copy_cor_xds = self.copy() # No deep copy
28
+
29
+ # Remove field_and_source_xds from all correlated_data (VISIBILITY/SPECTRUM) data variables
30
+ # and save them as separate zarr files.
31
+ for data_group_name, data_group in self.attrs["data_groups"].items():
32
+ del copy_cor_xds[data_group["correlated_data"]].attrs[
33
+ "field_and_source_xds"
34
+ ]
35
+
36
+ # print("data_group_name", data_group_name)
37
+ xr.Dataset.to_zarr(
38
+ self[data_group["correlated_data"]].attrs["field_and_source_xds"],
39
+ os.path.join(store, "field_and_source_xds_" + data_group_name),
40
+ **kwargs,
41
+ )
42
+
43
+ # Remove xds attributes from copy_cor_xds and save xds attributes as separate zarr files.
44
+ for attrs_name in self.attrs:
45
+ if "xds" in attrs_name:
46
+ del copy_cor_xds.attrs[attrs_name]
47
+ xr.Dataset.to_zarr(
48
+ self.attrs[attrs_name], os.path.join(store, attrs_name, **kwargs)
49
+ )
50
+
51
+ # Save copy_cor_xds as zarr file.
52
+ xr.Dataset.to_zarr(
53
+ copy_cor_xds, os.path.join(store, "correlated_xds"), **kwargs
54
+ )
55
+
56
+ def sel(self, data_group_name=None, **kwargs):
57
+ """
58
+ Select data along dimension(s) by label.
59
+
60
+ Args:
61
+ **kwargs: Keyword arguments to be passed to `xarray.Dataset.sel`. See https://xarray.pydata.org/en/stable/generated/xarray.Dataset.sel.html for more information.
62
+
63
+ Returns:
64
+ MeasurementSetXds
65
+ """
66
+
67
+ if data_group_name is not None:
68
+ sel_data_group_set = set(
69
+ self.attrs["data_groups"][data_group_name].values()
70
+ )
71
+
72
+ data_variables_to_drop = []
73
+ for dg in self.attrs["data_groups"].values():
74
+ temp_set = set(dg.values()) - sel_data_group_set
75
+ data_variables_to_drop.extend(list(temp_set))
76
+
77
+ data_variables_to_drop = list(set(data_variables_to_drop))
78
+
79
+ return MeasurementSetXds(
80
+ super().sel(**kwargs).drop_vars(data_variables_to_drop)
81
+ )
82
+ else:
83
+ return MeasurementSetXds(super().sel(**kwargs))
@@ -1,6 +1,6 @@
1
1
  import os
2
2
 
3
- from xradio.correlated_data import ProcessingSet
3
+ from xradio.measurement_set import ProcessingSet
4
4
  import toolviper.utils.logger as logger
5
5
  from xradio._utils.zarr.common import _open_dataset, _get_file_system_and_items
6
6
  import s3fs
@@ -25,6 +25,8 @@ def open_processing_set(
25
25
  processing_set
26
26
  Lazy representation of processing set (data is represented by Dask.arrays).
27
27
  """
28
+ from xradio.measurement_set import MeasurementSetXds
29
+
28
30
  file_system, ms_store_list = _get_file_system_and_items(ps_store)
29
31
 
30
32
  ps = ProcessingSet()
@@ -32,9 +34,9 @@ def open_processing_set(
32
34
  for ms_name in ms_store_list:
33
35
  # try:
34
36
  ms_store = os.path.join(ps_store, ms_name)
35
- ms_main_store = os.path.join(ms_store, "MAIN")
37
+ correlated_store = os.path.join(ms_store, "correlated_xds")
36
38
 
37
- xds = _open_dataset(ms_main_store, file_system)
39
+ xds = _open_dataset(correlated_store, file_system)
38
40
  data_groups = xds.attrs["data_groups"]
39
41
 
40
42
  if (intents is None) or (
@@ -54,7 +56,7 @@ def open_processing_set(
54
56
  "field_and_source_xds"
55
57
  ] = field_and_source_xds_dict[data_group_name]
56
58
 
57
- ps[ms_name] = xds
59
+ ps[ms_name] = MeasurementSetXds(xds)
58
60
  # except Exception as e:
59
61
  # logger.warning(f"Could not open {ms_name} due to {e}")
60
62
  # continue
@@ -66,15 +68,6 @@ def _open_sub_xds(ms_store, file_system, data_groups, load=False):
66
68
  sub_xds_dict = {}
67
69
  field_and_source_xds_dict = {}
68
70
 
69
- xds_names = {
70
- "ANTENNA": "antenna_xds",
71
- "POINTING": "pointing_xds",
72
- "SYSCAL": "system_calibration_xds",
73
- "GAIN_CURVE": "gain_curve_xds",
74
- "PHASE_CAL": "phase_calibration_xds",
75
- "WEATHER": "weather_xds",
76
- }
77
-
78
71
  if isinstance(file_system, s3fs.core.S3FileSystem):
79
72
  file_names = [
80
73
  bd.split(sep="/")[-1] for bd in file_system.listdir(ms_store, detail=False)
@@ -83,9 +76,9 @@ def _open_sub_xds(ms_store, file_system, data_groups, load=False):
83
76
  file_names = file_system.listdir(ms_store)
84
77
  file_names = [item for item in file_names if not item.startswith(".")]
85
78
 
86
- file_names.remove("MAIN")
79
+ file_names.remove("correlated_xds")
87
80
 
88
- field_dict = {"FIELD_AND_SOURCE_" + key.upper(): key for key in data_groups.keys()}
81
+ field_dict = {"field_and_source_xds_" + key: key for key in data_groups.keys()}
89
82
 
90
83
  # field_and_source_xds_name_start = "FIELD"
91
84
  for n in file_names:
@@ -98,7 +91,7 @@ def _open_sub_xds(ms_store, file_system, data_groups, load=False):
98
91
  if n in field_dict.keys():
99
92
  field_and_source_xds_dict[field_dict[n]] = xds
100
93
  else:
101
- sub_xds_dict[xds_names[n]] = xds
94
+ sub_xds_dict[n] = xds
102
95
 
103
96
  return sub_xds_dict, field_and_source_xds_dict
104
97