xradio 0.0.41__py3-none-any.whl → 0.0.43__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.
- xradio/_utils/coord_math.py +100 -0
- xradio/_utils/list_and_array.py +49 -4
- xradio/_utils/schema.py +36 -16
- xradio/image/_util/_casacore/xds_from_casacore.py +5 -5
- xradio/image/_util/_casacore/xds_to_casacore.py +12 -11
- xradio/image/_util/_fits/xds_from_fits.py +18 -17
- xradio/image/_util/_zarr/zarr_low_level.py +29 -12
- xradio/image/_util/common.py +1 -1
- xradio/image/_util/image_factory.py +1 -1
- xradio/{correlated_data → measurement_set}/__init__.py +7 -4
- xradio/measurement_set/_utils/__init__.py +5 -0
- xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/_tables/load_main_table.py +1 -1
- xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/_tables/read.py +1 -1
- xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/conversion.py +115 -37
- xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/create_antenna_xds.py +62 -37
- xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/create_field_and_source_xds.py +117 -25
- xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/msv4_sub_xdss.py +47 -13
- xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/partition_queries.py +4 -0
- xradio/{correlated_data → measurement_set}/_utils/_utils/xds_helper.py +1 -1
- xradio/{correlated_data/_utils/ms.py → measurement_set/_utils/msv2.py} +4 -4
- xradio/{correlated_data → measurement_set}/convert_msv2_to_processing_set.py +7 -2
- xradio/{correlated_data → measurement_set}/load_processing_set.py +5 -5
- xradio/measurement_set/measurement_set_xds.py +110 -0
- xradio/{correlated_data → measurement_set}/open_processing_set.py +9 -16
- xradio/measurement_set/processing_set.py +777 -0
- xradio/{correlated_data → measurement_set}/schema.py +1110 -586
- xradio/schema/check.py +42 -22
- xradio/schema/dataclass.py +56 -6
- xradio/sphinx/__init__.py +12 -0
- xradio/sphinx/schema_table.py +351 -0
- {xradio-0.0.41.dist-info → xradio-0.0.43.dist-info}/METADATA +9 -6
- xradio-0.0.43.dist-info/RECORD +76 -0
- {xradio-0.0.41.dist-info → xradio-0.0.43.dist-info}/WHEEL +1 -1
- xradio/_utils/common.py +0 -101
- xradio/correlated_data/_utils/__init__.py +0 -5
- xradio/correlated_data/correlated_xds.py +0 -13
- xradio/correlated_data/processing_set.py +0 -301
- xradio/correlated_data/test__processing_set.py +0 -74
- xradio-0.0.41.dist-info/RECORD +0 -75
- /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/_tables/load.py +0 -0
- /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/_tables/read_main_table.py +0 -0
- /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/_tables/read_subtables.py +0 -0
- /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/_tables/table_query.py +0 -0
- /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/_tables/write.py +0 -0
- /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/_tables/write_exp_api.py +0 -0
- /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/chunks.py +0 -0
- /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/descr.py +0 -0
- /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/msv2_msv3.py +0 -0
- /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/msv2_to_msv4_meta.py +0 -0
- /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/msv4_info_dicts.py +0 -0
- /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/optimised_functions.py +0 -0
- /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/partitions.py +0 -0
- /xradio/{correlated_data/_utils/_ms → measurement_set/_utils/_msv2}/subtables.py +0 -0
- /xradio/{correlated_data → measurement_set}/_utils/_utils/cds.py +0 -0
- /xradio/{correlated_data → measurement_set}/_utils/_utils/partition_attrs.py +0 -0
- /xradio/{correlated_data → measurement_set}/_utils/_utils/stokes_types.py +0 -0
- /xradio/{correlated_data → measurement_set}/_utils/_zarr/encoding.py +0 -0
- /xradio/{correlated_data → measurement_set}/_utils/_zarr/read.py +0 -0
- /xradio/{correlated_data → measurement_set}/_utils/_zarr/write.py +0 -0
- /xradio/{correlated_data → measurement_set}/_utils/zarr.py +0 -0
- {xradio-0.0.41.dist-info → xradio-0.0.43.dist-info}/LICENSE.txt +0 -0
- {xradio-0.0.41.dist-info → xradio-0.0.43.dist-info}/top_level.txt +0 -0
|
@@ -11,18 +11,18 @@ import xarray as xr
|
|
|
11
11
|
|
|
12
12
|
import toolviper.utils.logger as logger
|
|
13
13
|
from casacore import tables
|
|
14
|
-
from xradio.
|
|
14
|
+
from xradio.measurement_set._utils._msv2.msv4_sub_xdss import (
|
|
15
15
|
create_pointing_xds,
|
|
16
16
|
create_system_calibration_xds,
|
|
17
17
|
create_weather_xds,
|
|
18
18
|
)
|
|
19
19
|
from .msv4_info_dicts import create_info_dicts
|
|
20
|
-
from xradio.
|
|
20
|
+
from xradio.measurement_set._utils._msv2.create_antenna_xds import (
|
|
21
21
|
create_antenna_xds,
|
|
22
22
|
create_gain_curve_xds,
|
|
23
23
|
create_phase_calibration_xds,
|
|
24
24
|
)
|
|
25
|
-
from xradio.
|
|
25
|
+
from xradio.measurement_set._utils._msv2.create_field_and_source_xds import (
|
|
26
26
|
create_field_and_source_xds,
|
|
27
27
|
)
|
|
28
28
|
from xradio._utils.schema import column_description_casacore_to_msv4_measure
|
|
@@ -422,9 +422,9 @@ def create_coordinates(
|
|
|
422
422
|
"time": utime,
|
|
423
423
|
"baseline_antenna1_id": ("baseline_id", baseline_ant1_id),
|
|
424
424
|
"baseline_antenna2_id": ("baseline_id", baseline_ant2_id),
|
|
425
|
-
"uvw_label": ["u", "v", "w"],
|
|
426
425
|
"baseline_id": np.arange(len(baseline_ant1_id)),
|
|
427
426
|
"scan_number": ("time", scan_id),
|
|
427
|
+
"uvw_label": ["u", "v", "w"],
|
|
428
428
|
}
|
|
429
429
|
|
|
430
430
|
ddi_xds = load_generic_table(in_file, "DATA_DESCRIPTION").sel(row=ddi)
|
|
@@ -482,7 +482,6 @@ def create_coordinates(
|
|
|
482
482
|
}
|
|
483
483
|
xds.frequency.attrs["spectral_window_id"] = spectral_window_id
|
|
484
484
|
|
|
485
|
-
# xds.frequency.attrs["effective_channel_width"] = "EFFECTIVE_CHANNEL_WIDTH"
|
|
486
485
|
# Add if doppler table is present
|
|
487
486
|
# xds.frequency.attrs["doppler_velocity"] =
|
|
488
487
|
# xds.frequency.attrs["doppler_type"] =
|
|
@@ -605,8 +604,8 @@ def create_data_variables(
|
|
|
605
604
|
logger.debug(
|
|
606
605
|
"Time to read column " + str(col) + " : " + str(time.time() - start)
|
|
607
606
|
)
|
|
608
|
-
except Exception as
|
|
609
|
-
logger.debug("Could not load column
|
|
607
|
+
except Exception as exc:
|
|
608
|
+
logger.debug(f"Could not load column {col}, exception: {exc}")
|
|
610
609
|
|
|
611
610
|
if ("WEIGHT_SPECTRUM" == col) and (
|
|
612
611
|
"WEIGHT" in col_names
|
|
@@ -624,13 +623,27 @@ def create_data_variables(
|
|
|
624
623
|
|
|
625
624
|
|
|
626
625
|
def add_missing_data_var_attrs(xds):
|
|
627
|
-
"""
|
|
628
|
-
in the
|
|
629
|
-
|
|
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
|
+
"""
|
|
630
632
|
data_var_names = ["SPECTRUM", "SPECTRUM_CORRECTED"]
|
|
631
633
|
for var_name in data_var_names:
|
|
632
634
|
if var_name in xds.data_vars:
|
|
633
|
-
xds.data_vars[var_name].attrs["units"] = ["
|
|
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"] = [""]
|
|
634
647
|
|
|
635
648
|
return xds
|
|
636
649
|
|
|
@@ -674,6 +687,7 @@ def create_taql_query(partition_info):
|
|
|
674
687
|
"FIELD_ID",
|
|
675
688
|
"SCAN_NUMBER",
|
|
676
689
|
"STATE_ID",
|
|
690
|
+
"ANTENNA1",
|
|
677
691
|
]
|
|
678
692
|
|
|
679
693
|
taql_where = "WHERE "
|
|
@@ -683,11 +697,38 @@ def create_taql_query(partition_info):
|
|
|
683
697
|
taql_where
|
|
684
698
|
+ f"({col_name} IN [{','.join(map(str, partition_info[col_name]))}]) AND"
|
|
685
699
|
)
|
|
700
|
+
if col_name == "ANTENNA1":
|
|
701
|
+
taql_where = (
|
|
702
|
+
taql_where
|
|
703
|
+
+ f"(ANTENNA2 IN [{','.join(map(str, partition_info[col_name]))}]) AND"
|
|
704
|
+
)
|
|
686
705
|
taql_where = taql_where[:-3]
|
|
687
706
|
|
|
688
707
|
return taql_where
|
|
689
708
|
|
|
690
709
|
|
|
710
|
+
def fix_uvw_frame(
|
|
711
|
+
xds: xr.Dataset, field_and_source_xds: xr.Dataset, is_single_dish: bool
|
|
712
|
+
) -> xr.Dataset:
|
|
713
|
+
"""
|
|
714
|
+
Fix UVW frame
|
|
715
|
+
|
|
716
|
+
From CASA fixvis docs: clean and the im tool ignore the reference frame claimed by the UVW column (it is often
|
|
717
|
+
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
|
|
718
|
+
tracking center. calcuvw does not yet force the UVW column and field centers to use the same reference frame!
|
|
719
|
+
Blank = use the phase tracking frame of vis.
|
|
720
|
+
"""
|
|
721
|
+
if xds.UVW.attrs["frame"] == "ITRF":
|
|
722
|
+
if is_single_dish:
|
|
723
|
+
center_var = "FIELD_REFERENCE_CENTER"
|
|
724
|
+
else:
|
|
725
|
+
center_var = "FIELD_PHASE_CENTER"
|
|
726
|
+
|
|
727
|
+
xds.UVW.attrs["frame"] = field_and_source_xds[center_var].attrs["frame"]
|
|
728
|
+
|
|
729
|
+
return xds
|
|
730
|
+
|
|
731
|
+
|
|
691
732
|
def convert_and_write_partition(
|
|
692
733
|
in_file: str,
|
|
693
734
|
out_file: str,
|
|
@@ -798,9 +839,9 @@ def convert_and_write_partition(
|
|
|
798
839
|
start = time.time()
|
|
799
840
|
xds = xr.Dataset(
|
|
800
841
|
attrs={
|
|
801
|
-
"creation_date": datetime.datetime.
|
|
842
|
+
"creation_date": datetime.datetime.utcnow().isoformat(),
|
|
802
843
|
"xradio_version": importlib.metadata.version("xradio"),
|
|
803
|
-
"schema_version": "4.0.-
|
|
844
|
+
"schema_version": "4.0.-9994",
|
|
804
845
|
"type": "visibility",
|
|
805
846
|
}
|
|
806
847
|
)
|
|
@@ -847,7 +888,6 @@ def convert_and_write_partition(
|
|
|
847
888
|
|
|
848
889
|
# Add data_groups
|
|
849
890
|
xds, is_single_dish = add_data_groups(xds)
|
|
850
|
-
|
|
851
891
|
xds = add_missing_data_var_attrs(xds)
|
|
852
892
|
|
|
853
893
|
if (
|
|
@@ -895,8 +935,8 @@ def convert_and_write_partition(
|
|
|
895
935
|
antenna_id,
|
|
896
936
|
feed_id,
|
|
897
937
|
telescope_name,
|
|
938
|
+
xds.polarization,
|
|
898
939
|
)
|
|
899
|
-
|
|
900
940
|
logger.debug("Time antenna xds " + str(time.time() - start))
|
|
901
941
|
|
|
902
942
|
start = time.time()
|
|
@@ -916,7 +956,10 @@ def convert_and_write_partition(
|
|
|
916
956
|
logger.debug("Time phase_calibration xds " + str(time.time() - start))
|
|
917
957
|
|
|
918
958
|
# Change antenna_ids to antenna_names
|
|
919
|
-
|
|
959
|
+
with_antenna_partitioning = "ANTENNA1" in partition_info
|
|
960
|
+
xds = antenna_ids_to_names(
|
|
961
|
+
xds, ant_xds, is_single_dish, with_antenna_partitioning
|
|
962
|
+
)
|
|
920
963
|
# but before, keep the name-id arrays, we need them for the pointing and weather xds
|
|
921
964
|
ant_xds_name_ids = ant_xds["antenna_name"].set_xindex("antenna_id")
|
|
922
965
|
ant_xds_station_name_ids = ant_xds["station"].set_xindex("antenna_id")
|
|
@@ -943,6 +986,7 @@ def convert_and_write_partition(
|
|
|
943
986
|
logger.debug("Time weather " + str(time.time() - start))
|
|
944
987
|
|
|
945
988
|
# Create pointing_xds
|
|
989
|
+
pointing_xds = xr.Dataset()
|
|
946
990
|
if with_pointing:
|
|
947
991
|
start = time.time()
|
|
948
992
|
if pointing_interpolate:
|
|
@@ -1003,16 +1047,7 @@ def convert_and_write_partition(
|
|
|
1003
1047
|
)
|
|
1004
1048
|
logger.debug("Time field_and_source_xds " + str(time.time() - start))
|
|
1005
1049
|
|
|
1006
|
-
|
|
1007
|
-
# 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.
|
|
1008
|
-
if is_single_dish:
|
|
1009
|
-
xds.UVW.attrs["frame"] = field_and_source_xds[
|
|
1010
|
-
"FIELD_REFERENCE_CENTER"
|
|
1011
|
-
].attrs["frame"]
|
|
1012
|
-
else:
|
|
1013
|
-
xds.UVW.attrs["frame"] = field_and_source_xds[
|
|
1014
|
-
"FIELD_PHASE_CENTER"
|
|
1015
|
-
].attrs["frame"]
|
|
1050
|
+
xds = fix_uvw_frame(xds, field_and_source_xds, is_single_dish)
|
|
1016
1051
|
|
|
1017
1052
|
partition_info_misc_fields = {
|
|
1018
1053
|
"scan_id": scan_id,
|
|
@@ -1040,41 +1075,53 @@ def convert_and_write_partition(
|
|
|
1040
1075
|
else:
|
|
1041
1076
|
mode = "w-"
|
|
1042
1077
|
|
|
1078
|
+
if is_single_dish:
|
|
1079
|
+
xds.attrs["type"] = "spectrum"
|
|
1080
|
+
xds = xds.drop_vars(["UVW"])
|
|
1081
|
+
del xds["uvw_label"]
|
|
1082
|
+
else:
|
|
1083
|
+
if any("WVR" in s for s in intents):
|
|
1084
|
+
xds.attrs["type"] = "wvr"
|
|
1085
|
+
else:
|
|
1086
|
+
xds.attrs["type"] = "visibility"
|
|
1087
|
+
|
|
1043
1088
|
start = time.time()
|
|
1044
1089
|
if storage_backend == "zarr":
|
|
1045
|
-
xds.to_zarr(store=os.path.join(file_name, "
|
|
1046
|
-
ant_xds.to_zarr(store=os.path.join(file_name, "
|
|
1090
|
+
xds.to_zarr(store=os.path.join(file_name, "correlated_xds"), mode=mode)
|
|
1091
|
+
ant_xds.to_zarr(store=os.path.join(file_name, "antenna_xds"), mode=mode)
|
|
1047
1092
|
for group_name in xds.attrs["data_groups"]:
|
|
1048
1093
|
field_and_source_xds.to_zarr(
|
|
1049
1094
|
store=os.path.join(
|
|
1050
|
-
file_name, f"
|
|
1095
|
+
file_name, f"field_and_source_xds_{group_name}"
|
|
1051
1096
|
),
|
|
1052
1097
|
mode=mode,
|
|
1053
1098
|
)
|
|
1054
1099
|
|
|
1055
|
-
if with_pointing and len(pointing_xds.data_vars) >
|
|
1100
|
+
if with_pointing and len(pointing_xds.data_vars) > 0:
|
|
1056
1101
|
pointing_xds.to_zarr(
|
|
1057
|
-
store=os.path.join(file_name, "
|
|
1102
|
+
store=os.path.join(file_name, "pointing_xds"), mode=mode
|
|
1058
1103
|
)
|
|
1059
1104
|
|
|
1060
1105
|
if system_calibration_xds:
|
|
1061
1106
|
system_calibration_xds.to_zarr(
|
|
1062
|
-
store=os.path.join(file_name, "
|
|
1107
|
+
store=os.path.join(file_name, "system_calibration_xds"),
|
|
1108
|
+
mode=mode,
|
|
1063
1109
|
)
|
|
1064
1110
|
|
|
1065
1111
|
if gain_curve_xds:
|
|
1066
1112
|
gain_curve_xds.to_zarr(
|
|
1067
|
-
store=os.path.join(file_name, "
|
|
1113
|
+
store=os.path.join(file_name, "gain_curve_xds"), mode=mode
|
|
1068
1114
|
)
|
|
1069
1115
|
|
|
1070
1116
|
if phase_calibration_xds:
|
|
1071
1117
|
phase_calibration_xds.to_zarr(
|
|
1072
|
-
store=os.path.join(file_name, "
|
|
1118
|
+
store=os.path.join(file_name, "phase_calibration_xds"),
|
|
1119
|
+
mode=mode,
|
|
1073
1120
|
)
|
|
1074
1121
|
|
|
1075
1122
|
if weather_xds:
|
|
1076
1123
|
weather_xds.to_zarr(
|
|
1077
|
-
store=os.path.join(file_name, "
|
|
1124
|
+
store=os.path.join(file_name, "weather_xds"), mode=mode
|
|
1078
1125
|
)
|
|
1079
1126
|
|
|
1080
1127
|
elif storage_backend == "netcdf":
|
|
@@ -1086,8 +1133,31 @@ def convert_and_write_partition(
|
|
|
1086
1133
|
|
|
1087
1134
|
|
|
1088
1135
|
def antenna_ids_to_names(
|
|
1089
|
-
xds: xr.Dataset,
|
|
1136
|
+
xds: xr.Dataset,
|
|
1137
|
+
ant_xds: xr.Dataset,
|
|
1138
|
+
is_single_dish: bool,
|
|
1139
|
+
with_antenna_partitioning,
|
|
1090
1140
|
) -> xr.Dataset:
|
|
1141
|
+
"""
|
|
1142
|
+
Turns the antenna_ids that we get from MSv2 into MSv4 antenna_name
|
|
1143
|
+
|
|
1144
|
+
Parameters
|
|
1145
|
+
----------
|
|
1146
|
+
xds: xr.Dataset
|
|
1147
|
+
A main xds (MSv4)
|
|
1148
|
+
ant_xds: xr.Dataset
|
|
1149
|
+
The antenna_xds for this MSv4
|
|
1150
|
+
is_single_dish: bool
|
|
1151
|
+
Whether a single-dish ("spectrum" data) dataset
|
|
1152
|
+
with_antenna_partitioning: bool
|
|
1153
|
+
Whether the MSv4 partitions include the antenna axis => only
|
|
1154
|
+
one antenna (and implicitly one 'baseline' - auto-correlation)
|
|
1155
|
+
|
|
1156
|
+
Returns
|
|
1157
|
+
----------
|
|
1158
|
+
xr.Dataset
|
|
1159
|
+
The main xds with antenna_id replaced with antenna_name
|
|
1160
|
+
"""
|
|
1091
1161
|
ant_xds = ant_xds.set_xindex(
|
|
1092
1162
|
"antenna_id"
|
|
1093
1163
|
) # Allows for non-dimension coordinate selection.
|
|
@@ -1106,7 +1176,15 @@ def antenna_ids_to_names(
|
|
|
1106
1176
|
}
|
|
1107
1177
|
)
|
|
1108
1178
|
else:
|
|
1109
|
-
|
|
1179
|
+
if not with_antenna_partitioning:
|
|
1180
|
+
# baseline_antenna1_id will be removed soon below, but it is useful here to know the actual antenna_ids,
|
|
1181
|
+
# as opposed to the baseline_ids which can mismatch when data is missing for some antennas
|
|
1182
|
+
xds["baseline_id"] = ant_xds["antenna_name"].sel(
|
|
1183
|
+
antenna_id=xds["baseline_antenna1_id"]
|
|
1184
|
+
)
|
|
1185
|
+
else:
|
|
1186
|
+
xds["baseline_id"] = ant_xds["antenna_name"]
|
|
1187
|
+
|
|
1110
1188
|
unwanted_coords_from_ant_xds = [
|
|
1111
1189
|
"antenna_id",
|
|
1112
1190
|
"antenna_name",
|
|
@@ -6,8 +6,8 @@ import numpy as np
|
|
|
6
6
|
import xarray as xr
|
|
7
7
|
import os
|
|
8
8
|
|
|
9
|
-
from xradio.
|
|
10
|
-
from xradio.
|
|
9
|
+
from xradio.measurement_set._utils._msv2.subtables import subt_rename_ids
|
|
10
|
+
from xradio.measurement_set._utils._msv2._tables.read import (
|
|
11
11
|
load_generic_table,
|
|
12
12
|
convert_casacore_time,
|
|
13
13
|
convert_casacore_time_to_mjd,
|
|
@@ -15,7 +15,7 @@ from xradio.correlated_data._utils._ms._tables.read import (
|
|
|
15
15
|
table_exists,
|
|
16
16
|
)
|
|
17
17
|
from xradio._utils.schema import convert_generic_xds_to_xradio_schema
|
|
18
|
-
from xradio.
|
|
18
|
+
from xradio.measurement_set._utils._msv2.msv4_sub_xdss import interpolate_to_time
|
|
19
19
|
|
|
20
20
|
from xradio._utils.list_and_array import (
|
|
21
21
|
check_if_consistent,
|
|
@@ -31,6 +31,7 @@ def create_antenna_xds(
|
|
|
31
31
|
antenna_id: list,
|
|
32
32
|
feed_id: list,
|
|
33
33
|
telescope_name: str,
|
|
34
|
+
partition_polarization: xr.DataArray,
|
|
34
35
|
) -> xr.Dataset:
|
|
35
36
|
"""
|
|
36
37
|
Create an Xarray Dataset containing antenna information.
|
|
@@ -47,6 +48,8 @@ def create_antenna_xds(
|
|
|
47
48
|
List of feed IDs.
|
|
48
49
|
telescope_name : str
|
|
49
50
|
Name of the telescope.
|
|
51
|
+
partition_polarization: xr.DataArray
|
|
52
|
+
Polarization labels of this partition, needed if that info is not present in FEED
|
|
50
53
|
|
|
51
54
|
Returns
|
|
52
55
|
----------
|
|
@@ -59,6 +62,18 @@ def create_antenna_xds(
|
|
|
59
62
|
ant_xds = extract_feed_info(
|
|
60
63
|
ant_xds, in_file, antenna_id, feed_id, spectral_window_id
|
|
61
64
|
)
|
|
65
|
+
# Needed for special SPWs such as ALMA WVR or CHANNEL_AVERAGE data (have no feed info)
|
|
66
|
+
if "polarization_type" not in ant_xds:
|
|
67
|
+
pols_chars = list(partition_polarization.values[0])
|
|
68
|
+
pols_labels = [f"pol_{idx}" for idx in np.arange(0, len(pols_chars))]
|
|
69
|
+
ant_xds = ant_xds.assign_coords(receptor_label=pols_labels)
|
|
70
|
+
pol_type_values = [pols_chars] * len(ant_xds.antenna_name)
|
|
71
|
+
ant_xds = ant_xds.assign_coords(
|
|
72
|
+
polarization_type=(
|
|
73
|
+
["antenna_name", "receptor_label"],
|
|
74
|
+
pol_type_values,
|
|
75
|
+
)
|
|
76
|
+
)
|
|
62
77
|
|
|
63
78
|
ant_xds.attrs["overall_telescope_name"] = telescope_name
|
|
64
79
|
return ant_xds
|
|
@@ -87,7 +102,6 @@ def extract_antenna_info(
|
|
|
87
102
|
"""
|
|
88
103
|
to_new_data_variables = {
|
|
89
104
|
"POSITION": ["ANTENNA_POSITION", ["antenna_name", "cartesian_pos_label"]],
|
|
90
|
-
"OFFSET": ["ANTENNA_FEED_OFFSET", ["antenna_name", "cartesian_pos_label"]],
|
|
91
105
|
"DISH_DIAMETER": ["ANTENNA_DISH_DIAMETER", ["antenna_name"]],
|
|
92
106
|
}
|
|
93
107
|
|
|
@@ -95,7 +109,7 @@ def extract_antenna_info(
|
|
|
95
109
|
"NAME": ["antenna_name", ["antenna_name"]],
|
|
96
110
|
"STATION": ["station", ["antenna_name"]],
|
|
97
111
|
"MOUNT": ["mount", ["antenna_name"]],
|
|
98
|
-
"PHASED_ARRAY_ID": ["phased_array_id", ["antenna_name"]],
|
|
112
|
+
# "PHASED_ARRAY_ID": ["phased_array_id", ["antenna_name"]],
|
|
99
113
|
"antenna_id": ["antenna_id", ["antenna_name"]],
|
|
100
114
|
}
|
|
101
115
|
|
|
@@ -124,9 +138,8 @@ def extract_antenna_info(
|
|
|
124
138
|
|
|
125
139
|
ant_xds["ANTENNA_DISH_DIAMETER"].attrs.update({"units": ["m"], "type": "quantity"})
|
|
126
140
|
|
|
127
|
-
ant_xds["ANTENNA_FEED_OFFSET"].attrs["type"] = "earth_location_offset"
|
|
128
|
-
ant_xds["ANTENNA_FEED_OFFSET"].attrs["coordinate_system"] = "geocentric"
|
|
129
141
|
ant_xds["ANTENNA_POSITION"].attrs["coordinate_system"] = "geocentric"
|
|
142
|
+
ant_xds["ANTENNA_POSITION"].attrs["origin_object_name"] = "earth"
|
|
130
143
|
|
|
131
144
|
if telescope_name in ["ALMA", "VLA", "NOEMA", "EVLA"]:
|
|
132
145
|
# antenna_name = ant_xds["antenna_name"].values + "_" + ant_xds["station"].values
|
|
@@ -202,12 +215,17 @@ def extract_feed_info(
|
|
|
202
215
|
taql_where=f" where (ANTENNA_ID IN [{','.join(map(str, ant_xds.antenna_id.values))}]) AND (FEED_ID IN [{','.join(map(str, feed_id))}])",
|
|
203
216
|
) # Some Lofar and MeerKAT data have the spw column set to -1 so we can't use '(SPECTRAL_WINDOW_ID = {spectral_window_id})'
|
|
204
217
|
|
|
218
|
+
if not generic_feed_xds:
|
|
219
|
+
# Some MSv2 have a FEED table that does not cover all antenna_id (and feed_id)
|
|
220
|
+
return ant_xds
|
|
221
|
+
|
|
205
222
|
feed_spw = np.unique(generic_feed_xds.SPECTRAL_WINDOW_ID)
|
|
206
223
|
if len(feed_spw) == 1 and feed_spw[0] == -1:
|
|
207
224
|
generic_feed_xds = generic_feed_xds.isel(SPECTRAL_WINDOW_ID=0, drop=True)
|
|
208
225
|
else:
|
|
209
226
|
if spectral_window_id not in feed_spw:
|
|
210
|
-
|
|
227
|
+
# For some spw the feed table is empty (this is the case with ALMA spw WVR#NOMINAL).
|
|
228
|
+
return ant_xds
|
|
211
229
|
else:
|
|
212
230
|
generic_feed_xds = generic_feed_xds.sel(
|
|
213
231
|
SPECTRAL_WINDOW_ID=spectral_window_id, drop=True
|
|
@@ -228,14 +246,14 @@ def extract_feed_info(
|
|
|
228
246
|
), "The number of receptors must be constant in feed table."
|
|
229
247
|
|
|
230
248
|
to_new_data_variables = {
|
|
231
|
-
"
|
|
232
|
-
"
|
|
233
|
-
["antenna_name", "receptor_label"
|
|
249
|
+
"RECEPTOR_ANGLE": [
|
|
250
|
+
"ANTENNA_RECEPTOR_ANGLE",
|
|
251
|
+
["antenna_name", "receptor_label"],
|
|
234
252
|
],
|
|
235
|
-
"
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
253
|
+
"FOCUS_LENGTH": [
|
|
254
|
+
"ANTENNA_FOCUS_LENGTH",
|
|
255
|
+
["antenna_name"],
|
|
256
|
+
], # optional
|
|
239
257
|
}
|
|
240
258
|
|
|
241
259
|
to_new_coords = {
|
|
@@ -249,29 +267,31 @@ def extract_feed_info(
|
|
|
249
267
|
to_new_coords=to_new_coords,
|
|
250
268
|
)
|
|
251
269
|
|
|
252
|
-
# print('ant_xds["ANTENNA_FEED_OFFSET"]',ant_xds["ANTENNA_FEED_OFFSET"].data)
|
|
253
|
-
# print('generic_feed_xds["POSITION"].data',generic_feed_xds["POSITION"].data)
|
|
254
|
-
feed_offset_attrs = ant_xds["ANTENNA_FEED_OFFSET"].attrs
|
|
255
|
-
ant_xds["ANTENNA_FEED_OFFSET"] = (
|
|
256
|
-
ant_xds["ANTENNA_FEED_OFFSET"] + generic_feed_xds["POSITION"].data
|
|
257
|
-
)
|
|
258
|
-
# recover attrs after arithmetic operation
|
|
259
|
-
ant_xds["ANTENNA_FEED_OFFSET"].attrs.update(feed_offset_attrs)
|
|
260
|
-
|
|
261
|
-
coords = {}
|
|
262
270
|
# coords["receptor_label"] = "pol_" + np.arange(ant_xds.sizes["receptor_label"]).astype(str) #Works on laptop but fails in github test runner.
|
|
263
|
-
coords
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
271
|
+
coords = {
|
|
272
|
+
"receptor_label": np.array(
|
|
273
|
+
list(
|
|
274
|
+
map(
|
|
275
|
+
lambda x, y: x + "_" + y,
|
|
276
|
+
["pol"] * ant_xds.sizes["receptor_label"],
|
|
277
|
+
np.arange(ant_xds.sizes["receptor_label"]).astype(str),
|
|
278
|
+
)
|
|
279
|
+
),
|
|
280
|
+
dtype=str,
|
|
270
281
|
)
|
|
271
|
-
|
|
282
|
+
}
|
|
272
283
|
|
|
273
|
-
coords["sky_dir_label"] = ["ra", "dec"]
|
|
274
284
|
ant_xds = ant_xds.assign_coords(coords)
|
|
285
|
+
|
|
286
|
+
# Correct to expected types. Some ALMA-SD (at least) leave receptor_label, polarization_type columns
|
|
287
|
+
# in the MS empty, causing a type mismatch
|
|
288
|
+
if (
|
|
289
|
+
"polarization_type" in ant_xds.coords
|
|
290
|
+
and ant_xds.coords["polarization_type"].dtype != str
|
|
291
|
+
):
|
|
292
|
+
ant_xds.coords["polarization_type"] = ant_xds.coords[
|
|
293
|
+
"polarization_type"
|
|
294
|
+
].astype(str)
|
|
275
295
|
return ant_xds
|
|
276
296
|
|
|
277
297
|
|
|
@@ -370,6 +390,11 @@ def create_gain_curve_xds(
|
|
|
370
390
|
}
|
|
371
391
|
)
|
|
372
392
|
|
|
393
|
+
# correct expected types (for example "GAIN_CURVE" can be float32)
|
|
394
|
+
for data_var in gain_curve_xds:
|
|
395
|
+
if gain_curve_xds.data_vars[data_var].dtype != np.float64:
|
|
396
|
+
gain_curve_xds[data_var] = gain_curve_xds[data_var].astype(np.float64)
|
|
397
|
+
|
|
373
398
|
return gain_curve_xds
|
|
374
399
|
|
|
375
400
|
|
|
@@ -445,7 +470,7 @@ def create_phase_calibration_xds(
|
|
|
445
470
|
"TIME": ["time_phase_cal", ["time_phase_cal"]],
|
|
446
471
|
}
|
|
447
472
|
|
|
448
|
-
phase_cal_xds = xr.Dataset(attrs={"type": "
|
|
473
|
+
phase_cal_xds = xr.Dataset(attrs={"type": "phase_calibration"})
|
|
449
474
|
phase_cal_xds = convert_generic_xds_to_xradio_schema(
|
|
450
475
|
generic_phase_cal_xds, phase_cal_xds, to_new_data_variables, to_new_coords
|
|
451
476
|
)
|
|
@@ -494,8 +519,8 @@ def create_phase_calibration_xds(
|
|
|
494
519
|
time_coord_attrs = {
|
|
495
520
|
"type": "time",
|
|
496
521
|
"units": ["s"],
|
|
497
|
-
"scale": "
|
|
498
|
-
"format": "
|
|
522
|
+
"scale": "utc",
|
|
523
|
+
"format": "unix",
|
|
499
524
|
}
|
|
500
525
|
|
|
501
526
|
# If we interpolate rename the time_phase_cal axis to time.
|