xradio 0.0.40__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 (65) 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/measurement_set/__init__.py +18 -0
  11. xradio/measurement_set/_utils/__init__.py +5 -0
  12. xradio/{vis/_vis_utils/_ms → measurement_set/_utils/_msv2}/_tables/load_main_table.py +1 -1
  13. xradio/{vis/_vis_utils/_ms → measurement_set/_utils/_msv2}/_tables/read.py +15 -1
  14. xradio/{vis/_vis_utils/_ms → measurement_set/_utils/_msv2}/conversion.py +186 -84
  15. xradio/measurement_set/_utils/_msv2/create_antenna_xds.py +535 -0
  16. xradio/{vis/_vis_utils/_ms → measurement_set/_utils/_msv2}/create_field_and_source_xds.py +146 -58
  17. xradio/measurement_set/_utils/_msv2/msv4_info_dicts.py +203 -0
  18. xradio/measurement_set/_utils/_msv2/msv4_sub_xdss.py +550 -0
  19. xradio/{vis/_vis_utils/_ms → measurement_set/_utils/_msv2}/subtables.py +1 -1
  20. xradio/{vis/_vis_utils → measurement_set/_utils}/_utils/xds_helper.py +1 -1
  21. xradio/{vis/_vis_utils/ms.py → measurement_set/_utils/msv2.py} +4 -4
  22. xradio/{vis/_vis_utils → measurement_set/_utils}/zarr.py +3 -3
  23. xradio/{vis → measurement_set}/convert_msv2_to_processing_set.py +9 -2
  24. xradio/{vis → measurement_set}/load_processing_set.py +16 -20
  25. xradio/measurement_set/measurement_set_xds.py +83 -0
  26. xradio/{vis/read_processing_set.py → measurement_set/open_processing_set.py} +25 -34
  27. xradio/measurement_set/processing_set.py +777 -0
  28. xradio/measurement_set/schema.py +1979 -0
  29. xradio/schema/check.py +42 -22
  30. xradio/schema/dataclass.py +56 -6
  31. xradio/sphinx/__init__.py +12 -0
  32. xradio/sphinx/schema_table.py +351 -0
  33. {xradio-0.0.40.dist-info → xradio-0.0.42.dist-info}/METADATA +17 -15
  34. xradio-0.0.42.dist-info/RECORD +76 -0
  35. {xradio-0.0.40.dist-info → xradio-0.0.42.dist-info}/WHEEL +1 -1
  36. xradio/_utils/common.py +0 -101
  37. xradio/vis/__init__.py +0 -14
  38. xradio/vis/_processing_set.py +0 -302
  39. xradio/vis/_vis_utils/__init__.py +0 -5
  40. xradio/vis/_vis_utils/_ms/create_antenna_xds.py +0 -482
  41. xradio/vis/_vis_utils/_ms/msv4_infos.py +0 -0
  42. xradio/vis/_vis_utils/_ms/msv4_sub_xdss.py +0 -306
  43. xradio/vis/schema.py +0 -1102
  44. xradio-0.0.40.dist-info/RECORD +0 -73
  45. /xradio/{vis/_vis_utils/_ms → measurement_set/_utils/_msv2}/_tables/load.py +0 -0
  46. /xradio/{vis/_vis_utils/_ms → measurement_set/_utils/_msv2}/_tables/read_main_table.py +0 -0
  47. /xradio/{vis/_vis_utils/_ms → measurement_set/_utils/_msv2}/_tables/read_subtables.py +0 -0
  48. /xradio/{vis/_vis_utils/_ms → measurement_set/_utils/_msv2}/_tables/table_query.py +0 -0
  49. /xradio/{vis/_vis_utils/_ms → measurement_set/_utils/_msv2}/_tables/write.py +0 -0
  50. /xradio/{vis/_vis_utils/_ms → measurement_set/_utils/_msv2}/_tables/write_exp_api.py +0 -0
  51. /xradio/{vis/_vis_utils/_ms → measurement_set/_utils/_msv2}/chunks.py +0 -0
  52. /xradio/{vis/_vis_utils/_ms → measurement_set/_utils/_msv2}/descr.py +0 -0
  53. /xradio/{vis/_vis_utils/_ms → measurement_set/_utils/_msv2}/msv2_msv3.py +0 -0
  54. /xradio/{vis/_vis_utils/_ms → measurement_set/_utils/_msv2}/msv2_to_msv4_meta.py +0 -0
  55. /xradio/{vis/_vis_utils/_ms → measurement_set/_utils/_msv2}/optimised_functions.py +0 -0
  56. /xradio/{vis/_vis_utils/_ms → measurement_set/_utils/_msv2}/partition_queries.py +0 -0
  57. /xradio/{vis/_vis_utils/_ms → measurement_set/_utils/_msv2}/partitions.py +0 -0
  58. /xradio/{vis/_vis_utils → measurement_set/_utils}/_utils/cds.py +0 -0
  59. /xradio/{vis/_vis_utils → measurement_set/_utils}/_utils/partition_attrs.py +0 -0
  60. /xradio/{vis/_vis_utils → measurement_set/_utils}/_utils/stokes_types.py +0 -0
  61. /xradio/{vis/_vis_utils → measurement_set/_utils}/_zarr/encoding.py +0 -0
  62. /xradio/{vis/_vis_utils → measurement_set/_utils}/_zarr/read.py +0 -0
  63. /xradio/{vis/_vis_utils → measurement_set/_utils}/_zarr/write.py +0 -0
  64. {xradio-0.0.40.dist-info → xradio-0.0.42.dist-info}/LICENSE.txt +0 -0
  65. {xradio-0.0.40.dist-info → xradio-0.0.42.dist-info}/top_level.txt +0 -0
@@ -1,21 +1,28 @@
1
+ import datetime
2
+ import importlib
1
3
  import numcodecs
2
- import time
3
- from .._zarr.encoding import add_encoding
4
- from typing import Dict, Union
5
- import toolviper.utils.logger as logger
6
4
  import os
7
5
  import pathlib
6
+ import time
7
+ from typing import Dict, Union
8
8
 
9
9
  import numpy as np
10
10
  import xarray as xr
11
11
 
12
+ import toolviper.utils.logger as logger
12
13
  from casacore import tables
13
- from xradio.vis._vis_utils._ms.msv4_sub_xdss import (
14
+ from xradio.measurement_set._utils._msv2.msv4_sub_xdss import (
14
15
  create_pointing_xds,
16
+ create_system_calibration_xds,
15
17
  create_weather_xds,
16
18
  )
17
- from xradio.vis._vis_utils._ms.create_antenna_xds import create_antenna_xds
18
- from xradio.vis._vis_utils._ms.create_field_and_source_xds import (
19
+ from .msv4_info_dicts import create_info_dicts
20
+ from xradio.measurement_set._utils._msv2.create_antenna_xds import (
21
+ create_antenna_xds,
22
+ create_gain_curve_xds,
23
+ create_phase_calibration_xds,
24
+ )
25
+ from xradio.measurement_set._utils._msv2.create_field_and_source_xds import (
19
26
  create_field_and_source_xds,
20
27
  )
21
28
  from xradio._utils.schema import column_description_casacore_to_msv4_measure
@@ -25,6 +32,7 @@ from .msv2_to_msv4_meta import (
25
32
  col_dims,
26
33
  )
27
34
 
35
+ from .._zarr.encoding import add_encoding
28
36
  from .subtables import subt_rename_ids
29
37
  from ._tables.table_query import open_table_ro, open_query
30
38
  from ._tables.read import (
@@ -142,7 +150,7 @@ def mem_chunksize_to_dict_main(chunksize: float, xds: xr.Dataset) -> Dict[str, i
142
150
  polarization).
143
151
  """
144
152
 
145
- sizeof_vis = itemsize_vis_spec(xds)
153
+ sizeof_vis = itemsize_spec(xds)
146
154
  size_all_pols = sizeof_vis * xds.sizes["polarization"]
147
155
  if size_all_pols / GiBYTES_TO_BYTES > chunksize:
148
156
  raise RuntimeError(
@@ -324,7 +332,7 @@ def find_baseline_or_antenna_var(xds: xr.Dataset) -> str:
324
332
  return baseline_or_antenna_name
325
333
 
326
334
 
327
- def itemsize_vis_spec(xds: xr.Dataset) -> int:
335
+ def itemsize_spec(xds: xr.Dataset) -> int:
328
336
  """
329
337
  Size in bytes of one visibility (or spectrum) value.
330
338
  """
@@ -414,9 +422,9 @@ def create_coordinates(
414
422
  "time": utime,
415
423
  "baseline_antenna1_id": ("baseline_id", baseline_ant1_id),
416
424
  "baseline_antenna2_id": ("baseline_id", baseline_ant2_id),
417
- "uvw_label": ["u", "v", "w"],
418
425
  "baseline_id": np.arange(len(baseline_ant1_id)),
419
426
  "scan_number": ("time", scan_id),
427
+ "uvw_label": ["u", "v", "w"],
420
428
  }
421
429
 
422
430
  ddi_xds = load_generic_table(in_file, "DATA_DESCRIPTION").sel(row=ddi)
@@ -474,7 +482,6 @@ def create_coordinates(
474
482
  }
475
483
  xds.frequency.attrs["spectral_window_id"] = spectral_window_id
476
484
 
477
- # xds.frequency.attrs["effective_channel_width"] = "EFFECTIVE_CHANNEL_WIDTH"
478
485
  # Add if doppler table is present
479
486
  # xds.frequency.attrs["doppler_velocity"] =
480
487
  # xds.frequency.attrs["doppler_type"] =
@@ -597,8 +604,8 @@ def create_data_variables(
597
604
  logger.debug(
598
605
  "Time to read column " + str(col) + " : " + str(time.time() - start)
599
606
  )
600
- except Exception as e:
601
- logger.debug("Could not load column", col)
607
+ except Exception as exc:
608
+ logger.debug(f"Could not load column {col}, exception: {exc}")
602
609
 
603
610
  if ("WEIGHT_SPECTRUM" == col) and (
604
611
  "WEIGHT" in col_names
@@ -616,13 +623,27 @@ def create_data_variables(
616
623
 
617
624
 
618
625
  def add_missing_data_var_attrs(xds):
619
- """Adds in attributes expected metadata that cannot be found
620
- in the input MSv2. For now specifically for missing
621
- single-dish/SPECTRUM metadata"""
626
+ """
627
+ Adds in the xds attributes expected metadata that cannot be found in the input MSv2.
628
+ For now:
629
+ - missing single-dish/SPECTRUM metadata
630
+ - missing interferometry/VISIBILITY_MODEL metadata
631
+ """
622
632
  data_var_names = ["SPECTRUM", "SPECTRUM_CORRECTED"]
623
633
  for var_name in data_var_names:
624
634
  if var_name in xds.data_vars:
625
- xds.data_vars[var_name].attrs["units"] = ["Jy"]
635
+ xds.data_vars[var_name].attrs["units"] = [""]
636
+
637
+ vis_var_names = ["VISIBILITY_MODEL"]
638
+ for var_name in vis_var_names:
639
+ if var_name in xds.data_vars and "units" not in xds.data_vars[var_name].attrs:
640
+ # Assume MODEL uses the same units
641
+ if "VISIBILITY" in xds.data_vars:
642
+ xds.data_vars[var_name].attrs["units"] = xds.data_vars[
643
+ "VISIBILITY"
644
+ ].attrs["units"]
645
+ else:
646
+ xds.data_vars[var_name].attrs["units"] = [""]
626
647
 
627
648
  return xds
628
649
 
@@ -680,6 +701,28 @@ def create_taql_query(partition_info):
680
701
  return taql_where
681
702
 
682
703
 
704
+ def fix_uvw_frame(
705
+ xds: xr.Dataset, field_and_source_xds: xr.Dataset, is_single_dish: bool
706
+ ) -> xr.Dataset:
707
+ """
708
+ Fix UVW frame
709
+
710
+ From CASA fixvis docs: clean and the im tool ignore the reference frame claimed by the UVW column (it is often
711
+ mislabelled as ITRF when it is really FK5 (J2000)) and instead assume the (u, v, w)s are in the same frame as the phase
712
+ tracking center. calcuvw does not yet force the UVW column and field centers to use the same reference frame!
713
+ Blank = use the phase tracking frame of vis.
714
+ """
715
+ if xds.UVW.attrs["frame"] == "ITRF":
716
+ if is_single_dish:
717
+ center_var = "FIELD_REFERENCE_CENTER"
718
+ else:
719
+ center_var = "FIELD_PHASE_CENTER"
720
+
721
+ xds.UVW.attrs["frame"] = field_and_source_xds[center_var].attrs["frame"]
722
+
723
+ return xds
724
+
725
+
683
726
  def convert_and_write_partition(
684
727
  in_file: str,
685
728
  out_file: str,
@@ -693,6 +736,7 @@ def convert_and_write_partition(
693
736
  pointing_interpolate: bool = False,
694
737
  ephemeris_interpolate: bool = False,
695
738
  phase_cal_interpolate: bool = False,
739
+ sys_cal_interpolate: bool = False,
696
740
  compressor: numcodecs.abc.Codec = numcodecs.Zstd(level=2),
697
741
  storage_backend="zarr",
698
742
  overwrite: bool = False,
@@ -705,7 +749,7 @@ def convert_and_write_partition(
705
749
  _description_
706
750
  out_file : str
707
751
  _description_
708
- obs_mode : str
752
+ intents : str
709
753
  _description_
710
754
  ddi : int, optional
711
755
  _description_, by default 0
@@ -723,6 +767,10 @@ def convert_and_write_partition(
723
767
  _description_, by default None
724
768
  ephemeris_interpolate : bool, optional
725
769
  _description_, by default None
770
+ phase_cal_interpolate : bool, optional
771
+ _description_, by default None
772
+ sys_cal_interpolate : bool, optional
773
+ _description_, by default None
726
774
  compressor : numcodecs.abc.Codec, optional
727
775
  _description_, by default numcodecs.Zstd(level=2)
728
776
  storage_backend : str, optional
@@ -738,7 +786,7 @@ def convert_and_write_partition(
738
786
 
739
787
  taql_where = create_taql_query(partition_info)
740
788
  ddi = partition_info["DATA_DESC_ID"][0]
741
- obs_mode = str(partition_info["OBS_MODE"][0])
789
+ intents = str(partition_info["OBS_MODE"][0])
742
790
 
743
791
  start = time.time()
744
792
  with open_table_ro(in_file) as mtable:
@@ -766,24 +814,32 @@ def convert_and_write_partition(
766
814
  tb_tool.getcol("OBSERVATION_ID"), "OBSERVATION_ID"
767
815
  )
768
816
 
769
- def get_observation_info(in_file, observation_id, obs_mode):
817
+ def get_observation_info(in_file, observation_id, intents):
770
818
  generic_observation_xds = load_generic_table(
771
819
  in_file,
772
820
  "OBSERVATION",
773
821
  taql_where=f" where (ROWID() IN [{str(observation_id)}])",
774
822
  )
775
823
 
776
- if obs_mode == "None":
777
- obs_mode = "obs_" + str(observation_id)
824
+ if intents == "None":
825
+ intents = "obs_" + str(observation_id)
778
826
 
779
- return generic_observation_xds["TELESCOPE_NAME"].values[0], obs_mode
827
+ return generic_observation_xds["TELESCOPE_NAME"].values[0], intents
780
828
 
781
- telescope_name, obs_mode = get_observation_info(
782
- in_file, observation_id, obs_mode
829
+ telescope_name, intents = get_observation_info(
830
+ in_file, observation_id, intents
783
831
  )
784
832
 
785
833
  start = time.time()
786
- xds = xr.Dataset()
834
+ xds = xr.Dataset(
835
+ attrs={
836
+ "creation_date": datetime.datetime.utcnow().isoformat(),
837
+ "xradio_version": importlib.metadata.version("xradio"),
838
+ "schema_version": "4.0.-9994",
839
+ "type": "visibility",
840
+ }
841
+ )
842
+
787
843
  # interval = check_if_consistent(tb_tool.getcol("INTERVAL"), "INTERVAL")
788
844
  interval = tb_tool.getcol("INTERVAL")
789
845
 
@@ -826,7 +882,6 @@ def convert_and_write_partition(
826
882
 
827
883
  # Add data_groups
828
884
  xds, is_single_dish = add_data_groups(xds)
829
-
830
885
  xds = add_missing_data_var_attrs(xds)
831
886
 
832
887
  if (
@@ -874,25 +929,56 @@ def convert_and_write_partition(
874
929
  antenna_id,
875
930
  feed_id,
876
931
  telescope_name,
932
+ xds.polarization,
933
+ )
934
+ logger.debug("Time antenna xds " + str(time.time() - start))
935
+
936
+ start = time.time()
937
+ gain_curve_xds = create_gain_curve_xds(
938
+ in_file, xds.frequency.attrs["spectral_window_id"], ant_xds
939
+ )
940
+ logger.debug("Time gain_curve xds " + str(time.time() - start))
941
+
942
+ start = time.time()
943
+ phase_calibration_xds = create_phase_calibration_xds(
944
+ in_file,
945
+ xds.frequency.attrs["spectral_window_id"],
946
+ ant_xds,
877
947
  time_min_max,
878
948
  phase_cal_interp_time,
879
949
  )
950
+ logger.debug("Time phase_calibration xds " + str(time.time() - start))
880
951
 
881
952
  # Change antenna_ids to antenna_names
882
953
  xds = antenna_ids_to_names(xds, ant_xds, is_single_dish)
954
+ # but before, keep the name-id arrays, we need them for the pointing and weather xds
883
955
  ant_xds_name_ids = ant_xds["antenna_name"].set_xindex("antenna_id")
884
- ant_xds = ant_xds.drop_vars(
885
- "antenna_id"
886
- ) # No longer needed after converting to name.
956
+ ant_xds_station_name_ids = ant_xds["station"].set_xindex("antenna_id")
957
+ # No longer needed after converting to name.
958
+ ant_xds = ant_xds.drop_vars("antenna_id")
887
959
 
888
- logger.debug("Time ant xds " + str(time.time() - start))
960
+ # Create system_calibration_xds
961
+ start = time.time()
962
+ if sys_cal_interpolate:
963
+ sys_cal_interp_time = xds.time.values
964
+ else:
965
+ sys_cal_interp_time = None
966
+ system_calibration_xds = create_system_calibration_xds(
967
+ in_file,
968
+ xds.frequency,
969
+ ant_xds_name_ids,
970
+ sys_cal_interp_time,
971
+ )
972
+ logger.debug("Time system_calibation " + str(time.time() - start))
889
973
 
890
974
  # Create weather_xds
891
975
  start = time.time()
892
- weather_xds = create_weather_xds(in_file)
976
+ weather_xds = create_weather_xds(in_file, ant_xds_station_name_ids)
893
977
  logger.debug("Time weather " + str(time.time() - start))
894
978
 
895
979
  # Create pointing_xds
980
+ pointing_xds = xr.Dataset()
981
+ print("with_pointing", with_pointing)
896
982
  if with_pointing:
897
983
  start = time.time()
898
984
  if pointing_interpolate:
@@ -914,7 +1000,6 @@ def convert_and_write_partition(
914
1000
  )
915
1001
 
916
1002
  start = time.time()
917
- xds.attrs["type"] = "visibility"
918
1003
 
919
1004
  # Time and frequency should always be increasing
920
1005
  if len(xds.frequency) > 1 and xds.frequency[1] - xds.frequency[0] < 0:
@@ -943,7 +1028,7 @@ def convert_and_write_partition(
943
1028
  # assert len(col_unique) == 1, col_name + " is not consistent."
944
1029
  # return col_unique[0]
945
1030
 
946
- field_and_source_xds, source_id, num_lines = create_field_and_source_xds(
1031
+ field_and_source_xds, source_id, _num_lines = create_field_and_source_xds(
947
1032
  in_file,
948
1033
  field_id,
949
1034
  xds.frequency.attrs["spectral_window_id"],
@@ -954,22 +1039,20 @@ def convert_and_write_partition(
954
1039
  )
955
1040
  logger.debug("Time field_and_source_xds " + str(time.time() - start))
956
1041
 
957
- # Fix UVW frame
958
- # From CASA fixvis docs: clean and the im tool ignore the reference frame claimed by the UVW column (it is often mislabelled as ITRF when it is really FK5 (J2000)) and instead assume the (u, v, w)s are in the same frame as the phase tracking center. calcuvw does not yet force the UVW column and field centers to use the same reference frame! Blank = use the phase tracking frame of vis.
959
- if is_single_dish:
960
- xds.UVW.attrs["frame"] = field_and_source_xds[
961
- "FIELD_REFERENCE_CENTER"
962
- ].attrs["frame"]
963
- else:
964
- xds.UVW.attrs["frame"] = field_and_source_xds[
965
- "FIELD_PHASE_CENTER"
966
- ].attrs["frame"]
1042
+ xds = fix_uvw_frame(xds, field_and_source_xds, is_single_dish)
967
1043
 
968
- if overwrite:
969
- mode = "w"
970
- else:
971
- mode = "w-"
1044
+ partition_info_misc_fields = {
1045
+ "scan_id": scan_id,
1046
+ "intents": intents,
1047
+ "taql_where": taql_where,
1048
+ }
1049
+ info_dicts = create_info_dicts(
1050
+ in_file, xds, field_and_source_xds, partition_info_misc_fields, tb_tool
1051
+ )
1052
+ xds.attrs.update(info_dicts)
972
1053
 
1054
+ # xds ready, prepare to write
1055
+ start = time.time()
973
1056
  main_chunksize = parse_chunksize(main_chunksize, "main", xds)
974
1057
  add_encoding(xds, compressor=compressor, chunks=main_chunksize)
975
1058
  logger.debug("Time add compressor and chunk " + str(time.time() - start))
@@ -979,52 +1062,58 @@ def convert_and_write_partition(
979
1062
  pathlib.Path(in_file).name.replace(".ms", "") + "_" + str(ms_v4_id),
980
1063
  )
981
1064
 
982
- if "line_name" in field_and_source_xds.coords:
983
- line_name = to_list(
984
- unique_1d(np.ravel(field_and_source_xds.line_name.values))
985
- )
1065
+ if overwrite:
1066
+ mode = "w"
986
1067
  else:
987
- line_name = []
988
-
989
- xds.attrs["partition_info"] = {
990
- # "spectral_window_id": xds.frequency.attrs["spectral_window_id"],
991
- "spectral_window_name": xds.frequency.attrs["spectral_window_name"],
992
- # "field_id": to_list(unique_1d(field_id)),
993
- "field_name": to_list(
994
- np.unique(field_and_source_xds.field_name.values)
995
- ),
996
- # "source_id": to_list(unique_1d(source_id)),
997
- "line_name": line_name,
998
- "scan_number": to_list(np.unique(scan_id)),
999
- "source_name": to_list(
1000
- np.unique(field_and_source_xds.source_name.values)
1001
- ),
1002
- "polarization_setup": to_list(xds.polarization.values),
1003
- "num_lines": num_lines,
1004
- "obs_mode": obs_mode.split(","),
1005
- "taql": taql_where,
1006
- }
1068
+ mode = "w-"
1069
+
1070
+ if is_single_dish:
1071
+ xds.attrs["type"] = "spectrum"
1072
+ xds = xds.drop_vars(["UVW"])
1073
+ del xds["uvw_label"]
1074
+ else:
1075
+ if any("WVR" in s for s in intents):
1076
+ xds.attrs["type"] = "wvr"
1077
+ else:
1078
+ xds.attrs["type"] = "visibility"
1007
1079
 
1008
1080
  start = time.time()
1009
1081
  if storage_backend == "zarr":
1010
- xds.to_zarr(store=os.path.join(file_name, "MAIN"), mode=mode)
1011
- ant_xds.to_zarr(store=os.path.join(file_name, "ANTENNA"), mode=mode)
1082
+ xds.to_zarr(store=os.path.join(file_name, "correlated_xds"), mode=mode)
1083
+ ant_xds.to_zarr(store=os.path.join(file_name, "antenna_xds"), mode=mode)
1012
1084
  for group_name in xds.attrs["data_groups"]:
1013
1085
  field_and_source_xds.to_zarr(
1014
1086
  store=os.path.join(
1015
- file_name, f"FIELD_AND_SOURCE_{group_name.upper()}"
1087
+ file_name, f"field_and_source_xds_{group_name}"
1016
1088
  ),
1017
1089
  mode=mode,
1018
1090
  )
1019
1091
 
1020
- if with_pointing and len(pointing_xds.data_vars) > 1:
1092
+ if with_pointing and len(pointing_xds.data_vars) > 0:
1021
1093
  pointing_xds.to_zarr(
1022
- store=os.path.join(file_name, "POINTING"), mode=mode
1094
+ store=os.path.join(file_name, "pointing_xds"), mode=mode
1095
+ )
1096
+
1097
+ if system_calibration_xds:
1098
+ system_calibration_xds.to_zarr(
1099
+ store=os.path.join(file_name, "system_calibration_xds"),
1100
+ mode=mode,
1101
+ )
1102
+
1103
+ if gain_curve_xds:
1104
+ gain_curve_xds.to_zarr(
1105
+ store=os.path.join(file_name, "gain_curve_xds"), mode=mode
1106
+ )
1107
+
1108
+ if phase_calibration_xds:
1109
+ phase_calibration_xds.to_zarr(
1110
+ store=os.path.join(file_name, "phase_calibration_xds"),
1111
+ mode=mode,
1023
1112
  )
1024
1113
 
1025
1114
  if weather_xds:
1026
1115
  weather_xds.to_zarr(
1027
- store=os.path.join(file_name, "WEATHER"), mode=mode
1116
+ store=os.path.join(file_name, "weather_xds"), mode=mode
1028
1117
  )
1029
1118
 
1030
1119
  elif storage_backend == "netcdf":
@@ -1056,7 +1145,11 @@ def antenna_ids_to_names(
1056
1145
  }
1057
1146
  )
1058
1147
  else:
1059
- xds["baseline_id"] = ant_xds["antenna_name"].sel(antenna_id=xds["baseline_id"])
1148
+ # baseline_antenna1_id will be removed soon below, but it is useful here to know the actual antenna_ids,
1149
+ # as opposed to the baseline_ids which can mismatch when data is missing for some antennas
1150
+ xds["baseline_id"] = ant_xds["antenna_name"].sel(
1151
+ antenna_id=xds["baseline_antenna1_id"]
1152
+ )
1060
1153
  unwanted_coords_from_ant_xds = [
1061
1154
  "antenna_id",
1062
1155
  "antenna_name",
@@ -1080,7 +1173,7 @@ def add_data_groups(xds):
1080
1173
  xds.attrs["data_groups"] = {}
1081
1174
  if "VISIBILITY" in xds:
1082
1175
  xds.attrs["data_groups"]["base"] = {
1083
- "visibility": "VISIBILITY",
1176
+ "correlated_data": "VISIBILITY",
1084
1177
  "flag": "FLAG",
1085
1178
  "weight": "WEIGHT",
1086
1179
  "uvw": "UVW",
@@ -1088,7 +1181,7 @@ def add_data_groups(xds):
1088
1181
 
1089
1182
  if "VISIBILITY_CORRECTED" in xds:
1090
1183
  xds.attrs["data_groups"]["corrected"] = {
1091
- "visibility": "VISIBILITY_CORRECTED",
1184
+ "correlated_data": "VISIBILITY_CORRECTED",
1092
1185
  "flag": "FLAG",
1093
1186
  "weight": "WEIGHT",
1094
1187
  "uvw": "UVW",
@@ -1096,7 +1189,7 @@ def add_data_groups(xds):
1096
1189
 
1097
1190
  if "VISIBILITY_MODEL" in xds:
1098
1191
  xds.attrs["data_groups"]["model"] = {
1099
- "visibility": "VISIBILITY_MODEL",
1192
+ "correlated_data": "VISIBILITY_MODEL",
1100
1193
  "flag": "FLAG",
1101
1194
  "weight": "WEIGHT",
1102
1195
  "uvw": "UVW",
@@ -1105,7 +1198,16 @@ def add_data_groups(xds):
1105
1198
  is_single_dish = False
1106
1199
  if "SPECTRUM" in xds:
1107
1200
  xds.attrs["data_groups"]["base"] = {
1108
- "spectrum": "SPECTRUM",
1201
+ "correlated_data": "SPECTRUM",
1202
+ "flag": "FLAG",
1203
+ "weight": "WEIGHT",
1204
+ "uvw": "UVW",
1205
+ }
1206
+ is_single_dish = True
1207
+
1208
+ if "SPECTRUM_MODEL" in xds:
1209
+ xds.attrs["data_groups"]["model"] = {
1210
+ "correlated_data": "SPECTRUM_MODEL",
1109
1211
  "flag": "FLAG",
1110
1212
  "weight": "WEIGHT",
1111
1213
  "uvw": "UVW",
@@ -1114,7 +1216,7 @@ def add_data_groups(xds):
1114
1216
 
1115
1217
  if "SPECTRUM_CORRECTED" in xds:
1116
1218
  xds.attrs["data_groups"]["corrected"] = {
1117
- "spectrum": "SPECTRUM_CORRECTED",
1219
+ "correlated_data": "SPECTRUM_CORRECTED",
1118
1220
  "flag": "FLAG",
1119
1221
  "weight": "WEIGHT",
1120
1222
  "uvw": "UVW",