xradio 1.0.2__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 +1 -1
  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.2.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.2.dist-info → xradio-1.1.0.dist-info}/WHEEL +1 -1
  42. xradio-1.0.2.dist-info/RECORD +0 -66
  43. {xradio-1.0.2.dist-info → xradio-1.1.0.dist-info}/licenses/LICENSE.txt +0 -0
  44. {xradio-1.0.2.dist-info → xradio-1.1.0.dist-info}/top_level.txt +0 -0
@@ -513,7 +513,7 @@ class image(casatools.image):
513
513
  self,
514
514
  imagename,
515
515
  axis=0,
516
- maskname="mask0",
516
+ maskname="mask_0",
517
517
  images=(),
518
518
  values=None,
519
519
  coordsys=None,
@@ -1,3 +1,6 @@
1
+ from xradio._utils.list_and_array import to_python_type
2
+
3
+
1
4
  def make_quantity(value, units: str, dims: list = []) -> dict:
2
5
  """
3
6
  create a quantity dictionary given value and units
@@ -13,7 +16,11 @@ def make_quantity(value, units: str, dims: list = []) -> dict:
13
16
  -------
14
17
  dict
15
18
  """
16
- return {"data": value, "dims": dims, "attrs": make_quantity_attrs(units)}
19
+ return {
20
+ "data": to_python_type(value),
21
+ "dims": dims,
22
+ "attrs": make_quantity_attrs(units),
23
+ }
17
24
 
18
25
 
19
26
  def ensure_units_are_consistent(units):
@@ -67,7 +74,7 @@ def make_spectral_coord_reference_dict(
67
74
  u,
68
75
  observer.lower() if observer not in ["TOPO", "BARY", "REST"] else observer,
69
76
  ),
70
- "data": value,
77
+ "data": to_python_type(value),
71
78
  "dims": [],
72
79
  }
73
80
 
@@ -98,14 +105,38 @@ def make_skycoord_dict(data: list[float], units: str, frame: str) -> dict:
98
105
  "type": "sky_coord",
99
106
  "units": ensure_units_are_consistent(units),
100
107
  },
101
- "data": data,
102
- "dims": ["l", "m"],
108
+ "data": to_python_type(data),
109
+ "dims": "sky_dir_label",
110
+ "coords": {"sky_dir_label": {"data": ["ra", "dec"], "dims": "sky_dir_label"}},
111
+ }
112
+
113
+
114
+ def make_direction_location_dict(data: list[float], units: str, frame: str) -> dict:
115
+ return {
116
+ "attrs": {
117
+ "frame": frame.upper(),
118
+ "type": "location",
119
+ "units": ensure_units_are_consistent(units),
120
+ },
121
+ "data": to_python_type(data),
122
+ "dims": "ellipsoid_dir_label",
123
+ "coords": {
124
+ "ellipsoid_dir_label": {
125
+ "data": ["lon", "lat"],
126
+ "dims": "ellipsoid_dir_label",
127
+ }
128
+ },
103
129
  }
104
130
 
105
131
 
106
132
  def make_time_measure_attrs(units="s", scale="utc", time_format="mjd") -> dict:
107
133
  u = ensure_units_are_consistent(units)
108
- return {"units": u, "scale": scale, "format": time_format, "type": "time"}
134
+ return {
135
+ "units": u,
136
+ "scale": scale.lower(),
137
+ "format": time_format.lower(),
138
+ "type": "time",
139
+ }
109
140
 
110
141
 
111
142
  def make_time_measure_dict(data, units="s", scale="utc", time_format="mjd") -> dict:
@@ -127,7 +158,7 @@ def make_time_measure_dict(data, units="s", scale="utc", time_format="mjd") -> d
127
158
  """
128
159
  x = {}
129
160
  x["attrs"] = make_time_measure_attrs(units, scale, time_format)
130
- x["data"] = data
161
+ x["data"] = to_python_type(data)
131
162
  x["dims"] = []
132
163
  return x
133
164
 
@@ -149,7 +180,7 @@ def make_time_coord_attrs(units="s", scale="utc", time_format="mjd") -> dict:
149
180
  -------
150
181
  dict
151
182
  """
152
- x = make_time_measure_attrs(units, scale, time_format)
183
+ x = make_time_measure_attrs(units, scale.lower(), time_format.lower())
153
184
  del x["type"]
154
185
  return x
155
186
 
@@ -53,11 +53,34 @@ def get_pad_value(col_dtype: np.dtype) -> object:
53
53
  )
54
54
 
55
55
 
56
+ def to_python_type(x):
57
+ """
58
+ Convert any NumPy scalar, array, or nested structure to native Python types.
59
+
60
+ - np.float32, np.float64 → float
61
+ - np.int32, np.int64 → int
62
+ - np.bool_ → bool
63
+ - np.ndarray → list of Python-native types
64
+ - nested containers (list/tuple/dict) are handled recursively
65
+ """
66
+ if isinstance(x, np.generic): # covers all numpy scalar types
67
+ return x.item()
68
+ elif isinstance(x, np.ndarray):
69
+ return x.tolist()
70
+ elif isinstance(x, (list, tuple)):
71
+ return type(x)(to_python_type(v) for v in x)
72
+ elif isinstance(x, dict):
73
+ return {k: to_python_type(v) for k, v in x.items()}
74
+ else:
75
+ return x
76
+
77
+
56
78
  def to_list(x):
57
79
  if isinstance(x, np.ndarray):
58
- if x.ndim == 0:
59
- return [x.item()]
60
- return list(x) # needed for json serialization
80
+ z = x.astype(float)
81
+ if z.ndim == 0:
82
+ return [z.item()]
83
+ return list(z) # needed for json serialization
61
84
  elif isinstance(x, list):
62
85
  return x
63
86
  return [x]
xradio/_utils/schema.py CHANGED
@@ -224,3 +224,47 @@ casa_frequency_frames = [
224
224
  ]
225
225
 
226
226
  casa_frequency_frames_codes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 64]
227
+
228
+
229
+ def get_data_group_keys(schema_name: str) -> list[tuple[str, bool]]:
230
+ """Return (name, is_optional) pairs for data group keys for the given schema.
231
+
232
+ Parameters
233
+ ----------
234
+ schema_name : str
235
+ The name of the schema to retrieve data group keys for. Supported values are "msv4" and "image".
236
+
237
+ Returns
238
+ -------
239
+ list[tuple[str, bool]]
240
+ A list of tuples (key_name, is_optional) for the specified schema.
241
+
242
+ Raises
243
+ ------
244
+ ValueError
245
+ If the schema name is unknown.
246
+ """
247
+ from typing import get_type_hints, get_origin, get_args, Union
248
+
249
+ if schema_name == "msv4":
250
+ from xradio.measurement_set.schema import DataGroupDict
251
+
252
+ cls = DataGroupDict
253
+ elif schema_name == "image":
254
+ from xradio.image.schema import DataGroupDict
255
+
256
+ cls = DataGroupDict
257
+ else:
258
+ raise ValueError(f"Unknown schema name: {schema_name}")
259
+
260
+ annotations = get_type_hints(cls)
261
+ keys_with_optional = {}
262
+ for name, anno in annotations.items():
263
+ origin = get_origin(anno)
264
+ is_optional = False
265
+ if origin is Union:
266
+ args = get_args(anno)
267
+ is_optional = type(None) in args
268
+ keys_with_optional[name] = is_optional
269
+
270
+ return keys_with_optional
@@ -0,0 +1,63 @@
1
+ import xarray as xr
2
+ from xradio._utils.schema import get_data_group_keys
3
+ from collections.abc import Mapping, Iterable
4
+ from typing import Any, Union
5
+
6
+
7
+ def get_data_group_name(
8
+ xdx: Union[xr.Dataset, xr.DataTree], data_group_name: str = None
9
+ ) -> str:
10
+
11
+ if data_group_name is None:
12
+ if "base" in xdx.attrs["data_groups"]:
13
+ data_group_name = "base"
14
+ else:
15
+ data_group_name = list(xdx.attrs["data_groups"].keys())[0]
16
+
17
+ return data_group_name
18
+
19
+
20
+ def create_new_data_group(
21
+ xdx: Union[xr.Dataset, xr.DataTree],
22
+ schema_name: str,
23
+ new_data_group_name: str,
24
+ data_group: dict,
25
+ data_group_dv_shared_with: str = None,
26
+ ) -> xr.DataTree:
27
+ """Adds a data group to Xarray Data Structure (Dataset or DataTree).
28
+
29
+ Parameters
30
+ ----------
31
+ new_data_group_name : str
32
+ The name of the new data group to add.
33
+ data_group : dict
34
+ A dictionary containing the data group variables and their attributes.
35
+ data_group_dv_shared_with : str, optional
36
+ The name of the data group to share data variables with, by default "base"
37
+
38
+ Returns
39
+ -------
40
+ xr.DataTree
41
+ MSv4 DataTree with the new group added
42
+ """
43
+
44
+ data_group_dv_shared_with = get_data_group_name(xdx, data_group_dv_shared_with)
45
+
46
+ default_data_group = xdx.attrs["data_groups"][data_group_dv_shared_with]
47
+
48
+ new_data_group = {}
49
+
50
+ data_group_keys = get_data_group_keys(schema_name)
51
+
52
+ for key, optional in data_group_keys.items():
53
+ if key in data_group:
54
+ new_data_group[key] = data_group[key]
55
+ else:
56
+ if key not in default_data_group and not optional:
57
+ raise ValueError(
58
+ f"Data group key '{key}' is required but not provided and not present in shared data group '{data_group_dv_shared_with}'."
59
+ )
60
+ elif key in default_data_group:
61
+ new_data_group[key] = default_data_group[key]
62
+
63
+ return new_data_group_name, new_data_group
@@ -1,7 +1,5 @@
1
1
  import xarray as xr
2
- import s3fs
3
2
  import os
4
- from botocore.exceptions import NoCredentialsError
5
3
 
6
4
  # from xradio.vis._vis_utils._ms.msv2_to_msv4_meta import (
7
5
  # column_description_casacore_to_msv4_measure,
@@ -10,6 +8,9 @@ from botocore.exceptions import NoCredentialsError
10
8
 
11
9
  def _get_file_system_and_items(ps_store: str):
12
10
 
11
+ import s3fs
12
+ from botocore.exceptions import NoCredentialsError
13
+
13
14
  # default to assuming the data are accessible on local file system
14
15
  if os.path.isdir(ps_store):
15
16
  # handle a common shell convention
@@ -74,6 +75,7 @@ def _open_dataset(
74
75
  """
75
76
 
76
77
  import dask
78
+ import s3fs
77
79
 
78
80
  if isinstance(file_system, s3fs.core.S3FileSystem):
79
81
  mapping = s3fs.S3Map(root=store, s3=file_system, check=False)
xradio/image/__init__.py CHANGED
@@ -5,15 +5,17 @@ from .image import (
5
5
  make_empty_aperture_image,
6
6
  make_empty_lmuv_image,
7
7
  make_empty_sky_image,
8
- read_image,
8
+ open_image,
9
9
  write_image,
10
10
  )
11
11
 
12
+ from .image_xds import ImageXds
13
+
12
14
  __all__ = [
13
15
  "load_image",
14
16
  "make_empty_aperture_image",
15
17
  "make_empty_lmuv_image",
16
18
  "make_empty_sky_image",
17
- "read_image",
19
+ "open_image",
18
20
  "write_image",
19
21
  ]
@@ -42,7 +42,8 @@ def _create_new_image(
42
42
  del image
43
43
 
44
44
 
45
- _active_mask = "active_mask"
45
+ _image_flag = "flag"
46
+ _beam_fit_params = "beam_fit_params"
46
47
  # _native_types = ["FREQ", "VRAD", "VOPT", "BETA", "WAVE", "AWAV"]
47
48
  _object_name = "object_name"
48
49
  _pointing_center = "pointing_center"
@@ -9,6 +9,7 @@ import toolviper.utils.logger as logger
9
9
  import numpy as np
10
10
  import xarray as xr
11
11
  from astropy import units as u
12
+ from xradio._utils.list_and_array import to_python_type
12
13
 
13
14
  try:
14
15
  from casacore import tables
@@ -19,14 +20,15 @@ except ImportError:
19
20
  from xradio._utils._casacore.casacore_from_casatools import image as casa_image
20
21
 
21
22
 
22
- from .common import (
23
- _active_mask,
23
+ from xradio.image._util._casacore.common import (
24
+ _image_flag,
25
+ _beam_fit_params,
24
26
  _object_name,
25
27
  _open_image_ro,
26
28
  _pointing_center,
27
29
  )
28
30
 
29
- from ..common import (
31
+ from xradio.image._util.common import (
30
32
  _compute_linear_world_values,
31
33
  _compute_velocity_values,
32
34
  _compute_world_sph_dims,
@@ -37,10 +39,11 @@ from ..common import (
37
39
  _image_type,
38
40
  _l_m_attr_notes,
39
41
  )
40
- from ...._utils._casacore.tables import extract_table_attributes, open_table_ro
42
+ from xradio._utils._casacore.tables import extract_table_attributes, open_table_ro
41
43
  from xradio._utils.coord_math import _deg_to_rad
42
44
  from xradio._utils.dict_helpers import (
43
45
  _casacore_q_to_xradio_q,
46
+ make_direction_location_dict,
44
47
  make_spectral_coord_reference_dict,
45
48
  make_quantity,
46
49
  make_skycoord_dict,
@@ -68,13 +71,15 @@ def _add_mask(
68
71
  xda = xr.DataArray(ary, dims=dimorder)
69
72
  # True pixels are good in numpy masked arrays
70
73
  xda = da.logical_not(xda)
71
- xda.attrs["image_type"] = "Mask"
74
+ xda.attrs["type"] = "flag"
72
75
  xda = xda.rename(name)
73
76
  xds[xda.name] = xda
74
77
  return xds
75
78
 
76
79
 
77
- def _casa_image_to_xds_image_attrs(image: casa_image, history: bool = True) -> dict:
80
+ def _casa_image_to_xds_image_attrs(
81
+ image: casa_image, history: bool = False, image_type: str = "SKY"
82
+ ) -> dict:
78
83
  """
79
84
  get the image attributes from the casacoreimage object
80
85
  """
@@ -177,20 +182,27 @@ def _casa_image_to_xds_image_attrs(image: casa_image, history: bool = True) -> d
177
182
  obj = "objectname"
178
183
  attrs[_object_name] = imageinfo[obj] if obj in imageinfo else ""
179
184
  attrs["user"] = meta_dict["miscinfo"]
185
+ """
180
186
  defmask = "Image_defaultmask"
181
187
  with open_table_ro(image.name()) as casa_table:
182
188
  # the actual mask is a data var and data var names are all caps by convention
183
- attrs[_active_mask] = (
184
- casa_table.getkeyword(defmask).upper()
185
- if defmask in casa_table.keywordnames()
186
- else None
187
- )
189
+ import re
190
+
191
+ if defmask in casa_table.keywordnames():
192
+ am = casa_table.getkeyword(defmask).upper()
193
+ am = re.sub(r"\bMASK(\d+)\b", r"MASK_\1", am)
194
+ else:
195
+ am = None
196
+ attrs[_image_flag] = "FLAG_" + image_type.upper()
197
+ """
188
198
  attrs["description"] = None
189
- # if also loading history, put it as another xds in the image attrs
199
+ # Store history as a dict (not xr.Dataset) for Xarray compatibility
190
200
  if history:
191
201
  htable = os.sep.join([os.path.abspath(image.name()), "logtable"])
192
202
  if os.path.isdir(htable):
193
- attrs["history"] = read_generic_table(htable)
203
+ history_xds = read_generic_table(htable)
204
+ # Convert xr.Dataset to dict for serialization compatibility
205
+ attrs["history"] = history_xds.to_dict()
194
206
  else:
195
207
  logger.warning(
196
208
  f"Unable to find history table {htable}. History will not be included"
@@ -205,13 +217,14 @@ def _add_sky_or_aperture(
205
217
  img_full_path: str,
206
218
  has_sph_dims: bool,
207
219
  history: bool,
220
+ image_type: str = "SKY",
208
221
  ) -> xr.Dataset:
209
222
  xda = xr.DataArray(ary, dims=dimorder).astype(ary.dtype)
210
223
  with _open_image_ro(img_full_path) as casa_image:
211
- xda.attrs = _casa_image_to_xds_image_attrs(casa_image, history)
224
+ xda.attrs = _casa_image_to_xds_image_attrs(casa_image, history, image_type)
212
225
  # xds.attrs = attrs
213
- name = "SKY" if has_sph_dims else "APERTURE"
214
- xda = xda.rename(name)
226
+ # name = "SKY" if has_sph_dims else "APERTURE"
227
+ xda = xda.rename(image_type)
215
228
  xds[xda.name] = xda
216
229
  return xds
217
230
 
@@ -257,6 +270,8 @@ def _casa_image_to_xds_attrs(img_full_path: str) -> dict:
257
270
  """
258
271
  with _open_image_ro(img_full_path) as casa_image:
259
272
  meta_dict = casa_image.info()
273
+ # print("meta_dict:", meta_dict)
274
+ # print("***********")
260
275
  coord_dict = copy.deepcopy(meta_dict["coordinates"])
261
276
  attrs = {}
262
277
  dir_key = None
@@ -272,31 +287,67 @@ def _casa_image_to_xds_attrs(img_full_path: str) -> dict:
272
287
  raise RuntimeError("No direction reference frame found")
273
288
  casa_system = coord_dir_dict[system]
274
289
  ap_system, ap_equinox = _convert_direction_system(casa_system, "native")
275
- dir_dict = {}
276
290
 
277
- dir_dict["reference"] = make_skycoord_dict(
278
- data=[0.0, 0.0], units="rad", frame=ap_system
291
+ coordinate_system_info = {}
292
+
293
+ unit0 = u.Unit(_get_unit(coord_dir_dict["units"][0]))
294
+ unit1 = u.Unit(_get_unit(coord_dir_dict["units"][1]))
295
+ ra = float((coord_dir_dict["crval"][0] * unit0).to("rad").value)
296
+ dec = float((coord_dir_dict["crval"][1] * unit1).to("rad").value)
297
+ coordinate_system_info["reference_direction"] = make_skycoord_dict(
298
+ data=[ra, dec], units="rad", frame=ap_system
279
299
  )
280
300
  if ap_equinox:
281
- dir_dict["reference"]["attrs"]["equinox"] = ap_equinox
282
- for i in range(2):
283
- unit = u.Unit(_get_unit(coord_dir_dict["units"][i]))
284
- q = coord_dir_dict["crval"][i] * unit
285
- x = q.to("rad")
286
- dir_dict["reference"]["data"][i] = x.value
287
- k = "latpole"
288
- if k in coord_dir_dict:
289
- for j in (k, "lonpole"):
290
- m = "longpole" if j == "lonpole" else j
291
- dir_dict[j] = make_quantity(
292
- value=coord_dir_dict[m] * _deg_to_rad, units="rad", dims=["l", "m"]
293
- )
294
- for j in ("pc", "projection_parameters", "projection"):
295
- if j in coord_dir_dict:
296
- dir_dict[j] = coord_dir_dict[j]
297
- attrs["direction"] = dir_dict
301
+ coordinate_system_info["reference_direction"]["attrs"][
302
+ "equinox"
303
+ ] = ap_equinox
304
+
305
+ pol_dir = [-1, coord_dir_dict["latpole"] * _deg_to_rad]
306
+ if "lonpole" in coord_dir_dict:
307
+ pol_dir[0] = coord_dir_dict["lonpole"] * _deg_to_rad
308
+ else:
309
+ pol_dir[0] = coord_dir_dict["longpole"] * _deg_to_rad
310
+
311
+ coordinate_system_info["native_pole_direction"] = make_direction_location_dict(
312
+ pol_dir, "rad", "native_projection"
313
+ )
314
+
315
+ coordinate_system_info["projection"] = coord_dir_dict["projection"]
316
+ coordinate_system_info["projection_parameters"] = to_python_type(
317
+ coord_dir_dict["projection_parameters"]
318
+ )
319
+ coordinate_system_info["pixel_coordinate_transformation_matrix"] = (
320
+ to_python_type(coord_dir_dict["pc"])
321
+ )
322
+
323
+ attrs["coordinate_system_info"] = coordinate_system_info
298
324
  return copy.deepcopy(attrs)
299
325
 
326
+ # dir_dict = {}
327
+
328
+ # dir_dict["reference"] = make_skycoord_dict(
329
+ # data=[0.0, 0.0], units="rad", frame=ap_system
330
+ # )
331
+ # if ap_equinox:
332
+ # dir_dict["reference"]["attrs"]["equinox"] = ap_equinox
333
+ # for i in range(2):
334
+ # unit = u.Unit(_get_unit(coord_dir_dict["units"][i]))
335
+ # q = coord_dir_dict["crval"][i] * unit
336
+ # x = q.to("rad")
337
+ # dir_dict["reference"]["data"][i] = float(x.value)
338
+ # k = "latpole"
339
+ # if k in coord_dir_dict:
340
+ # for j in (k, "lonpole"):
341
+ # m = "longpole" if j == "lonpole" else j
342
+ # dir_dict[j] = make_quantity(
343
+ # value=coord_dir_dict[m] * _deg_to_rad, units="rad", dims=["l", "m"]
344
+ # )
345
+ # for j in ("pc", "projection_parameters", "projection"):
346
+ # if j in coord_dir_dict:
347
+ # dir_dict[j] = coord_dir_dict[j]
348
+ # attrs["direction"] = dir_dict
349
+ # return copy.deepcopy(attrs)
350
+
300
351
 
301
352
  def _casa_image_to_xds_coords(
302
353
  img_full_path: str, verbose: bool, do_sky_coords: bool
@@ -330,14 +381,12 @@ def _casa_image_to_xds_coords(
330
381
  attrs["sphr_dims"] = sphr_dims
331
382
  coords = {}
332
383
  coord_attrs = {}
333
- (coords["time"], coord_attrs["time"]) = _get_time_values_attrs(coord_dict)
334
- (coords["frequency"], coord_attrs["frequency"]) = _get_freq_values_attrs(
335
- csys, shape
336
- )
337
- (velocity_vals, coord_attrs["velocity"]) = _get_velocity_values_attrs(
384
+ coords["time"], coord_attrs["time"] = _get_time_values_attrs(coord_dict)
385
+ coords["frequency"], coord_attrs["frequency"] = _get_freq_values_attrs(csys, shape)
386
+ velocity_vals, coord_attrs["velocity"] = _get_velocity_values_attrs(
338
387
  coord_dict, coords["frequency"]
339
388
  )
340
- (coords["polarization"], coord_attrs["polarization"]) = _get_pol_values_attrs(
389
+ coords["polarization"], coord_attrs["polarization"] = _get_pol_values_attrs(
341
390
  coord_dict
342
391
  )
343
392
  coords["velocity"] = (["frequency"], velocity_vals)
@@ -380,7 +429,7 @@ def _casa_image_to_xds_coords(
380
429
  ret = _get_uv_values_attrs(coord_dict, axis_names, shape)
381
430
  for z in ["u", "v"]:
382
431
  coords[z], coord_attrs[z] = ret[z]
383
- coords["beam_param"] = ["major", "minor", "pa"]
432
+ coords["beam_params_label"] = ["major", "minor", "pa"]
384
433
  attrs["shape"] = shape
385
434
  xds = xr.Dataset(coords=coords)
386
435
  for c in coord_attrs.keys():
@@ -390,7 +439,7 @@ def _casa_image_to_xds_coords(
390
439
 
391
440
 
392
441
  def _convert_direction_system(
393
- casa_system: str, which: str, verbose: bool = True
442
+ casa_system: str, which: str, verbose: bool = False
394
443
  ) -> tuple:
395
444
  if casa_system == "J2000":
396
445
  if verbose:
@@ -634,6 +683,7 @@ def _get_persistent_block(
634
683
  block = _read_image_chunk(infile, shapes, starts)
635
684
  block = np.expand_dims(block, new_axes)
636
685
  block = block.transpose(transpose_list)
686
+ block = da.from_array(block, chunks=block.shape)
637
687
  block = xr.DataArray(block, dims=dimorder)
638
688
  return block
639
689
 
@@ -686,11 +736,11 @@ def _get_starts_shapes_slices(
686
736
  def _get_time_values_attrs(cimage_coord_dict: dict) -> Tuple[List[float], dict]:
687
737
  attrs = {}
688
738
  attrs["type"] = "time"
689
- attrs["scale"] = cimage_coord_dict["obsdate"]["refer"]
739
+ attrs["scale"] = cimage_coord_dict["obsdate"]["refer"].lower()
690
740
  unit = cimage_coord_dict["obsdate"]["m0"]["unit"]
691
741
  attrs["units"] = unit
692
742
  time_val = cimage_coord_dict["obsdate"]["m0"]["value"]
693
- attrs["format"] = _get_time_format(time_val, unit)
743
+ attrs["format"] = _get_time_format(time_val, unit).lower()
694
744
  return ([time_val], copy.deepcopy(attrs))
695
745
 
696
746
 
@@ -829,7 +879,11 @@ def _get_velocity_values_attrs(
829
879
 
830
880
 
831
881
  def _get_beam(
832
- img_full_path: str, nchan: int, npol: int, as_dask_array: bool
882
+ img_full_path: str,
883
+ nchan: int,
884
+ npol: int,
885
+ as_dask_array: bool,
886
+ image_type: str = "SKY",
833
887
  ) -> Union[xr.DataArray, None]:
834
888
  # the image may have multiple beams
835
889
  with _open_image_ro(img_full_path) as casa_image:
@@ -862,10 +916,10 @@ def _get_beam(
862
916
  if as_dask_array:
863
917
  beam_array = da.array(beam_array)
864
918
  xdb = xr.DataArray(
865
- beam_array, dims=["time", "frequency", "polarization", "beam_param"]
919
+ beam_array, dims=["time", "frequency", "polarization", "beam_params_label"]
866
920
  )
867
- xdb = xdb.rename("BEAM")
868
- xdb = xdb.assign_coords(beam_param=["major", "minor", "pa"])
921
+ xdb = xdb.rename("BEAM_FIT_PARAMS_" + image_type.upper())
922
+ xdb = xdb.assign_coords(beam_params_label=["major", "minor", "pa"])
869
923
  xdb.attrs["units"] = "rad"
870
924
  return xdb
871
925
 
@@ -1054,7 +1108,7 @@ def _read_image_array(
1054
1108
  indicating the length of a chunk on that particular axis. If
1055
1109
  a key is missing, the associated chunk length along that axis
1056
1110
  is 1. 'l' represents the longitude like dimension, and 'm'
1057
- represents the latitude like dimension. For apeature images,
1111
+ represents the latitude like dimension. For aperture images,
1058
1112
  'u' may be used in place of 'l', and 'v' in place of 'm'.
1059
1113
  :type chunks: list | dict, required
1060
1114
  :param mask: If specified, this is the associated image mask to read, rather than the actual