xradio 0.0.27__py3-none-any.whl → 0.0.29__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 (51) hide show
  1. xradio/__init__.py +5 -4
  2. xradio/_utils/array.py +90 -0
  3. xradio/_utils/zarr/common.py +48 -3
  4. xradio/image/_util/_fits/xds_from_fits.py +10 -5
  5. xradio/image/_util/_zarr/zarr_low_level.py +27 -24
  6. xradio/image/_util/common.py +4 -1
  7. xradio/image/_util/zarr.py +4 -1
  8. xradio/schema/__init__.py +24 -6
  9. xradio/schema/bases.py +440 -2
  10. xradio/schema/check.py +96 -55
  11. xradio/schema/dataclass.py +123 -27
  12. xradio/schema/metamodel.py +21 -4
  13. xradio/schema/typing.py +33 -18
  14. xradio/vis/__init__.py +5 -2
  15. xradio/vis/_processing_set.py +30 -9
  16. xradio/vis/_vis_utils/_ms/_tables/create_field_and_source_xds.py +710 -0
  17. xradio/vis/_vis_utils/_ms/_tables/load.py +23 -10
  18. xradio/vis/_vis_utils/_ms/_tables/load_main_table.py +145 -64
  19. xradio/vis/_vis_utils/_ms/_tables/read.py +782 -156
  20. xradio/vis/_vis_utils/_ms/_tables/read_main_table.py +176 -45
  21. xradio/vis/_vis_utils/_ms/_tables/read_subtables.py +79 -28
  22. xradio/vis/_vis_utils/_ms/_tables/write.py +102 -45
  23. xradio/vis/_vis_utils/_ms/_tables/write_exp_api.py +127 -65
  24. xradio/vis/_vis_utils/_ms/chunks.py +58 -21
  25. xradio/vis/_vis_utils/_ms/conversion.py +536 -67
  26. xradio/vis/_vis_utils/_ms/descr.py +52 -20
  27. xradio/vis/_vis_utils/_ms/msv2_to_msv4_meta.py +70 -35
  28. xradio/vis/_vis_utils/_ms/msv4_infos.py +0 -59
  29. xradio/vis/_vis_utils/_ms/msv4_sub_xdss.py +76 -9
  30. xradio/vis/_vis_utils/_ms/optimised_functions.py +0 -46
  31. xradio/vis/_vis_utils/_ms/partition_queries.py +308 -119
  32. xradio/vis/_vis_utils/_ms/partitions.py +82 -25
  33. xradio/vis/_vis_utils/_ms/subtables.py +32 -14
  34. xradio/vis/_vis_utils/_utils/partition_attrs.py +30 -11
  35. xradio/vis/_vis_utils/_utils/xds_helper.py +136 -45
  36. xradio/vis/_vis_utils/_zarr/read.py +60 -22
  37. xradio/vis/_vis_utils/_zarr/write.py +83 -9
  38. xradio/vis/_vis_utils/ms.py +48 -29
  39. xradio/vis/_vis_utils/zarr.py +44 -20
  40. xradio/vis/convert_msv2_to_processing_set.py +106 -32
  41. xradio/vis/load_processing_set.py +38 -61
  42. xradio/vis/read_processing_set.py +62 -96
  43. xradio/vis/schema.py +687 -0
  44. xradio/vis/vis_io.py +75 -43
  45. {xradio-0.0.27.dist-info → xradio-0.0.29.dist-info}/LICENSE.txt +6 -1
  46. {xradio-0.0.27.dist-info → xradio-0.0.29.dist-info}/METADATA +10 -5
  47. xradio-0.0.29.dist-info/RECORD +73 -0
  48. {xradio-0.0.27.dist-info → xradio-0.0.29.dist-info}/WHEEL +1 -1
  49. xradio/vis/model.py +0 -497
  50. xradio-0.0.27.dist-info/RECORD +0 -71
  51. {xradio-0.0.27.dist-info → xradio-0.0.29.dist-info}/top_level.txt +0 -0
@@ -3,28 +3,37 @@ from typing import Dict, Union
3
3
 
4
4
  import numpy as np
5
5
  import pandas as pd
6
+ import xarray as xr
6
7
 
7
8
  from ._tables.read import read_generic_table, read_flat_col_chunk
8
9
  from ._tables.table_query import open_query, open_table_ro
9
- from xradio.vis._vis_utils._ms.optimised_functions import unique_1d
10
+ from xradio._utils.array import unique_1d
10
11
 
11
12
 
12
13
  def describe_ms(
13
- infile: str, mode="summary", rowmap: Union[dict, None] = None
14
+ infile: str, mode: str = "summary", rowmap: Union[dict, None] = None
14
15
  ) -> Union[pd.DataFrame, Dict]:
15
16
  """
16
17
  Summarize the contents of an MS directory in casacore table format
17
18
 
18
- :param infile: input MS filename
19
- :param mode: type of information returned ('summary', 'flat', 'expanded').
20
- 'summary' returns a pandas dataframe that is nice for displaying in notebooks
21
- etc. 'flat' returns a list of tuples of (ddi, row, chan, pol). 'expanded'
22
- returns a list of tuples of (ddi, time, baseline, chan, pol). These latter two
23
- are good for trying to determine chunk size for read_ms(expand=True/False).
24
- :param rowmap: dict of DDI to tuple of (row indices, channel indices). Returned
25
- by ms_selection function. Default None ignores selections
26
-
27
- :return: summary as a pd dataframe
19
+ Parameters
20
+ ----------
21
+ infile : str
22
+ input MS filename
23
+ mode : str (Default value = "summary")
24
+ type of information returned ('summary', 'flat', 'expanded').
25
+ 'summary' returns a pandas dataframe that is nice for displaying in notebooks
26
+ etc. 'flat' returns a list of tuples of (ddi, row, chan, pol). 'expanded'
27
+ returns a list of tuples of (ddi, time, baseline, chan, pol). These latter two
28
+ are good for trying to determine chunk size for read_ms(expand=True/False). (Default value = "summary")
29
+ rowmap : Union[dict, None] (Default value = None)
30
+ dict of DDI to tuple of (row indices, channel indices). Returned
31
+ by ms_selection function. Default None ignores selections
32
+
33
+ Returns
34
+ -------
35
+ Union[pd.DataFrame, Dict]
36
+ summary as a pd dataframe
28
37
  """
29
38
  infile = os.path.expanduser(infile) # does nothing if $HOME is unknown
30
39
  if not os.path.isdir(infile):
@@ -38,18 +47,22 @@ def describe_ms(
38
47
 
39
48
  ddi_xds = read_generic_table(infile, "DATA_DESCRIPTION")
40
49
  ddis = list(ddi_xds.row.values) if rowmap is None else list(rowmap.keys())
41
-
42
50
  summary: Union[pd.DataFrame, Dict] = []
43
51
  if mode == "summary":
44
52
  summary = pd.DataFrame([])
45
53
 
54
+ all_sdf = []
46
55
  with open_table_ro(infile) as tb_tool:
47
56
  for ddi in ddis:
48
57
  taql = f"select * from $tb_tool where DATA_DESC_ID = {ddi}"
49
58
  with open_query(tb_tool, taql) as query_per_ddi:
50
- populate_ms_descr(infile, mode, query_per_ddi, summary, ddi, ddi_xds)
59
+ sdf = populate_ms_descr(
60
+ infile, mode, query_per_ddi, summary, ddi, ddi_xds
61
+ )
62
+ all_sdf.append(sdf)
51
63
 
52
64
  if mode == "summary":
65
+ summary = pd.DataFrame(all_sdf)
53
66
  summary = summary.set_index("ddi").sort_index()
54
67
  else:
55
68
  summary = dict(summary)
@@ -57,16 +70,35 @@ def describe_ms(
57
70
 
58
71
 
59
72
  def populate_ms_descr(
60
- infile, mode, query_per_ddi, summary, ddi, ddi_xds, rowmap=None
73
+ infile: str,
74
+ mode: str,
75
+ query_per_ddi,
76
+ summary: dict,
77
+ ddi: int,
78
+ ddi_xds: xr.Dataset,
79
+ rowmap: Union[Dict, None] = None,
61
80
  ) -> pd.DataFrame:
62
- """Adds information from the time and baseline (antenna1+antenna2)
81
+ """
82
+ Adds information from the time and baseline (antenna1+antenna2)
63
83
  columns as well as channel and polarizations, based on a taql
64
84
  query.
65
85
 
66
- :param mode: mode (as in describe_ms())
67
- :param query_per_ddi: a TaQL query with data per individual DDI
68
- :param sdf: summary dict being populated
69
- :param summary: final summary object being populated from the invividual sdf's
86
+ Parameters
87
+ ----------
88
+ infile : str
89
+ input table/MS path
90
+ mode : str
91
+ mode (as in describe_ms())
92
+ query_per_ddi :
93
+ a TaQL query with data per individual DDI
94
+ summary : Dict
95
+ summary dict being populated
96
+ ddi_xds : xr.Dataset
97
+ final summary object being populated from the invividual sdf's
98
+
99
+ Returns
100
+ -------
101
+ pd.DataFrame
70
102
  """
71
103
  spw_ids = ddi_xds.spectral_window_id.values
72
104
  pol_ids = ddi_xds.polarization_id.values
@@ -1,3 +1,5 @@
1
+ import graphviper.utils.logger as logger
2
+
1
3
  col_to_data_variable_names = {
2
4
  "FLOAT_DATA": "SPECTRUM",
3
5
  "DATA": "VISIBILITY",
@@ -26,17 +28,38 @@ col_to_coord_names = {
26
28
  "ANTENNA2": "baseline_ant2_id",
27
29
  }
28
30
 
31
+ # Map casacore measures to astropy
29
32
  casacore_to_msv4_measure_type = {
30
- "quanta": {"type": "quantity", "Ref": None},
31
- "direction": {"type": "sky_coord", "Ref": "frame"},
32
- "epoch": {"type": "time", "Ref": "scale"},
33
- "frequency": {"type": "spectral_coord", "Ref": "frame"},
34
- "position": {"type": "earth_location", "Ref": "ellipsoid"},
35
- "uvw": {"type": "uvw", "Ref": "frame"},
33
+ "quanta": {
34
+ "type": "quantity",
35
+ },
36
+ "direction": {"type": "sky_coord", "Ref": "frame", "Ref_map": {"J2000": "fk5"}},
37
+ "epoch": {"type": "time", "Ref": "scale", "Ref_map": {"UTC": "utc"}},
38
+ "frequency": {
39
+ "type": "spectral_coord",
40
+ "Ref": "frame",
41
+ "Ref_map": {
42
+ "REST": "REST",
43
+ "LSRK": "LSRK",
44
+ "LSRD": "LSRD",
45
+ "BARY": "BARY",
46
+ "GEO": "GEO",
47
+ "TOPO": "TOPO",
48
+ "GALACTO": "GALACTO",
49
+ "LGROUP": "LGROUP",
50
+ "CMB": "CMB",
51
+ "Undefined": "Undefined",
52
+ },
53
+ },
54
+ "position": {
55
+ "type": "earth_location",
56
+ "Ref": "ellipsoid",
57
+ "Ref_map": {"ITRF": "GRS80"},
58
+ },
59
+ "uvw": {"type": "uvw", "Ref": "frame", "Ref_map": {"ITRF": "GRS80"}},
60
+ "radialvelocity": {"type": "quantity"},
36
61
  }
37
62
 
38
- casacore_to_msv4_ref = {"J2000": "FK5", "ITRF": "GRS80"}
39
-
40
63
  casa_frequency_frames = [
41
64
  "REST",
42
65
  "LSRK",
@@ -54,48 +77,60 @@ casa_frequency_frames_codes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 64]
54
77
 
55
78
 
56
79
  def column_description_casacore_to_msv4_measure(
57
- casacore_column_description, ref_code=None, time_format="unix"
80
+ casacore_column_description, ref_code=None, time_format="UNIX"
58
81
  ):
59
82
  import numpy as np
60
83
 
61
84
  msv4_measure = {}
62
85
  if "MEASINFO" in casacore_column_description["keywords"]:
63
- msv4_measure["type"] = casacore_to_msv4_measure_type[
64
- casacore_column_description["keywords"]["MEASINFO"]["type"]
65
- ]["type"]
86
+ measinfo = casacore_column_description["keywords"]["MEASINFO"]
87
+
88
+ # Get conversion information
89
+ msv4_measure_conversion = casacore_to_msv4_measure_type[measinfo["type"]]
90
+
91
+ # Convert type, copy unit
92
+ msv4_measure["type"] = msv4_measure_conversion["type"]
66
93
  msv4_measure["units"] = list(
67
94
  casacore_column_description["keywords"]["QuantumUnits"]
68
95
  )
69
96
 
70
- if "TabRefCodes" in casacore_column_description["keywords"]["MEASINFO"]:
71
- ref_index = np.where(
72
- casacore_column_description["keywords"]["MEASINFO"]["TabRefCodes"]
73
- == ref_code
74
- )[0][0]
75
- casa_ref = casacore_column_description["keywords"]["MEASINFO"][
76
- "TabRefTypes"
77
- ][ref_index]
78
- else:
79
- if "Ref" in casacore_column_description["keywords"]["MEASINFO"]:
80
- casa_ref = casacore_column_description["keywords"]["MEASINFO"]["Ref"]
81
- elif (
82
- casacore_column_description["keywords"]["MEASINFO"]["type"]
83
- == "frequency"
84
- ):
97
+ # Reference frame to convert?
98
+ if "Ref" in msv4_measure_conversion:
99
+ # Find reference frame
100
+ if "TabRefCodes" in measinfo:
101
+ ref_index = np.where(measinfo["TabRefCodes"] == ref_code)[0][0]
102
+ casa_ref = measinfo["TabRefTypes"][ref_index]
103
+ elif "Ref" in measinfo:
104
+ casa_ref = measinfo["Ref"]
105
+ elif measinfo["type"] == "frequency":
85
106
  # Some MSv2 don't have the "TabRefCodes".
86
107
  ref_index = np.where(casa_frequency_frames_codes == ref_code)[0][0]
87
108
  casa_ref = casa_frequency_frames[ref_index]
109
+ else:
110
+ logger.warning(
111
+ f"Could not determine {measinfo['type']} measure "
112
+ "reference frame!"
113
+ )
114
+
115
+ # Convert into MSv4 representation of reference frame, warn if unknown
116
+ if casa_ref in msv4_measure_conversion.get("Ref_map", {}):
117
+ casa_ref = msv4_measure_conversion["Ref_map"][casa_ref]
118
+ else:
119
+ logger.warning(
120
+ f"Unknown reference frame for {measinfo['type']} "
121
+ f"measure, using verbatim: {casa_ref}"
122
+ )
88
123
 
89
- if casa_ref in casacore_to_msv4_ref:
90
- casa_ref = casacore_to_msv4_ref[casa_ref]
91
- msv4_measure[
92
- casacore_to_msv4_measure_type[
93
- casacore_column_description["keywords"]["MEASINFO"]["type"]
94
- ]["Ref"]
95
- ] = casa_ref
124
+ msv4_measure[msv4_measure_conversion["Ref"]] = casa_ref
96
125
 
97
126
  if msv4_measure["type"] == "time":
98
- msv4_measure["format"] = "unix"
127
+ msv4_measure["format"] = time_format
128
+ elif "QuantumUnits" in casacore_column_description["keywords"]:
129
+ msv4_measure = {
130
+ "type": "quantity",
131
+ "units": list(casacore_column_description["keywords"]["QuantumUnits"]),
132
+ }
133
+
99
134
  return msv4_measure
100
135
 
101
136
 
@@ -1,59 +0,0 @@
1
- from .msv2_to_msv4_meta import column_description_casacore_to_msv4_measure
2
- from .subtables import subt_rename_ids
3
- from ._tables.read import read_generic_table
4
-
5
-
6
- def create_field_info(in_file, field_id):
7
- field_xds = read_generic_table(
8
- in_file,
9
- "FIELD",
10
- rename_ids=subt_rename_ids["FIELD"],
11
- ).sel(field_id=field_id)
12
- # https://stackoverflow.com/questions/53195684/how-to-navigate-a-dict-by-list-of-keys
13
-
14
- field_column_description = field_xds.attrs["other"]["msv2"]["ctds_attrs"][
15
- "column_descriptions"
16
- ]
17
- # ['DELAY_DIR', 'PHASE_DIR', 'REFERENCE_DIR', 'CODE', 'FLAG_ROW', 'NAME', 'NUM_POLY', 'SOURCE_ID', 'TIME']
18
-
19
- msv4_measure = column_description_casacore_to_msv4_measure(
20
- field_column_description["REFERENCE_DIR"],
21
- ref_code=getattr(field_xds.get("refdir_ref"), "data", None),
22
- )
23
- delay_dir = {
24
- "dims": "",
25
- "data": list(field_xds["delay_dir"].data[0, :]),
26
- "attrs": msv4_measure,
27
- }
28
-
29
- msv4_measure = column_description_casacore_to_msv4_measure(
30
- field_column_description["PHASE_DIR"],
31
- ref_code=getattr(field_xds.get("phasedir_ref"), "data", None),
32
- )
33
- phase_dir = {
34
- "dims": "",
35
- "data": list(field_xds["phase_dir"].data[0, :]),
36
- "attrs": msv4_measure,
37
- }
38
-
39
- msv4_measure = column_description_casacore_to_msv4_measure(
40
- field_column_description["DELAY_DIR"],
41
- ref_code=getattr(field_xds.get("delaydir_ref"), "data", None),
42
- )
43
- reference_dir = {
44
- "dims": "",
45
- "data": list(field_xds["delay_dir"].data[0, :]),
46
- "attrs": msv4_measure,
47
- }
48
-
49
- field_info = {
50
- "name": str(field_xds["name"].data),
51
- "code": str(field_xds["code"].data),
52
- "delay_direction": delay_dir,
53
- "phase_direction": phase_dir,
54
- "reference_direction": reference_dir,
55
- "field_id": field_id,
56
- }
57
- # xds.attrs["field_info"] = field_info
58
-
59
- return field_info
@@ -1,13 +1,61 @@
1
+ import graphviper.utils.logger as logger
2
+ import time
3
+ from typing import Tuple, Union
4
+
1
5
  import numpy as np
2
6
  import xarray as xr
3
7
 
4
8
  from .msv2_to_msv4_meta import column_description_casacore_to_msv4_measure
5
9
  from .subtables import subt_rename_ids
6
- from ._tables.read import read_generic_table
10
+ from ._tables.read import make_taql_where_between_min_max, read_generic_table
11
+
12
+
13
+ def interpolate_to_time(
14
+ xds: xr.Dataset, interp_time: Union[xr.DataArray, None], message_prefix: str
15
+ ) -> xr.Dataset:
16
+ """
17
+ Interpolate the time coordinate of the input xarray dataset to the
18
+ a data array. This can be used for example to interpolate a pointing_xds
19
+ to the time coord of the (main) MSv4, or similarly the ephemeris
20
+ data variables of a field_and_source_xds.
21
+
22
+ Uses interpolation method "linear", unless the source number of points is
23
+ 1 in which case "nearest" is used, to avoid divide-by-zero issues.
24
+
25
+ Parameters:
26
+ ----------
27
+ xds : xr.Dataset
28
+ Xarray dataset to interpolate (presumably a pointing_xds or an xds of
29
+ ephemeris variables)
30
+ interp_time : Union[xr.DataArray, None]
31
+ Time axis to interpolate the dataset to (usually main MSv4 time)
32
+ message_prefix: str
33
+ A prefix for info/debug/etc. messages
34
+
35
+ Returns:
36
+ -------
37
+ interpolated_xds : xr.Dataset
38
+ xarray dataset with time axis interpolated to interp_time.
39
+ """
40
+ if interp_time is not None:
41
+ points_before = xds.time.size
42
+ if points_before > 1:
43
+ method = "linear"
44
+ else:
45
+ method = "nearest"
46
+ xds = xds.interp(time=interp_time, method=method, assume_sorted=True)
47
+ points_after = xds.time.size
48
+ logger.debug(
49
+ f"{message_prefix}: interpolating the time coordinate "
50
+ f"from {points_before} to {points_after} points"
51
+ )
52
+
53
+ return xds
7
54
 
8
55
 
9
56
  def create_ant_xds(in_file: str):
10
- """Creates an Antenna Xarray Dataset from a MS v2 ANTENNA table.
57
+ """
58
+ Creates an Antenna Xarray Dataset from a MS v2 ANTENNA table.
11
59
 
12
60
  Parameters
13
61
  ----------
@@ -16,7 +64,7 @@ def create_ant_xds(in_file: str):
16
64
 
17
65
  Returns
18
66
  -------
19
- ant_xds : xarray.Dataset
67
+ xr.Dataset
20
68
  Antenna Xarray Dataset.
21
69
  """
22
70
  # Dictionaries that define the conversion from MSv2 to MSv4:
@@ -90,7 +138,8 @@ def create_ant_xds(in_file: str):
90
138
 
91
139
 
92
140
  def create_weather_xds(in_file: str):
93
- """Creates a Weather Xarray Dataset from a MS v2 WEATHER table.
141
+ """
142
+ Creates a Weather Xarray Dataset from a MS v2 WEATHER table.
94
143
 
95
144
  Parameters
96
145
  ----------
@@ -99,7 +148,7 @@ def create_weather_xds(in_file: str):
99
148
 
100
149
  Returns
101
150
  -------
102
- weather_xds : xarray.Dataset
151
+ xr.Dataset
103
152
  Weather Xarray Dataset.
104
153
  """
105
154
  # Dictionaries that define the conversion from MSv2 to MSv4:
@@ -209,8 +258,13 @@ def create_weather_xds(in_file: str):
209
258
  return weather_xds
210
259
 
211
260
 
212
- def create_pointing_xds(in_file: str):
213
- """Creates a Pointing Xarray Dataset from an MS v2 POINTING (sub)table.
261
+ def create_pointing_xds(
262
+ in_file: str,
263
+ time_min_max: Union[Tuple[np.float64, np.float64], None],
264
+ interp_time: Union[xr.DataArray, None] = None,
265
+ ) -> xr.Dataset:
266
+ """
267
+ Creates a Pointing Xarray Dataset from an MS v2 POINTING (sub)table.
214
268
 
215
269
  WIP: details of a few direction variables (and possibly moving some to attributes) to be
216
270
  settled (see MSv4 spreadsheet).
@@ -219,12 +273,17 @@ def create_pointing_xds(in_file: str):
219
273
  ----------
220
274
  in_file : str
221
275
  Input MS name.
276
+ time_min_max : tuple
277
+ min / max times values to constrain loading (from the TIME column)
278
+ interp_time : Union[xr.DataArray, None] (Default value = None)
279
+ interpolate time to this (presumably main dataset time)
222
280
 
223
281
  Returns
224
282
  -------
225
- pointing_xds : xarray.Dataset
226
- Pointing Xarray Dataset.
283
+ xr.Dataset
284
+ Pointing Xarray dataset
227
285
  """
286
+ start = time.time()
228
287
 
229
288
  # Dictionaries that define the conversion from MSv2 to MSv4:
230
289
  to_new_data_variable_names = {
@@ -257,11 +316,15 @@ def create_pointing_xds(in_file: str):
257
316
  to_new_coord_names = {"ra/dec": "direction"}
258
317
  coord_dims = {}
259
318
 
319
+ taql_time_range = make_taql_where_between_min_max(
320
+ time_min_max, in_file, "POINTING", "TIME"
321
+ )
260
322
  # Read POINTING table into a Xarray Dataset.
261
323
  generic_pointing_xds = read_generic_table(
262
324
  in_file,
263
325
  "POINTING",
264
326
  rename_ids=subt_rename_ids["POINTING"],
327
+ taql_where=taql_time_range,
265
328
  )
266
329
  if not generic_pointing_xds.data_vars:
267
330
  # apparently empty MS/POINTING table => produce empty xds
@@ -319,4 +382,8 @@ def create_pointing_xds(in_file: str):
319
382
  }
320
383
  # TODO: move also source_offset/pointing_offset from data_vars to attrs?
321
384
 
385
+ pointing_xds = interpolate_to_time(pointing_xds, interp_time, "pointing_xds")
386
+
387
+ logger.debug(f"create_pointing_xds() execution time {time.time() - start:0.2f} s")
388
+
322
389
  return pointing_xds
@@ -1,46 +0,0 @@
1
- """Contains optimised functions to be used within other modules."""
2
-
3
- import numpy as np
4
- import pandas as pd
5
-
6
-
7
- def unique_1d(array: np.ndarray) -> np.ndarray:
8
- """Optimised version of np.unique for 1D arrays.
9
-
10
- Args:
11
- array (np.ndarray): a 1D array of values.
12
-
13
- Returns:
14
- np.ndarray: a sorted array of unique values.
15
- """
16
- return np.sort(pd.unique(array))
17
-
18
-
19
- def pairing_function(antenna_pairs: np.ndarray) -> np.ndarray:
20
- """Pairing function to convert each array pair to a single value.
21
-
22
- This custom pairing function will only work if the maximum value is less
23
- than 2**20 and less than 2,048 if using signed 32-bit integers.
24
-
25
- Args:
26
- antenna_pairs (np.ndarray): a 2D array containing antenna 1 and antenna
27
- 2 ids, which forms a baseline.
28
-
29
- Returns:
30
- np.ndarray: a 1D array of the paired values.
31
- """
32
- return antenna_pairs[:, 0] * 2**20 + antenna_pairs[:, 1]
33
-
34
-
35
- def inverse_pairing_function(paired_array: np.ndarray) -> np.ndarray:
36
- """Inverse pairing function to convert each paired value to an antenna pair.
37
-
38
- This inverse pairing function is the inverse of the custom pairing function.
39
-
40
- Args:
41
- paired_array (np.ndarray): a 1D array of the paired values.
42
-
43
- Returns:
44
- np.ndarray: a 2D array containing antenna 1 and antenna 2 ids.
45
- """
46
- return np.column_stack(np.divmod(paired_array, 2**20))