xradio 0.0.49__tar.gz → 0.0.51__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 (84) hide show
  1. {xradio-0.0.49/src/xradio.egg-info → xradio-0.0.51}/PKG-INFO +11 -4
  2. xradio-0.0.51/README.md +27 -0
  3. {xradio-0.0.49 → xradio-0.0.51}/pyproject.toml +7 -3
  4. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/_utils/schema.py +2 -2
  5. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_msv2/conversion.py +5 -4
  6. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_msv2/create_antenna_xds.py +5 -5
  7. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_msv2/create_field_and_source_xds.py +2 -1
  8. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_msv2/msv4_sub_xdss.py +151 -13
  9. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/measurement_set_xdt.py +126 -4
  10. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/processing_set_xdt.py +151 -36
  11. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/schema.py +42 -25
  12. {xradio-0.0.49 → xradio-0.0.51/src/xradio.egg-info}/PKG-INFO +11 -4
  13. {xradio-0.0.49 → xradio-0.0.51}/src/xradio.egg-info/requires.txt +2 -1
  14. xradio-0.0.49/README.md +0 -21
  15. {xradio-0.0.49 → xradio-0.0.51}/LICENSE.txt +0 -0
  16. {xradio-0.0.49 → xradio-0.0.51}/MANIFEST.in +0 -0
  17. {xradio-0.0.49 → xradio-0.0.51}/setup.cfg +0 -0
  18. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/__init__.py +0 -0
  19. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/_utils/__init__.py +0 -0
  20. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/_utils/_casacore/tables.py +0 -0
  21. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/_utils/coord_math.py +0 -0
  22. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/_utils/dict_helpers.py +0 -0
  23. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/_utils/list_and_array.py +0 -0
  24. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/_utils/zarr/__init__.py +0 -0
  25. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/_utils/zarr/common.py +0 -0
  26. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/image/__init__.py +0 -0
  27. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/image/_util/__init__.py +0 -0
  28. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/image/_util/_casacore/__init__.py +0 -0
  29. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/image/_util/_casacore/common.py +0 -0
  30. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/image/_util/_casacore/xds_from_casacore.py +0 -0
  31. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/image/_util/_casacore/xds_to_casacore.py +0 -0
  32. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/image/_util/_fits/xds_from_fits.py +0 -0
  33. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/image/_util/_zarr/common.py +0 -0
  34. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/image/_util/_zarr/xds_from_zarr.py +0 -0
  35. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/image/_util/_zarr/xds_to_zarr.py +0 -0
  36. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/image/_util/_zarr/zarr_low_level.py +0 -0
  37. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/image/_util/casacore.py +0 -0
  38. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/image/_util/common.py +0 -0
  39. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/image/_util/fits.py +0 -0
  40. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/image/_util/image_factory.py +0 -0
  41. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/image/_util/zarr.py +0 -0
  42. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/image/image.py +0 -0
  43. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/__init__.py +0 -0
  44. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/__init__.py +0 -0
  45. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_msv2/_tables/load.py +0 -0
  46. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_msv2/_tables/load_main_table.py +0 -0
  47. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_msv2/_tables/read.py +0 -0
  48. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_msv2/_tables/read_main_table.py +0 -0
  49. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_msv2/_tables/read_subtables.py +0 -0
  50. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_msv2/_tables/table_query.py +0 -0
  51. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_msv2/_tables/write.py +0 -0
  52. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_msv2/_tables/write_exp_api.py +0 -0
  53. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_msv2/chunks.py +0 -0
  54. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_msv2/descr.py +0 -0
  55. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_msv2/msv2_msv3.py +0 -0
  56. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_msv2/msv2_to_msv4_meta.py +0 -0
  57. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_msv2/msv4_info_dicts.py +0 -0
  58. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_msv2/optimised_functions.py +0 -0
  59. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_msv2/partition_queries.py +0 -0
  60. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_msv2/partitions.py +0 -0
  61. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_msv2/subtables.py +0 -0
  62. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_utils/cds.py +0 -0
  63. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_utils/partition_attrs.py +0 -0
  64. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_utils/stokes_types.py +0 -0
  65. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_utils/xds_helper.py +0 -0
  66. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_zarr/encoding.py +0 -0
  67. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_zarr/read.py +0 -0
  68. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/_zarr/write.py +0 -0
  69. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/msv2.py +0 -0
  70. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/_utils/zarr.py +0 -0
  71. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/convert_msv2_to_processing_set.py +0 -0
  72. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/load_processing_set.py +0 -0
  73. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/measurement_set/open_processing_set.py +0 -0
  74. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/schema/__init__.py +0 -0
  75. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/schema/bases.py +0 -0
  76. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/schema/check.py +0 -0
  77. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/schema/dataclass.py +0 -0
  78. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/schema/metamodel.py +0 -0
  79. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/schema/typing.py +0 -0
  80. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/sphinx/__init__.py +0 -0
  81. {xradio-0.0.49 → xradio-0.0.51}/src/xradio/sphinx/schema_table.py +0 -0
  82. {xradio-0.0.49 → xradio-0.0.51}/src/xradio.egg-info/SOURCES.txt +0 -0
  83. {xradio-0.0.49 → xradio-0.0.51}/src/xradio.egg-info/dependency_links.txt +0 -0
  84. {xradio-0.0.49 → xradio-0.0.51}/src/xradio.egg-info/top_level.txt +0 -0
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xradio
3
- Version: 0.0.49
3
+ Version: 0.0.51
4
4
  Summary: Xarray Radio Astronomy Data IO
5
- Author-email: Jan-Willem Steeb <jsteeb@nrao.edu>
5
+ Author-email: Jan-Willem Steeb <jsteeb@nrao.edu>, Federico Montesino Pouzols <pouzols@eso.edu>, Dave Mehringer <dmehring@nrao.edu>, Peter Wortmann <peter.wortmann@skao.int>
6
6
  License: BSD 3-Clause License
7
7
 
8
8
  All works in this repository are copyrighted 2024.
@@ -43,7 +43,7 @@ License-File: LICENSE.txt
43
43
  Requires-Dist: astropy
44
44
  Requires-Dist: dask
45
45
  Requires-Dist: distributed
46
- Requires-Dist: toolviper
46
+ Requires-Dist: toolviper>=0.0.11
47
47
  Requires-Dist: numba>=0.57.0
48
48
  Requires-Dist: numpy
49
49
  Requires-Dist: pytest
@@ -56,6 +56,7 @@ Requires-Dist: zarr<3,>=2
56
56
  Requires-Dist: pyarrow
57
57
  Requires-Dist: python_casacore>=3.6.1; sys_platform != "darwin"
58
58
  Requires-Dist: typeguard
59
+ Requires-Dist: numcodecs<0.16
59
60
  Provides-Extra: interactive
60
61
  Requires-Dist: matplotlib; extra == "interactive"
61
62
  Requires-Dist: prettytable; extra == "interactive"
@@ -78,7 +79,13 @@ Dynamic: license-file
78
79
  # xradio
79
80
  Xarray Radio Astronomy Data IO is still in development.
80
81
 
81
- [![Python 3.9 3.10 3.11 3.12](https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%203.12-blue)](https://www.python.org/downloads/release/python-380/)
82
+ [![Python 3.11 3.12 3.13](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13-blue)](https://www.python.org/downloads/release/python-3130/)
83
+ ![Linux Tests](https://github.com/casangi/xradio/actions/workflows/python-testing-linux.yml/badge.svg?branch=main)
84
+ ![macOS Tests](https://github.com/casangi/xradio/actions/workflows/python-testing-macos.yml/badge.svg?branch=main)
85
+ ![ipynb Tests](https://github.com/casangi/xradio/actions/workflows/run-ipynb.yml/badge.svg?branch=main)
86
+ [![Coverage](https://codecov.io/gh/casangi/xradio/branch/main/graph/badge.svg)](https://codecov.io/gh/casangi/xradio/branch/main/xradio)
87
+ [![Documentation Status](https://readthedocs.org/projects/xradio/badge/?version=latest)](https://xradio.readthedocs.io)
88
+ [![Version Status](https://img.shields.io/pypi/v/xradio.svg)](https://pypi.python.org/pypi/xradio/)
82
89
 
83
90
  # Installing
84
91
  It is recommended to use the conda environment manager from [miniforge](https://github.com/conda-forge/miniforge) to create a clean, self-contained runtime where XRADIO and all its dependencies can be installed:
@@ -0,0 +1,27 @@
1
+ # xradio
2
+ Xarray Radio Astronomy Data IO is still in development.
3
+
4
+ [![Python 3.11 3.12 3.13](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13-blue)](https://www.python.org/downloads/release/python-3130/)
5
+ ![Linux Tests](https://github.com/casangi/xradio/actions/workflows/python-testing-linux.yml/badge.svg?branch=main)
6
+ ![macOS Tests](https://github.com/casangi/xradio/actions/workflows/python-testing-macos.yml/badge.svg?branch=main)
7
+ ![ipynb Tests](https://github.com/casangi/xradio/actions/workflows/run-ipynb.yml/badge.svg?branch=main)
8
+ [![Coverage](https://codecov.io/gh/casangi/xradio/branch/main/graph/badge.svg)](https://codecov.io/gh/casangi/xradio/branch/main/xradio)
9
+ [![Documentation Status](https://readthedocs.org/projects/xradio/badge/?version=latest)](https://xradio.readthedocs.io)
10
+ [![Version Status](https://img.shields.io/pypi/v/xradio.svg)](https://pypi.python.org/pypi/xradio/)
11
+
12
+ # Installing
13
+ It is recommended to use the conda environment manager from [miniforge](https://github.com/conda-forge/miniforge) to create a clean, self-contained runtime where XRADIO and all its dependencies can be installed:
14
+ ```sh
15
+ conda create --name xradio python=3.12 --no-default-packages
16
+ conda activate xradio
17
+ ```
18
+ > 📝 On macOS it is required to pre-install `python-casacore` using `conda install -c conda-forge python-casacore`.
19
+
20
+ XRADIO can now be installed using:
21
+ ```sh
22
+ pip install xradio
23
+ ```
24
+ This will also install the minimal dependencies for XRADIO. To install the minimal dependencies and the interactive components (JupyterLab) use:
25
+ ```sh
26
+ pip install "xradio[interactive]"
27
+ ```
@@ -1,9 +1,12 @@
1
1
  [project]
2
2
  name = "xradio"
3
- version = "0.0.49"
3
+ version = "0.0.51"
4
4
  description = " Xarray Radio Astronomy Data IO"
5
5
  authors = [
6
6
  {name = "Jan-Willem Steeb", email="jsteeb@nrao.edu"},
7
+ {name = "Federico Montesino Pouzols", email="pouzols@eso.edu"},
8
+ {name = "Dave Mehringer", email="dmehring@nrao.edu"},
9
+ {name = "Peter Wortmann", email="peter.wortmann@skao.int"}
7
10
  ]
8
11
  license = {file = "LICENSE.txt"}
9
12
  readme = "README.md"
@@ -13,7 +16,7 @@ dependencies = [
13
16
  'astropy',
14
17
  'dask',
15
18
  'distributed',
16
- 'toolviper',
19
+ 'toolviper>=0.0.11',
17
20
  'numba>=0.57.0',
18
21
  'numpy',
19
22
  'pytest',
@@ -25,7 +28,8 @@ dependencies = [
25
28
  'zarr>=2,<3',
26
29
  'pyarrow',
27
30
  'python_casacore>=3.6.1; sys_platform != "darwin" ',
28
- 'typeguard'
31
+ 'typeguard',
32
+ 'numcodecs<0.16'
29
33
  ]
30
34
 
31
35
  [project.optional-dependencies]
@@ -190,14 +190,14 @@ casacore_to_msv4_measure_type = {
190
190
  "LSRK": "lsrk",
191
191
  "LSRD": "lsrd",
192
192
  "BARY": "BARY",
193
- "GEO": "GEO",
193
+ "GEO": "gcrs",
194
194
  "TOPO": "TOPO",
195
195
  }, # The frames/observer we are not sure if/how to translate to astropy are uppercase
196
196
  },
197
197
  "position": {
198
198
  "type": "location",
199
199
  "Ref": "frame",
200
- "Ref_map": {"ITRF": "GRS80"},
200
+ "Ref_map": {"ITRF": "ITRS"},
201
201
  },
202
202
  "uvw": {
203
203
  "type": "uvw",
@@ -1214,13 +1214,13 @@ def convert_and_write_partition(
1214
1214
  )
1215
1215
  # but before, keep the name-id arrays, we need them for the pointing and weather xds
1216
1216
  ant_xds_name_ids = ant_xds["antenna_name"].set_xindex("antenna_id")
1217
- ant_xds_station_name_ids = ant_xds["station"].set_xindex("antenna_id")
1217
+ ant_position_xds_with_ids = ant_xds["ANTENNA_POSITION"].set_xindex("antenna_id")
1218
1218
  # No longer needed after converting to name.
1219
1219
  ant_xds = ant_xds.drop_vars("antenna_id")
1220
1220
 
1221
1221
  # Create weather_xds
1222
1222
  start = time.time()
1223
- weather_xds = create_weather_xds(in_file, ant_xds_station_name_ids)
1223
+ weather_xds = create_weather_xds(in_file, ant_position_xds_with_ids)
1224
1224
  logger.debug("Time weather " + str(time.time() - start))
1225
1225
 
1226
1226
  # Create pointing_xds
@@ -1341,7 +1341,7 @@ def convert_and_write_partition(
1341
1341
 
1342
1342
  ms_xdt["/antenna_xds"] = ant_xds
1343
1343
  for group_name in xds.attrs["data_groups"]:
1344
- ms_xdt["/" + f"field_and_source_xds_{group_name}"] = field_and_source_xds
1344
+ ms_xdt["/" + f"field_and_source_{group_name}_xds"] = field_and_source_xds
1345
1345
 
1346
1346
  if with_pointing and len(pointing_xds.data_vars) > 0:
1347
1347
  ms_xdt["/pointing_xds"] = pointing_xds
@@ -1425,7 +1425,7 @@ def antenna_ids_to_names(
1425
1425
  "antenna_id",
1426
1426
  "antenna_name",
1427
1427
  "mount",
1428
- "station",
1428
+ "station_name",
1429
1429
  ]
1430
1430
  for unwanted_coord in unwanted_coords_from_ant_xds:
1431
1431
  xds = xds.drop_vars(unwanted_coord)
@@ -1468,6 +1468,7 @@ def add_group_to_data_groups(
1468
1468
  "correlated_data": correlated_data_name,
1469
1469
  "flag": "FLAG",
1470
1470
  "weight": "WEIGHT",
1471
+ "field_and_source": f"field_and_source_{what_group}_xds",
1471
1472
  "description": f"Data group derived from the data column '{correlated_data_name}' of an MSv2 converted to MSv4",
1472
1473
  "date": datetime.datetime.now(datetime.timezone.utc).isoformat(),
1473
1474
  }
@@ -109,7 +109,7 @@ def extract_antenna_info(
109
109
 
110
110
  to_new_coords = {
111
111
  "NAME": ["antenna_name", ["antenna_name"]],
112
- "STATION": ["station", ["antenna_name"]],
112
+ "STATION": ["station_name", ["antenna_name"]],
113
113
  "MOUNT": ["mount", ["antenna_name"]],
114
114
  # "PHASED_ARRAY_ID": ["phased_array_id", ["antenna_name"]],
115
115
  "antenna_id": ["antenna_id", ["antenna_name"]],
@@ -158,9 +158,9 @@ def extract_antenna_info(
158
158
 
159
159
  # None of the native numpy functions work on the github test runner.
160
160
  antenna_name = ant_xds["antenna_name"].values
161
- station = ant_xds["station"].values
161
+ station_name = ant_xds["station_name"].values
162
162
  antenna_name = np.array(
163
- list(map(lambda x, y: x + "_" + y, antenna_name, station))
163
+ list(map(lambda x, y: x + "_" + y, antenna_name, station_name))
164
164
  )
165
165
 
166
166
  ant_xds["antenna_name"] = xr.DataArray(antenna_name, dims=["antenna_name"])
@@ -376,7 +376,7 @@ def create_gain_curve_xds(
376
376
 
377
377
  ant_borrowed_coords = {
378
378
  "antenna_name": ant_xds.coords["antenna_name"],
379
- "station": ant_xds.coords["station"],
379
+ "station_name": ant_xds.coords["station_name"],
380
380
  "mount": ant_xds.coords["mount"],
381
381
  "telescope_name": ant_xds.coords["telescope_name"],
382
382
  "receptor_label": ant_xds.coords["receptor_label"],
@@ -486,7 +486,7 @@ def create_phase_calibration_xds(
486
486
 
487
487
  ant_borrowed_coords = {
488
488
  "antenna_name": ant_xds.coords["antenna_name"],
489
- "station": ant_xds.coords["station"],
489
+ "station_name": ant_xds.coords["station_name"],
490
490
  "mount": ant_xds.coords["mount"],
491
491
  "telescope_name": ant_xds.coords["telescope_name"],
492
492
  "receptor_label": ant_xds.coords["receptor_label"],
@@ -226,9 +226,10 @@ def extract_ephemeris_info(
226
226
  "type": "location",
227
227
  "units": ["deg", "deg", "m"],
228
228
  "data": observer_position,
229
- "frame": "WGS84",
229
+ "frame": "ITRS",
230
230
  "origin_object_name": "Earth",
231
231
  "coordinate_system": ephemeris_meta["obsloc"].lower(),
232
+ "ellipsoid": "WGS84",
232
233
  }
233
234
  ) # I think the units are ['deg','deg','m'] and 'WGS84'.
234
235
 
@@ -172,7 +172,130 @@ def make_taql_where_weather(
172
172
  return taql_where
173
173
 
174
174
 
175
- def create_weather_xds(in_file: str, ant_xds_station_name_ids: xr.DataArray):
175
+ def prepare_generic_weather_xds_and_station_name(
176
+ generic_weather_xds: xr.Dataset,
177
+ in_file: str,
178
+ ant_position_with_ids: xr.DataArray,
179
+ has_asdm_station_position: bool,
180
+ ) -> tuple[xr.Dataset, np.ndarray]:
181
+ """
182
+ A generic_weather_xds loaded with load_generic_table() might still need to be reloaded
183
+ with an additional WHERE condition to constrain the indices of antennas. But this depends on whether
184
+ ASDM/importasdm extension columns are present or not.
185
+
186
+ This also prepares the station_name values:
187
+ - if has_asdm_station_ids:
188
+ - tries to find from ASDM_STATION the station names,
189
+ - otherwise, takes ids (antenna_ids in generic_weather were actually the ASDM_STATION_IDs
190
+ - else: get the values from antenna_xds (the stations present)
191
+
192
+
193
+ Parameters
194
+ ----------
195
+ generic_weather_xds : xr.Dataset
196
+ generic dataset read from an MSv2 WEATHER subtable
197
+ in_file : str
198
+ Input MS name.
199
+ ant_position_with_ids : xr.DataArray
200
+ antenna_position data var from the antenna_xds (expected to still include the initial ANTENNA_ID
201
+ coordinate as well as other coordinates from the antenna_xds)
202
+ has_asdm_station_position : bool
203
+ wHether this generic weatehr_xds should be treated as including the nonstandard extensions
204
+ NS_WX_STATION_ID and NS_WX_STATION_POSITION as created by CASA/importasdm (ALMA and VLA).
205
+
206
+ Returns
207
+ -------
208
+ (generic_weather_xds, station_name): tuple[[xarray.Dataset, numpy.ndarray]
209
+ Weather Xarray Dataset prepared for generic conversion to MSv4, values for the station_name coordinate
210
+ """
211
+
212
+ if has_asdm_station_position:
213
+ asdm_station_path = os.path.join(in_file, "ASDM_STATION")
214
+ if table_exists(asdm_station_path):
215
+ asdm_station_xds = load_generic_table(in_file, "ASDM_STATION")
216
+ station_name = asdm_station_xds.name.values[
217
+ generic_weather_xds["ANTENNA_ID"].values
218
+ ]
219
+ else:
220
+ # if no info from ASDM_STATION, use the indices from antenna_id which was actually the NS_WX_STATION_ID
221
+ len_antenna_id = generic_weather_xds.sizes["ANTENNA_ID"]
222
+ station_name = list(
223
+ map(
224
+ lambda x, y: x + "_" + y,
225
+ ["Station"] * len_antenna_id,
226
+ generic_weather_xds["ANTENNA_ID"].values.astype(str),
227
+ )
228
+ )
229
+
230
+ else:
231
+ taql_where = make_taql_where_weather(in_file, ant_position_with_ids)
232
+ generic_weather_xds = load_generic_table(
233
+ in_file,
234
+ "WEATHER",
235
+ rename_ids=subt_rename_ids["WEATHER"],
236
+ taql_where=taql_where,
237
+ )
238
+
239
+ if not generic_weather_xds.data_vars:
240
+ # for example when the weather subtable only has info for antennas/stations
241
+ # not present in the MSv4 (no overlap between antennas loaded in ant_xds and weather)
242
+ return None, None
243
+
244
+ stations_present = ant_position_with_ids.sel(
245
+ antenna_id=generic_weather_xds["ANTENNA_ID"]
246
+ ).station_name
247
+ station_name = stations_present.values
248
+
249
+ return generic_weather_xds, station_name
250
+
251
+
252
+ def finalize_station_position(
253
+ weather_xds: xr.Dataset, ant_position_with_ids, has_asdm_station_position: bool
254
+ ) -> xr.Dataset:
255
+ """
256
+ For a STATION_POSITION data var being added to a weather_xds, make sure coordinates and dimensions
257
+ are conforming to the schema.
258
+
259
+ Parameters
260
+ ----------
261
+ weather_xds : xr.Dataset
262
+ weather_xds where we still need to ensure the right coordinates and attributes
263
+ ant_position_with_ids : xr.DataArray
264
+ antenna_position data var from the antenna_xds (expected to still include the initial ANTENNA_ID
265
+ coordinate as well as other coordinates from the antenna_xds)
266
+ has_asdm_station_position : bool
267
+ Whether this generic weatehr_xds should be treated as including the nonstandard extensions
268
+ NS_WX_STATION_ID and NS_WX_STATION_POSITION as created by CASA/importasdm (ALMA and VLA).
269
+
270
+ Returns
271
+ -------
272
+ weather_xds: xarray.Dataset
273
+ Weather Xarray Dataset with all coordinates and attributes in STATION_POSITION
274
+ """
275
+ if has_asdm_station_position:
276
+ # STATION_POSITION has been created but needs prooper dimensions and attrs
277
+ # Drop the time dim
278
+ weather_xds["STATION_POSITION"] = weather_xds["STATION_POSITION"].sel(
279
+ time_weather=0, drop=True, method="nearest"
280
+ )
281
+ # borrow location frame attributes from antenna position
282
+ weather_xds["STATION_POSITION"].attrs = ant_position_with_ids.attrs
283
+ else:
284
+ # borrow from ant_posision_with_ids but without carrying over other coords
285
+ weather_xds = weather_xds.assign(
286
+ {
287
+ "STATION_POSITION": (
288
+ ["station_name", "cartesian_pos_label"],
289
+ ant_position_with_ids.values,
290
+ ant_position_with_ids.attrs,
291
+ )
292
+ }
293
+ )
294
+
295
+ return weather_xds
296
+
297
+
298
+ def create_weather_xds(in_file: str, ant_position_with_ids: xr.DataArray):
176
299
  """
177
300
  Creates a Weather Xarray Dataset from a MS v2 WEATHER table.
178
301
 
@@ -180,8 +303,9 @@ def create_weather_xds(in_file: str, ant_xds_station_name_ids: xr.DataArray):
180
303
  ----------
181
304
  in_file : str
182
305
  Input MS name.
183
- ant_xds_station_name_ids : xr.DataArray
184
- station name data array from antenna_xds, with name/id information
306
+ ant_position_with_ids : xr.DataArray
307
+ antenna_position data var from the antenna_xds (expected to still including the initial ANTENNA_ID coordinate
308
+ as wellas other coordinates from the antenna_xds)
185
309
 
186
310
  Returns
187
311
  -------
@@ -190,32 +314,32 @@ def create_weather_xds(in_file: str, ant_xds_station_name_ids: xr.DataArray):
190
314
  """
191
315
 
192
316
  try:
193
- taql_where = make_taql_where_weather(in_file, ant_xds_station_name_ids)
194
317
  generic_weather_xds = load_generic_table(
195
318
  in_file,
196
319
  "WEATHER",
197
320
  rename_ids=subt_rename_ids["WEATHER"],
198
- taql_where=taql_where,
199
321
  )
200
322
  except ValueError as _exc:
201
323
  return None
202
324
 
203
- if not generic_weather_xds.data_vars:
204
- # for example when the weather subtable only has info for antennas/stations
205
- # not present in the MSv4 (no overlap between antennas loaded in ant_xds and weather)
325
+ has_asdm_station_position = (
326
+ "NS_WX_STATION_POSITION" in generic_weather_xds.data_vars
327
+ )
328
+ generic_weather_xds, station_name = prepare_generic_weather_xds_and_station_name(
329
+ generic_weather_xds, in_file, ant_position_with_ids, has_asdm_station_position
330
+ )
331
+ if not generic_weather_xds:
206
332
  return None
207
333
 
208
334
  weather_xds = xr.Dataset(attrs={"type": "weather"})
209
- stations_present = ant_xds_station_name_ids.sel(
210
- antenna_id=generic_weather_xds["ANTENNA_ID"]
211
- )
212
335
  coords = {
213
- "station_name": stations_present.data,
214
- "antenna_name": stations_present.coords["antenna_name"].data,
336
+ "station_name": station_name,
337
+ "cartesian_pos_label": ["x", "y", "z"],
215
338
  }
216
339
  weather_xds = weather_xds.assign_coords(coords)
217
340
 
218
341
  dims_station_time = ["station_name", "time_weather"]
342
+ dims_station_time_position = dims_station_time + ["cartesian_pos_label"]
219
343
  to_new_data_variables = {
220
344
  "H20": ["H2O", dims_station_time],
221
345
  "IONOS_ELECTRON": ["IONOS_ELECTRON", dims_station_time],
@@ -226,6 +350,15 @@ def create_weather_xds(in_file: str, ant_xds_station_name_ids: xr.DataArray):
226
350
  "WIND_DIRECTION": ["WIND_DIRECTION", dims_station_time],
227
351
  "WIND_SPEED": ["WIND_SPEED", dims_station_time],
228
352
  }
353
+ if has_asdm_station_position:
354
+ to_new_data_variables.update(
355
+ {
356
+ "NS_WX_STATION_POSITION": [
357
+ "STATION_POSITION",
358
+ dims_station_time_position,
359
+ ],
360
+ }
361
+ )
229
362
 
230
363
  to_new_coords = {
231
364
  "TIME": ["time_weather", ["time_weather"]],
@@ -234,6 +367,9 @@ def create_weather_xds(in_file: str, ant_xds_station_name_ids: xr.DataArray):
234
367
  weather_xds = convert_generic_xds_to_xradio_schema(
235
368
  generic_weather_xds, weather_xds, to_new_data_variables, to_new_coords
236
369
  )
370
+ weather_xds = finalize_station_position(
371
+ weather_xds, ant_position_with_ids, has_asdm_station_position
372
+ )
237
373
 
238
374
  # TODO: option to interpolate to main time
239
375
 
@@ -256,6 +392,7 @@ def correct_generic_pointing_xds(
256
392
  and tries to correct several deviations from the MSv2 specs seen in
257
393
  common test data.
258
394
  The problems fixed here include wrong dimensions:
395
+
259
396
  - for example transposed dimensions with respect to the MSv2 specs (output
260
397
  from CASA simulator),
261
398
  - missing/additional unexpected dimensions when some of the columns are
@@ -423,6 +560,7 @@ def prepare_generic_sys_cal_xds(generic_sys_cal_xds: xr.Dataset) -> xr.Dataset:
423
560
  sys_cal_xds dataset, as their structure differs in dimensions and order
424
561
  of dimensions.
425
562
  This function performs various prepareation steps, such as:
563
+
426
564
  - filter out dimensions not neeed for an individual MSv4 (SPW, FEED),
427
565
  - drop variables loaded from columns with all items set to empty array,
428
566
  - transpose the dimensions frequency,receptor
@@ -93,17 +93,34 @@ class MeasurementSetXdt:
93
93
  if data_group_name is not None:
94
94
  sel_data_group_set = set(
95
95
  self._xdt.attrs["data_groups"][data_group_name].values()
96
- )
96
+ ) - set(["date", "description"])
97
+
98
+ sel_field_and_source_xds = self._xdt.attrs["data_groups"][data_group_name][
99
+ "field_and_source"
100
+ ]
97
101
 
98
102
  data_variables_to_drop = []
99
- for dg in self._xdt.attrs["data_groups"].values():
100
- temp_set = set(dg.values()) - sel_data_group_set
103
+ field_and_source_to_drop = []
104
+ for dg_name, dg in self._xdt.attrs["data_groups"].items():
105
+ print(f"Data group: {dg_name}", dg)
106
+ f_and_s = dg["field_and_source"]
107
+ dg_copy = dg.copy()
108
+ dg_copy.pop("date", None)
109
+ dg_copy.pop("description", None)
110
+ dg_copy.pop("field_and_source", None)
111
+ temp_set = set(dg_copy.values()) - sel_data_group_set
101
112
  data_variables_to_drop.extend(list(temp_set))
102
113
 
114
+ if f_and_s != sel_field_and_source_xds:
115
+ field_and_source_to_drop.append(f_and_s)
116
+
103
117
  data_variables_to_drop = list(set(data_variables_to_drop))
104
118
 
105
119
  sel_ms_xdt = self._xdt
106
120
 
121
+ print("Data variables to drop: ", data_variables_to_drop)
122
+ print("Field and source to drop: ", field_and_source_to_drop)
123
+
107
124
  sel_corr_xds = self._xdt.ds.sel(
108
125
  indexers, method, tolerance, drop, **indexers_kwargs
109
126
  ).drop_vars(data_variables_to_drop)
@@ -140,7 +157,10 @@ class MeasurementSetXdt:
140
157
  else:
141
158
  data_group_name = list(self._xdt.attrs["data_groups"].keys())[0]
142
159
 
143
- return self._xdt[f"field_and_source_xds_{data_group_name}"].ds
160
+ field_and_source_xds_name = self._xdt.attrs["data_groups"][data_group_name][
161
+ "field_and_source"
162
+ ]
163
+ return self._xdt[field_and_source_xds_name].ds
144
164
 
145
165
  def get_partition_info(self, data_group_name: str = None) -> dict:
146
166
  """
@@ -195,3 +215,105 @@ class MeasurementSetXdt:
195
215
  }
196
216
 
197
217
  return partition_info
218
+
219
+ def add_data_group(
220
+ self,
221
+ new_data_group_name: str,
222
+ correlated_data: str = None,
223
+ weight: str = None,
224
+ flag: str = None,
225
+ uvw: str = None,
226
+ field_and_source_xds: str = None,
227
+ date_time: str = None,
228
+ description: str = None,
229
+ data_group_dv_shared_with: str = None,
230
+ ) -> xr.DataTree:
231
+ """_summary_
232
+
233
+ Parameters
234
+ ----------
235
+ new_data_group_name : str
236
+ _description_
237
+ correlated_data : str, optional
238
+ _description_, by default None
239
+ weights : str, optional
240
+ _description_, by default None
241
+ flag : str, optional
242
+ _description_, by default None
243
+ uvw : str, optional
244
+ _description_, by default None
245
+ field_and_source_xds : str, optional
246
+ _description_, by default None
247
+ date_time : str, optional
248
+ _description_, by default None
249
+ description : str, optional
250
+ _description_, by default None
251
+ data_group_dv_shared_with : str, optional
252
+ _description_, by default "base"
253
+
254
+ Returns
255
+ -------
256
+ xr.DataTree
257
+ _description_
258
+ """
259
+
260
+ if data_group_dv_shared_with is None:
261
+ data_group_dv_shared_with = self._xdt.xr_ms._get_default_data_group_name()
262
+ default_data_group = self._xdt.attrs["data_groups"][data_group_dv_shared_with]
263
+
264
+ new_data_group = {}
265
+
266
+ if correlated_data is None:
267
+ correlated_data = default_data_group["correlated_data"]
268
+ new_data_group["correlated_data"] = correlated_data
269
+ assert (
270
+ correlated_data in self._xdt.ds.data_vars
271
+ ), f"Data variable {correlated_data} not found in dataset."
272
+
273
+ if weight is None:
274
+ weight = default_data_group["weight"]
275
+ new_data_group["weight"] = weight
276
+ assert (
277
+ weight in self._xdt.ds.data_vars
278
+ ), f"Data variable {weight} not found in dataset."
279
+
280
+ if flag is None:
281
+ flag = default_data_group["flag"]
282
+ new_data_group["flag"] = flag
283
+ assert (
284
+ flag in self._xdt.ds.data_vars
285
+ ), f"Data variable {flag} not found in dataset."
286
+
287
+ if self._xdt.attrs["type"] == "visibility":
288
+ if uvw is None:
289
+ uvw = default_data_group["uvw"]
290
+ new_data_group["uvw"] = uvw
291
+ assert (
292
+ uvw in self._xdt.ds.data_vars
293
+ ), f"Data variable {uvw} not found in dataset."
294
+
295
+ if field_and_source_xds is None:
296
+ field_and_source_xds = default_data_group["field_and_source_xds"]
297
+ new_data_group["field_and_source"] = field_and_source_xds
298
+ assert (
299
+ field_and_source_xds in self._xdt.children
300
+ ), f"Data variable {field_and_source_xds} not found in dataset."
301
+
302
+ if date_time is None:
303
+ date_time = datetime.now().isoformat()
304
+ new_data_group["date"] = date_time
305
+
306
+ if description is None:
307
+ description = ""
308
+ new_data_group["description"] = description
309
+
310
+ self._xdt.attrs["data_groups"][new_data_group_name] = new_data_group
311
+
312
+ return self._xdt
313
+
314
+ def _get_default_data_group_name(self):
315
+ if "base" in self._xdt.attrs["data_groups"].keys():
316
+ data_group_name = "base"
317
+ else:
318
+ data_group_name = list(self._xdt.attrs["data_groups"].keys())[0]
319
+ return data_group_name