xradio 1.0.1__py3-none-any.whl → 1.1.0__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 (44) hide show
  1. xradio/_utils/_casacore/casacore_from_casatools.py +1 -1
  2. xradio/_utils/dict_helpers.py +38 -7
  3. xradio/_utils/list_and_array.py +26 -3
  4. xradio/_utils/schema.py +44 -0
  5. xradio/_utils/xarray_helpers.py +63 -0
  6. xradio/_utils/zarr/common.py +4 -2
  7. xradio/image/__init__.py +4 -2
  8. xradio/image/_util/_casacore/common.py +2 -1
  9. xradio/image/_util/_casacore/xds_from_casacore.py +105 -51
  10. xradio/image/_util/_casacore/xds_to_casacore.py +117 -52
  11. xradio/image/_util/_fits/xds_from_fits.py +124 -36
  12. xradio/image/_util/_zarr/common.py +0 -1
  13. xradio/image/_util/casacore.py +133 -16
  14. xradio/image/_util/common.py +6 -5
  15. xradio/image/_util/image_factory.py +466 -27
  16. xradio/image/image.py +72 -100
  17. xradio/image/image_xds.py +262 -0
  18. xradio/image/schema.py +85 -0
  19. xradio/measurement_set/__init__.py +5 -4
  20. xradio/measurement_set/_utils/_msv2/_tables/read.py +7 -3
  21. xradio/measurement_set/_utils/_msv2/conversion.py +6 -9
  22. xradio/measurement_set/_utils/_msv2/create_field_and_source_xds.py +1 -0
  23. xradio/measurement_set/_utils/_msv2/msv4_sub_xdss.py +1 -1
  24. xradio/measurement_set/_utils/_utils/interpolate.py +5 -0
  25. xradio/measurement_set/_utils/_utils/partition_attrs.py +0 -1
  26. xradio/measurement_set/convert_msv2_to_processing_set.py +9 -9
  27. xradio/measurement_set/load_processing_set.py +2 -2
  28. xradio/measurement_set/measurement_set_xdt.py +83 -93
  29. xradio/measurement_set/open_processing_set.py +7 -3
  30. xradio/measurement_set/processing_set_xdt.py +33 -26
  31. xradio/schema/check.py +70 -19
  32. xradio/schema/common.py +0 -1
  33. xradio/testing/__init__.py +0 -0
  34. xradio/testing/_utils/__template__.py +58 -0
  35. xradio/testing/measurement_set/__init__.py +58 -0
  36. xradio/testing/measurement_set/checker.py +131 -0
  37. xradio/testing/measurement_set/io.py +22 -0
  38. xradio/testing/measurement_set/msv2_io.py +1854 -0
  39. {xradio-1.0.1.dist-info → xradio-1.1.0.dist-info}/METADATA +64 -23
  40. xradio-1.1.0.dist-info/RECORD +75 -0
  41. {xradio-1.0.1.dist-info → xradio-1.1.0.dist-info}/WHEEL +1 -1
  42. xradio-1.0.1.dist-info/RECORD +0 -66
  43. {xradio-1.0.1.dist-info → xradio-1.1.0.dist-info}/licenses/LICENSE.txt +0 -0
  44. {xradio-1.0.1.dist-info → xradio-1.1.0.dist-info}/top_level.txt +0 -0
@@ -9,14 +9,17 @@ import warnings
9
9
  from typing import Union
10
10
 
11
11
  import xarray as xr
12
+ import re
13
+
14
+ from xradio._utils.schema import get_data_group_keys
12
15
 
13
16
  try:
14
17
  from casacore import tables
15
18
  except ImportError:
16
19
  import xradio._utils._casacore.casacore_from_casatools as tables
17
20
 
18
- from ._casacore.common import _open_image_ro
19
- from ._casacore.xds_from_casacore import (
21
+
22
+ from xradio.image._util._casacore.xds_from_casacore import (
20
23
  _add_mask,
21
24
  _add_sky_or_aperture,
22
25
  _casa_image_to_xds_attrs,
@@ -28,18 +31,25 @@ from ._casacore.xds_from_casacore import (
28
31
  _get_beam,
29
32
  _read_image_array,
30
33
  )
31
- from ._casacore.xds_to_casacore import (
34
+ from xradio.image._util._casacore.xds_to_casacore import (
32
35
  _coord_dict_from_xds,
33
36
  _history_from_xds,
34
37
  _imageinfo_dict_from_xds,
35
38
  _write_casa_data,
36
39
  )
37
- from .common import _aperture_or_sky, _get_xds_dim_order, _dask_arrayize_dv
40
+ from xradio.image._util.common import (
41
+ _aperture_or_sky,
42
+ _get_xds_dim_order,
43
+ _dask_arrayize_dv,
44
+ )
45
+ from xradio.image._util._casacore.common import _beam_fit_params, _open_image_ro
38
46
 
39
47
  warnings.filterwarnings("ignore", category=FutureWarning)
40
48
 
41
49
 
42
- def _load_casa_image_block(infile: str, block_des: dict, do_sky_coords) -> xr.Dataset:
50
+ def _load_casa_image_block(
51
+ infile: str, block_des: dict, do_sky_coords, image_type: str
52
+ ) -> xr.Dataset:
43
53
  image_full_path = os.path.expanduser(infile)
44
54
  with _open_image_ro(image_full_path) as casa_image:
45
55
  coords = casa_image.coordinates()
@@ -55,7 +65,7 @@ def _load_casa_image_block(infile: str, block_des: dict, do_sky_coords) -> xr.Da
55
65
  image_full_path, shapes, starts, dimorder, transpose_list, new_axes
56
66
  )
57
67
  xds = _add_sky_or_aperture(
58
- xds, block, dimorder, image_full_path, ret["sphr_dims"], True
68
+ xds, block, dimorder, image_full_path, ret["sphr_dims"], False, image_type
59
69
  )
60
70
  mymasks = _get_mask_names(image_full_path)
61
71
  for m in mymasks:
@@ -64,26 +74,35 @@ def _load_casa_image_block(infile: str, block_des: dict, do_sky_coords) -> xr.Da
64
74
  full_path, shapes, starts, dimorder, transpose_list, new_axes
65
75
  )
66
76
  # data vars are all caps by convention
67
- xds = _add_mask(xds, m.upper(), block, dimorder)
77
+ mask_name = re.sub(r"\bMASK(\d+)\b", r"MASK_\1", m.upper())
78
+ xds = _add_mask(xds, mask_name, block, dimorder)
68
79
  xds.attrs = _casa_image_to_xds_attrs(image_full_path)
69
- beam = _get_beam(image_full_path, nchan, npol, False)
80
+ beam = _get_beam(image_full_path, nchan, npol, False, image_type)
81
+
70
82
  if beam is not None:
71
83
  selectors = {
72
84
  k: block_des[k]
73
85
  for k in ("time", "frequency", "polarization")
74
86
  if k in block_des
75
87
  }
76
- xds["BEAM"] = beam.isel(selectors)
88
+ xds["BEAM_FIT_PARAMS_" + image_type.upper()] = beam.isel(selectors)
89
+ xds["BEAM_FIT_PARAMS_" + image_type.upper()].attrs["type"] = (
90
+ "beam_fit_params_" + image_type.lower()
91
+ )
92
+ xds[image_type.upper()].attrs[_beam_fit_params] = (
93
+ "BEAM_FIT_PARAMS_" + image_type.upper()
94
+ )
77
95
  return xds
78
96
 
79
97
 
80
- def _read_casa_image(
98
+ def _open_casa_image(
81
99
  infile: str,
82
100
  chunks: Union[list, dict],
83
101
  verbose: bool,
84
102
  do_sky_coords: bool,
85
103
  masks: bool = True,
86
- history: bool = True,
104
+ history: bool = False,
105
+ image_type: str = "SKY",
87
106
  ) -> xr.Dataset:
88
107
  img_full_path = os.path.expanduser(infile)
89
108
  ret = _casa_image_to_xds_coords(img_full_path, verbose, do_sky_coords)
@@ -96,26 +115,124 @@ def _read_casa_image(
96
115
  img_full_path,
97
116
  ret["sphr_dims"],
98
117
  history,
118
+ image_type=image_type,
99
119
  )
100
120
  if masks:
101
121
  mymasks = _get_mask_names(img_full_path)
102
122
  for m in mymasks:
103
123
  ary = _read_image_array(img_full_path, chunks, mask=m, verbose=verbose)
104
124
  # data var names are all caps by convention
105
- xds = _add_mask(xds, m.upper(), ary, dimorder)
125
+ mask_name = re.sub(r"\bMASK(\d+)\b", r"MASK_\1", m.upper())
126
+ xds = _add_mask(xds, mask_name, ary, dimorder)
106
127
  xds.attrs = _casa_image_to_xds_attrs(img_full_path)
107
128
  beam = _get_beam(
108
- img_full_path, xds.sizes["frequency"], xds.sizes["polarization"], True
129
+ img_full_path,
130
+ xds.sizes["frequency"],
131
+ xds.sizes["polarization"],
132
+ True,
133
+ image_type,
109
134
  )
110
135
  if beam is not None:
111
- xds["BEAM"] = beam
136
+ xds["BEAM_FIT_PARAMS_" + image_type.upper()] = beam
137
+ xds["BEAM_FIT_PARAMS_" + image_type.upper()].attrs["type"] = (
138
+ "beam_fit_params_" + image_type.lower()
139
+ )
140
+ xds[image_type.upper()].attrs[_beam_fit_params] = (
141
+ "BEAM_FIT_PARAMS_" + image_type.upper()
142
+ )
143
+
112
144
  # xds = _add_coord_attrs(xds, ret["icoords"], ret["dir_axes"])
113
145
  xds = _dask_arrayize_dv(xds)
146
+
114
147
  return xds
115
148
 
116
149
 
117
- def _xds_to_casa_image(xds: xr.Dataset, imagename: str) -> None:
118
- image_full_path = os.path.expanduser(imagename)
150
+ def _xds_to_multiple_casa_images(xds: xr.Dataset, image_store_name: str) -> None:
151
+ """Function disentagles xradio xr.Dataset into multiple casa images based on data_groups attribute.
152
+ An xr.Dataset may contain multiple images (sky, residual, psf, etc) stored under different data variables sharing common coordinates.
153
+ An addtional complication is that CASA images allow for internal masks and beam fit parameters to be stored alongside the main image data so these also need to be handled.
154
+ This function creates separate casa images for each image type found in the data_groups attribute of the xr.Dataset.
155
+
156
+ Parameters
157
+ ----------
158
+ xds : xr.Dataset
159
+ The xradio xr.Dataset containing multiple images and associated data.
160
+ image_store_name : str
161
+ The base name or path for storing the output CASA images.
162
+ If only one image is written, it will be named image_store_name, esle the images
163
+ will be named image_store_name.<image_type> where <image_type> is sky, residual, point_spread_function, etc.
164
+ """
165
+
166
+ data_vars_name_set = set(xds.data_vars.keys())
167
+
168
+ data_group_keys = list(get_data_group_keys(schema_name="image").keys())
169
+ internal_image_types_to_exclude = [
170
+ "flag",
171
+ "beam_fit_params_sky",
172
+ "beam_fit_params_point_spread_function",
173
+ ]
174
+ n_image_written = 0
175
+ last_image_written = ""
176
+ for data_group in xds.attrs["data_groups"].keys():
177
+ for image_type in data_group_keys:
178
+ if (image_type in xds.attrs["data_groups"][data_group]) and (
179
+ image_type not in internal_image_types_to_exclude
180
+ ):
181
+ image_name = xds.attrs["data_groups"][data_group][image_type]
182
+ if image_name in data_vars_name_set:
183
+ image_to_write_xds = xr.Dataset()
184
+ image_to_write_xds.attrs = xds.attrs.copy()
185
+
186
+ if image_type == "aperture":
187
+ image_to_write_xds["APERTURE"] = xds[image_name]
188
+ else:
189
+ image_to_write_xds["SKY"] = xds[image_name]
190
+
191
+ # This code handles adding internal masks and beam fit params if they exist.
192
+ if image_type == "sky":
193
+ if (
194
+ "beam_fit_params_sky"
195
+ in xds.attrs["data_groups"][data_group]
196
+ ):
197
+ beam_fit_params_name = xds.attrs["data_groups"][data_group][
198
+ "beam_fit_params_sky"
199
+ ]
200
+ image_to_write_xds["BEAM_FIT_PARAMS"] = xds[
201
+ beam_fit_params_name
202
+ ]
203
+
204
+ if "flag" in xds.attrs["data_groups"][data_group]:
205
+ mask_sky_name = xds.attrs["data_groups"][data_group]["flag"]
206
+ image_to_write_xds["MASK_0"] = xds[mask_sky_name]
207
+ image_to_write_xds["SKY"].attrs["flag"] = "MASK_0"
208
+
209
+ if image_type == "point_spread_function":
210
+ if (
211
+ "beam_fit_params_point_spread_function"
212
+ in xds.attrs["data_groups"][data_group]
213
+ ):
214
+ beam_fit_params_name = xds.attrs["data_groups"][data_group][
215
+ "beam_fit_params_point_spread_function"
216
+ ]
217
+ image_to_write_xds["BEAM_FIT_PARAMS"] = xds[
218
+ beam_fit_params_name
219
+ ]
220
+ outname = image_store_name + "." + image_type
221
+ _xds_to_casa_image(image_to_write_xds, outname)
222
+ if not os.path.exists(outname):
223
+ raise IOError(f"Failed to write CASA image {outname}")
224
+ n_image_written += 1
225
+ last_image_written = outname
226
+ data_vars_name_set.remove(image_name)
227
+ if n_image_written == 0:
228
+ raise ValueError("No valid image types found in xds to write to CASA images.")
229
+ if n_image_written == 1:
230
+ # rename the single written image to what the user requested
231
+ os.rename(last_image_written, image_store_name)
232
+
233
+
234
+ def _xds_to_casa_image(xds: xr.Dataset, image_store_name: str) -> None:
235
+ image_full_path = os.path.expanduser(image_store_name)
119
236
  _write_casa_data(xds, image_full_path)
120
237
  # create coordinates
121
238
  ap_sky = _aperture_or_sky(xds)
@@ -7,6 +7,7 @@ from typing import Dict, List
7
7
  import xarray as xr
8
8
  from xradio._utils.coord_math import _deg_to_rad
9
9
  from xradio._utils.dict_helpers import make_quantity
10
+ import toolviper.utils.logger as logger
10
11
 
11
12
  _c = 2.99792458e08 * u.m / u.s
12
13
  # OPTICAL = Z
@@ -17,7 +18,7 @@ _doppler_types = [
17
18
  "beta",
18
19
  "gamma",
19
20
  ]
20
- _image_type = "image_type"
21
+ _image_type = "type"
21
22
 
22
23
 
23
24
  def _aperture_or_sky(xds: xr.Dataset) -> str:
@@ -252,10 +253,10 @@ def _compute_sky_reference_pixel(xds: xr.Dataset) -> np.ndarray:
252
253
 
253
254
  def _l_m_attr_notes() -> Dict[str, str]:
254
255
  return {
255
- "l": "l is the angle measured from the phase center to the east. "
256
- "So l = x*cdelt, where x is the number of pixels from the phase center. "
256
+ "l": "l is the angle measured from the reference direction to the east. "
257
+ "So l = x*cdelt, where x is the number of pixels from the reference direction. "
257
258
  "See AIPS Memo #27, Section III.",
258
- "m": "m is the angle measured from the phase center to the north. "
259
- "So m = y*cdelt, where y is the number of pixels from the phase center. "
259
+ "m": "m is the angle measured from the reference direction to the north. "
260
+ "So m = y*cdelt, where y is the number of pixels from the reference direction. "
260
261
  "See AIPS Memo #27, Section III.",
261
262
  }