xradio 0.0.48__py3-none-any.whl → 0.0.50__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 (32) hide show
  1. xradio/__init__.py +1 -0
  2. xradio/_utils/dict_helpers.py +69 -2
  3. xradio/image/_util/__init__.py +0 -3
  4. xradio/image/_util/_casacore/common.py +0 -13
  5. xradio/image/_util/_casacore/xds_from_casacore.py +102 -97
  6. xradio/image/_util/_casacore/xds_to_casacore.py +36 -24
  7. xradio/image/_util/_fits/xds_from_fits.py +81 -36
  8. xradio/image/_util/_zarr/zarr_low_level.py +3 -3
  9. xradio/image/_util/casacore.py +7 -5
  10. xradio/image/_util/common.py +13 -26
  11. xradio/image/_util/image_factory.py +143 -191
  12. xradio/image/image.py +10 -59
  13. xradio/measurement_set/__init__.py +11 -6
  14. xradio/measurement_set/_utils/_msv2/_tables/read.py +187 -46
  15. xradio/measurement_set/_utils/_msv2/_tables/table_query.py +22 -0
  16. xradio/measurement_set/_utils/_msv2/conversion.py +352 -318
  17. xradio/measurement_set/_utils/_msv2/msv4_info_dicts.py +20 -17
  18. xradio/measurement_set/convert_msv2_to_processing_set.py +46 -6
  19. xradio/measurement_set/load_processing_set.py +100 -53
  20. xradio/measurement_set/measurement_set_xdt.py +319 -0
  21. xradio/measurement_set/open_processing_set.py +122 -86
  22. xradio/measurement_set/processing_set_xdt.py +1552 -0
  23. xradio/measurement_set/schema.py +201 -94
  24. xradio/schema/bases.py +5 -1
  25. xradio/schema/check.py +97 -5
  26. {xradio-0.0.48.dist-info → xradio-0.0.50.dist-info}/METADATA +5 -4
  27. {xradio-0.0.48.dist-info → xradio-0.0.50.dist-info}/RECORD +30 -30
  28. {xradio-0.0.48.dist-info → xradio-0.0.50.dist-info}/WHEEL +1 -1
  29. xradio/measurement_set/measurement_set_xds.py +0 -117
  30. xradio/measurement_set/processing_set.py +0 -803
  31. {xradio-0.0.48.dist-info → xradio-0.0.50.dist-info/licenses}/LICENSE.txt +0 -0
  32. {xradio-0.0.48.dist-info → xradio-0.0.50.dist-info}/top_level.txt +0 -0
@@ -16,7 +16,11 @@ from ..common import (
16
16
  _l_m_attr_notes,
17
17
  )
18
18
  from xradio._utils.coord_math import _deg_to_rad
19
- from xradio._utils.dict_helpers import make_quantity
19
+ from xradio._utils.dict_helpers import (
20
+ make_quantity,
21
+ make_frequency_reference_dict,
22
+ make_skycoord_dict,
23
+ )
20
24
  import copy
21
25
  import dask
22
26
  import dask.array as da
@@ -50,6 +54,8 @@ def _fits_image_to_xds(
50
54
  xds = _add_coord_attrs(xds, helpers)
51
55
  if helpers["has_multibeam"]:
52
56
  xds = _do_multibeam(xds, img_full_path)
57
+ elif "beam" in helpers and helpers["beam"] is not None:
58
+ xds = _add_beam(xds, helpers)
53
59
  return xds
54
60
 
55
61
 
@@ -66,6 +72,7 @@ def _add_time_attrs(xds: xr.Dataset, helpers: dict) -> xr.Dataset:
66
72
  time_coord = xds.coords["time"]
67
73
  meta = copy.deepcopy(helpers["obsdate"])
68
74
  del meta["value"]
75
+ # meta["units"] = [ meta["units"] ]
69
76
  # meta['format'] = 'MJD'
70
77
  # meta['time_scale'] = meta['refer']
71
78
  # del meta['refer']
@@ -79,13 +86,16 @@ def _add_freq_attrs(xds: xr.Dataset, helpers: dict) -> xr.Dataset:
79
86
  meta = {}
80
87
  if helpers["has_freq"]:
81
88
  meta["rest_frequency"] = make_quantity(helpers["restfreq"], "Hz")
82
- meta["frame"] = helpers["specsys"]
83
- meta["units"] = "Hz"
89
+ meta["rest_frequencies"] = [meta["rest_frequency"]]
90
+ # meta["frame"] = helpers["specsys"]
91
+ # meta["units"] = "Hz"
84
92
  meta["type"] = "frequency"
85
93
  meta["wave_unit"] = "mm"
86
94
  freq_axis = helpers["freq_axis"]
87
- meta["crval"] = helpers["crval"][freq_axis]
88
- meta["cdelt"] = helpers["cdelt"][freq_axis]
95
+ meta["reference_value"] = make_frequency_reference_dict(
96
+ helpers["crval"][freq_axis], ["Hz"], helpers["specsys"]
97
+ )
98
+ # meta["cdelt"] = helpers["cdelt"][freq_axis]
89
99
  if not meta:
90
100
  # this is the default frequency information CASA creates
91
101
  meta = _default_freq_info()
@@ -96,7 +106,7 @@ def _add_freq_attrs(xds: xr.Dataset, helpers: dict) -> xr.Dataset:
96
106
 
97
107
  def _add_vel_attrs(xds: xr.Dataset, helpers: dict) -> xr.Dataset:
98
108
  vel_coord = xds.coords["velocity"]
99
- meta = {"units": "m/s"}
109
+ meta = {"units": ["m/s"]}
100
110
  if helpers["has_freq"]:
101
111
  meta["doppler_type"] = helpers.get("doppler", "RADIO")
102
112
  else:
@@ -112,10 +122,6 @@ def _add_l_m_attrs(xds: xr.Dataset, helpers: dict) -> xr.Dataset:
112
122
  for c in ["l", "m"]:
113
123
  if c in xds.coords:
114
124
  xds[c].attrs = {
115
- "crval": 0.0,
116
- "cdelt": helpers[c]["cdelt"],
117
- "units": "rad",
118
- "type": "quantity",
119
125
  "note": attr_note[c],
120
126
  }
121
127
  return xds
@@ -167,6 +173,10 @@ def _xds_direction_attrs_from_header(helpers: dict, header) -> dict:
167
173
  helpers["ref_sys"] = ref_sys
168
174
  helpers["ref_eqx"] = ref_eqx
169
175
  # fits does not support conversion frames
176
+ direction["reference"] = make_skycoord_dict(
177
+ [0.0, 0.0], units=["rad", "rad"], frame=ref_sys
178
+ )
179
+ """
170
180
  direction["reference"] = {
171
181
  "type": "sky_coord",
172
182
  "frame": ref_sys,
@@ -174,20 +184,39 @@ def _xds_direction_attrs_from_header(helpers: dict, header) -> dict:
174
184
  "units": ["rad", "rad"],
175
185
  "value": [0.0, 0.0],
176
186
  }
187
+ """
177
188
  dir_axes = helpers["dir_axes"]
189
+ ddata = []
190
+ dunits = []
178
191
  for i in dir_axes:
179
192
  x = helpers["crval"][i] * u.Unit(_get_unit(helpers["cunit"][i]))
180
193
  x = x.to("rad")
181
- direction["reference"]["value"][i] = x.value
194
+ ddata.append(x.value)
195
+ # direction["reference"]["value"][i] = x.value
182
196
  x = helpers["cdelt"][i] * u.Unit(_get_unit(helpers["cunit"][i]))
183
- x = x.to("rad")
184
- direction["latpole"] = make_quantity(header["LATPOLE"] * _deg_to_rad, "rad")
185
- direction["longpole"] = make_quantity(header["LONPOLE"] * _deg_to_rad, "rad")
197
+ dunits.append(x.to("rad"))
198
+ direction["reference"] = make_skycoord_dict(ddata, units=dunits, frame=ref_sys)
199
+ direction["reference"]["attrs"]["equinox"] = ref_eqx.lower()
200
+ direction["latpole"] = make_quantity(
201
+ header["LATPOLE"] * _deg_to_rad, "rad", dims=["l", "m"]
202
+ )
203
+ direction["lonpole"] = make_quantity(
204
+ header["LONPOLE"] * _deg_to_rad, "rad", dims=["l", "m"]
205
+ )
186
206
  pc = np.zeros([2, 2])
187
207
  for i in (0, 1):
188
208
  for j in (0, 1):
189
209
  # dir_axes are now 0-based, but fits needs 1-based
190
- pc[i][j] = header[f"PC{dir_axes[i]+1}_{dir_axes[j]+1}"]
210
+ try:
211
+ pc[i][j] = header[f"PC{dir_axes[i]+1}_{dir_axes[j]+1}"]
212
+ except KeyError:
213
+ try:
214
+ pc[i][j] = header[f"PC0{dir_axes[i]+1}_0{dir_axes[j]+1}"]
215
+ except KeyError:
216
+ raise RuntimeError(
217
+ f"Could not find PC{dir_axes[i]+1}_{dir_axes[j]+1} or "
218
+ f"PC0{dir_axes[i]+1}_0{dir_axes[j]+1} in FITS header"
219
+ )
191
220
  direction["pc"] = pc
192
221
  # Is there really no fits header parameter for projection_parameters?
193
222
  direction["projection_parameters"] = np.array([0.0, 0.0])
@@ -314,9 +343,9 @@ def _beam_attr_from_header(helpers: dict, header) -> Union[dict, str, None]:
314
343
  if "BMAJ" in header:
315
344
  # single global beam
316
345
  beam = {
317
- "bmaj": make_quantity(header["BMAJ"], "arcsec"),
318
- "bmin": make_quantity(header["BMIN"], "arcsec"),
319
- "pa": make_quantity(header["BPA"], "arcsec"),
346
+ "bmaj": make_quantity(header["BMAJ"], "deg"),
347
+ "bmin": make_quantity(header["BMIN"], "deg"),
348
+ "pa": make_quantity(header["BPA"], "deg"),
320
349
  }
321
350
  return _convert_beam_to_rad(beam)
322
351
  elif "CASAMBM" in header and header["CASAMBM"]:
@@ -390,11 +419,11 @@ def _fits_header_to_xds_attrs(hdulist: fits.hdu.hdulist.HDUList) -> dict:
390
419
  attrs["direction"] = _xds_direction_attrs_from_header(helpers, header)
391
420
  # FIXME read fits data in chunks in case all data too large to hold in memory
392
421
  has_mask = da.any(da.isnan(primary.data)).compute()
393
- attrs["active_mask"] = "mask0" if has_mask else None
422
+ attrs["active_mask"] = "MASK0" if has_mask else None
394
423
  helpers["has_mask"] = has_mask
395
424
  beam = _beam_attr_from_header(helpers, header)
396
425
  if beam != "mb":
397
- attrs["beam"] = beam
426
+ helpers["beam"] = beam
398
427
  if "BITPIX" in header:
399
428
  v = abs(header["BITPIX"])
400
429
  if v == 32:
@@ -409,7 +438,7 @@ def _fits_header_to_xds_attrs(hdulist: fits.hdu.hdulist.HDUList) -> dict:
409
438
  obsdate = {}
410
439
  obsdate["type"] = "time"
411
440
  obsdate["value"] = Time(header["DATE-OBS"], format="isot").mjd
412
- obsdate["units"] = "d"
441
+ obsdate["units"] = ["d"]
413
442
  obsdate["scale"] = header["TIMESYS"]
414
443
  obsdate["format"] = "MJD"
415
444
  attrs["obsdate"] = obsdate
@@ -453,8 +482,8 @@ def _create_coords(
453
482
  helpers["sphr_dims"] = sphr_dims
454
483
  coords = {}
455
484
  coords["time"] = _get_time_values(helpers)
456
- coords["polarization"] = _get_pol_values(helpers)
457
485
  coords["frequency"] = _get_freq_values(helpers)
486
+ coords["polarization"] = _get_pol_values(helpers)
458
487
  coords["velocity"] = (["frequency"], _get_velocity_values(helpers))
459
488
  if len(sphr_dims) > 0:
460
489
  for i, c in enumerate(["l", "m"]):
@@ -497,6 +526,7 @@ def _create_coords(
497
526
  else:
498
527
  # Fourier image
499
528
  coords["u"], coords["v"] = _get_uv_values(helpers)
529
+ coords["beam_param"] = ["major", "minor", "pa"]
500
530
  xds = xr.Dataset(coords=coords)
501
531
  return xds
502
532
 
@@ -615,28 +645,43 @@ def _do_multibeam(xds: xr.Dataset, imname: str) -> xr.Dataset:
615
645
  )
616
646
  nchan = header["NCHAN"]
617
647
  npol = header["NPOL"]
618
- beam_array = np.zeros([1, npol, nchan, 3])
648
+ beam_array = np.zeros([1, nchan, npol, 3])
619
649
  data = hdu.data
650
+ hdulist.close()
620
651
  for t in data:
621
- beam_array[0, t[4], t[3]] = t[0:3]
652
+ beam_array[0, t[3], t[4]] = t[0:3]
622
653
  for i in (0, 1, 2):
623
654
  beam_array[:, :, :, i] = (
624
655
  (beam_array[:, :, :, i] * units[i]).to("rad").value
625
656
  )
626
- xdb = xr.DataArray(
627
- beam_array, dims=["time", "polarization", "frequency", "beam_param"]
628
- )
629
- xdb = xdb.rename("beam")
630
- xdb = xdb.assign_coords(beam_param=["major", "minor", "pa"])
631
- xdb.attrs["units"] = "rad"
632
- xds["beam"] = xdb
633
- return xds
657
+ return _create_beam_data_var(xds, beam_array)
634
658
  raise RuntimeError(
635
659
  "It looks like there should be a BEAMS table but no "
636
660
  "such table found in FITS file"
637
661
  )
638
662
 
639
663
 
664
+ def _add_beam(xds: xr.Dataset, helpers: dict) -> xr.Dataset:
665
+ nchan = xds.sizes["frequency"]
666
+ npol = xds.sizes["polarization"]
667
+ beam_array = np.zeros([1, nchan, npol, 3])
668
+ beam_array[0, :, :, 0] = helpers["beam"]["bmaj"]["data"]
669
+ beam_array[0, :, :, 1] = helpers["beam"]["bmin"]["data"]
670
+ beam_array[0, :, :, 2] = helpers["beam"]["pa"]["data"]
671
+ return _create_beam_data_var(xds, beam_array)
672
+
673
+
674
+ def _create_beam_data_var(xds: xr.Dataset, beam_array: np.array) -> xr.Dataset:
675
+ xdb = xr.DataArray(
676
+ beam_array, dims=["time", "frequency", "polarization", "beam_param"]
677
+ )
678
+ xdb = xdb.rename("BEAM")
679
+ xdb = xdb.assign_coords(beam_param=["major", "minor", "pa"])
680
+ xdb.attrs["units"] = "rad"
681
+ xds["BEAM"] = xdb
682
+ return xds
683
+
684
+
640
685
  def _get_uv_values(helpers: dict) -> tuple:
641
686
  shape = helpers["shape"]
642
687
  ctype = helpers["ctype"]
@@ -680,8 +725,8 @@ def _add_sky_or_aperture(
680
725
  pp = da if type(xda[0].data) == dask.array.core.Array else np
681
726
  mask = pp.isnan(xda)
682
727
  mask.attrs = {}
683
- mask = mask.rename("mask0")
684
- xds["mask0"] = mask
728
+ mask = mask.rename("MASK0")
729
+ xds["MASK0"] = mask
685
730
  return xds
686
731
 
687
732
 
@@ -790,10 +835,10 @@ def _get_transpose_list(helpers: dict) -> tuple:
790
835
  or b.startswith("vopt")
791
836
  or b.startswith("vrad")
792
837
  ):
793
- transpose_list[2] = i
838
+ transpose_list[1] = i
794
839
  not_covered.remove("f")
795
840
  elif b.startswith("stok"):
796
- transpose_list[1] = i
841
+ transpose_list[2] = i
797
842
  not_covered.remove("s")
798
843
  else:
799
844
  raise RuntimeError(f"Unhandled axis name {c}")
@@ -12,9 +12,9 @@ from numcodecs.compat import (
12
12
  ensure_contiguous_ndarray_like,
13
13
  )
14
14
 
15
- full_dims_lm = ["time", "polarization", "frequency", "l", "m"]
16
- full_dims_uv = ["time", "polarization", "frequency", "l", "m"]
17
- norm_dims = ["polarization", "frequency"]
15
+ full_dims_lm = ["time", "frequency", "polarization", "l", "m"]
16
+ full_dims_uv = ["time", "frequency", "polarization", "l", "m"]
17
+ norm_dims = ["frequency", "polarization"]
18
18
 
19
19
  image_data_variables_and_dims_double_precision = {
20
20
  "aperture": {"dims": full_dims_uv, "dtype": "<c16", "name": "APERTURE"},
@@ -55,15 +55,16 @@ def _load_casa_image_block(infile: str, block_des: dict, do_sky_coords) -> xr.Da
55
55
  block = _get_persistent_block(
56
56
  full_path, shapes, starts, dimorder, transpose_list, new_axes
57
57
  )
58
- xds = _add_mask(xds, m, block, dimorder)
58
+ # data vars are all caps by convention
59
+ xds = _add_mask(xds, m.upper(), block, dimorder)
59
60
  xds.attrs = _casa_image_to_xds_attrs(image_full_path, True)
60
61
  mb = _multibeam_array(xds, image_full_path, False)
61
62
  if mb is not None:
62
63
  selectors = {}
63
- for k in ("time", "polarization", "frequency"):
64
+ for k in ("time", "frequency", "polarization"):
64
65
  if k in block_des:
65
66
  selectors[k] = block_des[k]
66
- xds["beam"] = mb.isel(selectors)
67
+ xds["BEAM"] = mb.isel(selectors)
67
68
  return xds
68
69
 
69
70
 
@@ -90,11 +91,12 @@ def _read_casa_image(
90
91
  mymasks = _get_mask_names(img_full_path)
91
92
  for m in mymasks:
92
93
  ary = _read_image_array(img_full_path, chunks, mask=m, verbose=verbose)
93
- xds = _add_mask(xds, m, ary, dimorder)
94
+ # data var names are all caps by convention
95
+ xds = _add_mask(xds, m.upper(), ary, dimorder)
94
96
  xds.attrs = _casa_image_to_xds_attrs(img_full_path, history)
95
97
  mb = _multibeam_array(xds, img_full_path, True)
96
98
  if mb is not None:
97
- xds["beam"] = mb
99
+ xds["BEAM"] = mb
98
100
  # xds = _add_coord_attrs(xds, ret["icoords"], ret["dir_axes"])
99
101
  xds = _dask_arrayize_dv(xds)
100
102
  return xds
@@ -10,7 +10,13 @@ from xradio._utils.dict_helpers import make_quantity
10
10
 
11
11
  _c = 2.99792458e08 * u.m / u.s
12
12
  # OPTICAL = Z
13
- _doppler_types = ["RADIO", "Z", "RATIO", "BETA", "GAMMA"]
13
+ _doppler_types = [
14
+ "radio",
15
+ "z",
16
+ "ratio",
17
+ "beta",
18
+ "gamma",
19
+ ]
14
20
  _image_type = "image_type"
15
21
 
16
22
 
@@ -19,7 +25,7 @@ def _aperture_or_sky(xds: xr.Dataset) -> str:
19
25
 
20
26
 
21
27
  def _get_xds_dim_order(has_sph: bool) -> list:
22
- dimorder = ["time", "polarization", "frequency"]
28
+ dimorder = ["time", "frequency", "polarization"]
23
29
  dir_lin = ["l", "m"] if has_sph else ["u", "v"]
24
30
  dimorder.extend(dir_lin)
25
31
  return dimorder
@@ -34,10 +40,10 @@ def _convert_beam_to_rad(beam: dict) -> dict:
34
40
  """
35
41
  mybeam = {}
36
42
  for k in beam:
37
- try:
38
- q = u.quantity.Quantity(f"{beam[k]['value']}{beam[k]['unit']}")
39
- except:
40
- q = u.quantity.Quantity(f"{beam[k]['value']}{beam[k]['units']}")
43
+ myu = beam[k]["attrs"]["units"]
44
+ myu = myu[0] if isinstance(myu, list) else myu
45
+ units = _get_unit(myu)
46
+ q = u.quantity.Quantity(f"{beam[k]['data']}{units}")
41
47
  q = q.to("rad")
42
48
  j = "pa" if k == "positionangle" else k
43
49
  mybeam[j] = make_quantity(q.value, "rad")
@@ -105,7 +111,7 @@ def _default_freq_info() -> dict:
105
111
  return {
106
112
  "rest_frequency": make_quantity(1420405751.7860003, "Hz"),
107
113
  "type": "frequency",
108
- "frame": "LSRK",
114
+ "frame": "lsrk",
109
115
  "units": "Hz",
110
116
  "waveUnit": "mm",
111
117
  "cdelt": 1000.0,
@@ -253,22 +259,3 @@ def _l_m_attr_notes() -> Dict[str, str]:
253
259
  "So m = y*cdelt, where y is the number of pixels from the phase center. "
254
260
  "See AIPS Memo #27, Section III.",
255
261
  }
256
-
257
-
258
- def _set_multibeam_array(xds, beam_ary, units):
259
- if "beam" in xds.attrs:
260
- if xds.attrs["beam"] is None:
261
- del xds.attrs["beam"]
262
- else:
263
- raise RuntimeError(
264
- "Error: xds has beam attr. It must be removed "
265
- "before multiple beams can be added"
266
- )
267
- xdb = xr.DataArray(
268
- beam_ary, dims=["time", "polarization", "frequency", "beam_param"]
269
- )
270
- xdb = xdb.rename("beam")
271
- xdb = xdb.assign_coords(beam_param=["major", "minor", "pa"])
272
- xdb.attrs["units"] = units
273
- xds["beam"] = xdb
274
- return xds