xradio 1.0.2__py3-none-any.whl → 1.1.1__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/_casacore/casacore_from_casatools.py +75 -9
- xradio/_utils/dict_helpers.py +38 -7
- xradio/_utils/list_and_array.py +26 -3
- xradio/_utils/schema.py +44 -0
- xradio/_utils/xarray_helpers.py +63 -0
- xradio/_utils/zarr/common.py +4 -2
- xradio/image/__init__.py +4 -2
- xradio/image/_util/_casacore/common.py +2 -1
- xradio/image/_util/_casacore/xds_from_casacore.py +144 -92
- xradio/image/_util/_casacore/xds_to_casacore.py +118 -53
- xradio/image/_util/_fits/xds_from_fits.py +125 -37
- xradio/image/_util/_zarr/common.py +0 -1
- xradio/image/_util/casacore.py +183 -25
- xradio/image/_util/common.py +10 -8
- xradio/image/_util/image_factory.py +469 -27
- xradio/image/image.py +72 -100
- xradio/image/image_xds.py +262 -0
- xradio/image/schema.py +85 -0
- xradio/measurement_set/__init__.py +5 -4
- xradio/measurement_set/_utils/_msv2/_tables/read.py +4 -3
- xradio/measurement_set/_utils/_msv2/conversion.py +6 -9
- xradio/measurement_set/_utils/_msv2/create_field_and_source_xds.py +1 -0
- xradio/measurement_set/_utils/_msv2/msv4_sub_xdss.py +1 -1
- xradio/measurement_set/_utils/_utils/interpolate.py +5 -0
- xradio/measurement_set/_utils/_utils/partition_attrs.py +0 -1
- xradio/measurement_set/convert_msv2_to_processing_set.py +9 -9
- xradio/measurement_set/load_processing_set.py +2 -2
- xradio/measurement_set/measurement_set_xdt.py +83 -93
- xradio/measurement_set/open_processing_set.py +1 -1
- xradio/measurement_set/processing_set_xdt.py +33 -26
- xradio/schema/check.py +70 -19
- xradio/schema/common.py +0 -1
- xradio/testing/__init__.py +0 -0
- xradio/testing/_utils/__template__.py +58 -0
- xradio/testing/measurement_set/__init__.py +58 -0
- xradio/testing/measurement_set/checker.py +131 -0
- xradio/testing/measurement_set/io.py +22 -0
- xradio/testing/measurement_set/msv2_io.py +1854 -0
- {xradio-1.0.2.dist-info → xradio-1.1.1.dist-info}/METADATA +65 -23
- xradio-1.1.1.dist-info/RECORD +75 -0
- {xradio-1.0.2.dist-info → xradio-1.1.1.dist-info}/WHEEL +1 -1
- xradio-1.0.2.dist-info/RECORD +0 -66
- {xradio-1.0.2.dist-info → xradio-1.1.1.dist-info}/licenses/LICENSE.txt +0 -0
- {xradio-1.0.2.dist-info → xradio-1.1.1.dist-info}/top_level.txt +0 -0
xradio/image/_util/casacore.py
CHANGED
|
@@ -9,14 +9,17 @@ import warnings
|
|
|
9
9
|
from typing import Union
|
|
10
10
|
|
|
11
11
|
import xarray as xr
|
|
12
|
+
import re
|
|
13
|
+
import dask.array as da
|
|
14
|
+
from xradio._utils.schema import get_data_group_keys
|
|
12
15
|
|
|
13
16
|
try:
|
|
14
17
|
from casacore import tables
|
|
15
18
|
except ImportError:
|
|
16
19
|
import xradio._utils._casacore.casacore_from_casatools as tables
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
from ._casacore.xds_from_casacore import (
|
|
21
|
+
|
|
22
|
+
from xradio.image._util._casacore.xds_from_casacore import (
|
|
20
23
|
_add_mask,
|
|
21
24
|
_add_sky_or_aperture,
|
|
22
25
|
_casa_image_to_xds_attrs,
|
|
@@ -28,34 +31,79 @@ from ._casacore.xds_from_casacore import (
|
|
|
28
31
|
_get_beam,
|
|
29
32
|
_read_image_array,
|
|
30
33
|
)
|
|
31
|
-
from ._casacore.xds_to_casacore import (
|
|
34
|
+
from xradio.image._util._casacore.xds_to_casacore import (
|
|
32
35
|
_coord_dict_from_xds,
|
|
33
36
|
_history_from_xds,
|
|
34
37
|
_imageinfo_dict_from_xds,
|
|
35
38
|
_write_casa_data,
|
|
36
39
|
)
|
|
37
|
-
from .common import
|
|
40
|
+
from xradio.image._util.common import (
|
|
41
|
+
_aperture_or_sky,
|
|
42
|
+
_get_xds_dim_order,
|
|
43
|
+
_dask_arrayize_dv,
|
|
44
|
+
)
|
|
45
|
+
from xradio.image._util._casacore.common import _beam_fit_params, _open_image_ro
|
|
38
46
|
|
|
39
47
|
warnings.filterwarnings("ignore", category=FutureWarning)
|
|
40
48
|
|
|
41
49
|
|
|
42
|
-
def
|
|
50
|
+
def _squeeze_if_needed(ary: da, image_type: str) -> da:
|
|
51
|
+
if image_type.upper() == "VISIBILITY_NORMALIZATION":
|
|
52
|
+
shape = ary.shape
|
|
53
|
+
if shape[3] != 1 or shape[4] != 1:
|
|
54
|
+
raise ValueError(
|
|
55
|
+
"VISIBILITY_NORMALIZATION casa image must have l and m of length 1. Found "
|
|
56
|
+
+ [shape[3], shape[4]]
|
|
57
|
+
)
|
|
58
|
+
ary = ary.squeeze(axis=(3, 4))
|
|
59
|
+
return ary
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _get_casa_image_metadata(infile: str, do_sky_coords: bool, image_type: str) -> dict:
|
|
43
63
|
image_full_path = os.path.expanduser(infile)
|
|
44
64
|
with _open_image_ro(image_full_path) as casa_image:
|
|
45
65
|
coords = casa_image.coordinates()
|
|
46
66
|
cshape = casa_image.shape()
|
|
47
|
-
ret = _casa_image_to_xds_coords(image_full_path, False, do_sky_coords)
|
|
48
|
-
xds = ret["xds"]
|
|
67
|
+
ret = _casa_image_to_xds_coords(image_full_path, False, do_sky_coords, image_type)
|
|
68
|
+
xds = ret["xds"]
|
|
69
|
+
sphr_dims = ret["sphr_dims"]
|
|
49
70
|
nchan = ret["xds"].sizes["frequency"]
|
|
50
71
|
npol = ret["xds"].sizes["polarization"]
|
|
72
|
+
dimorder = _get_xds_dim_order(ret["sphr_dims"], image_type)
|
|
73
|
+
metadata = {
|
|
74
|
+
"coords": coords,
|
|
75
|
+
"cshape": cshape,
|
|
76
|
+
"image_full_path": image_full_path,
|
|
77
|
+
"nchan": nchan,
|
|
78
|
+
"npol": npol,
|
|
79
|
+
"dimorder": dimorder,
|
|
80
|
+
# "xds_attrs": _casa_image_to_xds_attrs(image_full_path),
|
|
81
|
+
"sphr_dims": sphr_dims,
|
|
82
|
+
"xds": xds,
|
|
83
|
+
}
|
|
84
|
+
return metadata
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _load_casa_image_block(
|
|
88
|
+
infile: str, block_des: dict, do_sky_coords: bool, image_type: str
|
|
89
|
+
) -> xr.Dataset:
|
|
90
|
+
md = _get_casa_image_metadata(infile, do_sky_coords, image_type)
|
|
91
|
+
coords = md["coords"]
|
|
92
|
+
cshape = md["cshape"]
|
|
93
|
+
dimorder = md["dimorder"]
|
|
94
|
+
sphr_dims = md["sphr_dims"]
|
|
95
|
+
nchan = md["nchan"]
|
|
96
|
+
npol = md["npol"]
|
|
97
|
+
xds = md["xds"].isel(block_des)
|
|
98
|
+
image_full_path = md["image_full_path"]
|
|
51
99
|
starts, shapes, slices = _get_starts_shapes_slices(block_des, coords, cshape)
|
|
52
|
-
dimorder = _get_xds_dim_order(ret["sphr_dims"])
|
|
53
100
|
transpose_list, new_axes = _get_transpose_list(coords)
|
|
54
101
|
block = _get_persistent_block(
|
|
55
102
|
image_full_path, shapes, starts, dimorder, transpose_list, new_axes
|
|
56
103
|
)
|
|
104
|
+
block = _squeeze_if_needed(block, image_type)
|
|
57
105
|
xds = _add_sky_or_aperture(
|
|
58
|
-
xds, block, dimorder, image_full_path,
|
|
106
|
+
xds, block, dimorder, image_full_path, sphr_dims, False, image_type
|
|
59
107
|
)
|
|
60
108
|
mymasks = _get_mask_names(image_full_path)
|
|
61
109
|
for m in mymasks:
|
|
@@ -64,58 +112,168 @@ def _load_casa_image_block(infile: str, block_des: dict, do_sky_coords) -> xr.Da
|
|
|
64
112
|
full_path, shapes, starts, dimorder, transpose_list, new_axes
|
|
65
113
|
)
|
|
66
114
|
# data vars are all caps by convention
|
|
67
|
-
|
|
115
|
+
mask_name = re.sub(r"\bMASK(\d+)\b", r"MASK_\1", m.upper())
|
|
116
|
+
xds = _add_mask(xds, mask_name, block, dimorder)
|
|
68
117
|
xds.attrs = _casa_image_to_xds_attrs(image_full_path)
|
|
69
|
-
beam = _get_beam(image_full_path, nchan, npol, False)
|
|
118
|
+
beam = _get_beam(image_full_path, nchan, npol, False, image_type)
|
|
119
|
+
|
|
70
120
|
if beam is not None:
|
|
71
121
|
selectors = {
|
|
72
122
|
k: block_des[k]
|
|
73
123
|
for k in ("time", "frequency", "polarization")
|
|
74
124
|
if k in block_des
|
|
75
125
|
}
|
|
76
|
-
xds["
|
|
126
|
+
xds["BEAM_FIT_PARAMS_" + image_type.upper()] = beam.isel(selectors)
|
|
127
|
+
xds["BEAM_FIT_PARAMS_" + image_type.upper()].attrs["type"] = (
|
|
128
|
+
"beam_fit_params_" + image_type.lower()
|
|
129
|
+
)
|
|
130
|
+
xds[image_type.upper()].attrs[_beam_fit_params] = (
|
|
131
|
+
"BEAM_FIT_PARAMS_" + image_type.upper()
|
|
132
|
+
)
|
|
77
133
|
return xds
|
|
78
134
|
|
|
79
135
|
|
|
80
|
-
def
|
|
136
|
+
def _open_casa_image(
|
|
81
137
|
infile: str,
|
|
82
138
|
chunks: Union[list, dict],
|
|
83
139
|
verbose: bool,
|
|
84
140
|
do_sky_coords: bool,
|
|
85
141
|
masks: bool = True,
|
|
86
|
-
history: bool =
|
|
142
|
+
history: bool = False,
|
|
143
|
+
image_type: str = "SKY",
|
|
87
144
|
) -> xr.Dataset:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
145
|
+
md = _get_casa_image_metadata(infile, do_sky_coords, image_type)
|
|
146
|
+
xds = md["xds"]
|
|
147
|
+
dimorder = md["dimorder"]
|
|
148
|
+
sphr_dims = md["sphr_dims"]
|
|
149
|
+
img_full_path = md["image_full_path"]
|
|
150
|
+
ary = _read_image_array(img_full_path, chunks, verbose=verbose)
|
|
151
|
+
ary = _squeeze_if_needed(ary, image_type)
|
|
92
152
|
xds = _add_sky_or_aperture(
|
|
93
153
|
xds,
|
|
94
|
-
|
|
154
|
+
ary,
|
|
95
155
|
dimorder,
|
|
96
156
|
img_full_path,
|
|
97
|
-
|
|
157
|
+
sphr_dims,
|
|
98
158
|
history,
|
|
159
|
+
image_type,
|
|
99
160
|
)
|
|
100
161
|
if masks:
|
|
101
162
|
mymasks = _get_mask_names(img_full_path)
|
|
102
163
|
for m in mymasks:
|
|
103
164
|
ary = _read_image_array(img_full_path, chunks, mask=m, verbose=verbose)
|
|
104
165
|
# data var names are all caps by convention
|
|
105
|
-
|
|
166
|
+
mask_name = re.sub(r"\bMASK(\d+)\b", r"MASK_\1", m.upper())
|
|
167
|
+
xds = _add_mask(xds, mask_name, ary, dimorder)
|
|
106
168
|
xds.attrs = _casa_image_to_xds_attrs(img_full_path)
|
|
107
169
|
beam = _get_beam(
|
|
108
|
-
img_full_path,
|
|
170
|
+
img_full_path,
|
|
171
|
+
xds.sizes["frequency"],
|
|
172
|
+
xds.sizes["polarization"],
|
|
173
|
+
True,
|
|
174
|
+
image_type,
|
|
109
175
|
)
|
|
110
176
|
if beam is not None:
|
|
111
|
-
xds["
|
|
177
|
+
xds["BEAM_FIT_PARAMS_" + image_type.upper()] = beam
|
|
178
|
+
xds["BEAM_FIT_PARAMS_" + image_type.upper()].attrs["type"] = (
|
|
179
|
+
"beam_fit_params_" + image_type.lower()
|
|
180
|
+
)
|
|
181
|
+
xds[image_type.upper()].attrs[_beam_fit_params] = (
|
|
182
|
+
"BEAM_FIT_PARAMS_" + image_type.upper()
|
|
183
|
+
)
|
|
184
|
+
|
|
112
185
|
# xds = _add_coord_attrs(xds, ret["icoords"], ret["dir_axes"])
|
|
113
186
|
xds = _dask_arrayize_dv(xds)
|
|
187
|
+
|
|
114
188
|
return xds
|
|
115
189
|
|
|
116
190
|
|
|
117
|
-
def
|
|
118
|
-
|
|
191
|
+
def _xds_to_multiple_casa_images(xds: xr.Dataset, image_store_name: str) -> None:
|
|
192
|
+
"""Function disentagles xradio xr.Dataset into multiple casa images based on data_groups attribute.
|
|
193
|
+
An xr.Dataset may contain multiple images (sky, residual, psf, etc) stored under different data variables sharing common coordinates.
|
|
194
|
+
An addtional complication is that CASA images allow for internal masks and beam fit parameters to be stored alongside the main image data so these also need to be handled.
|
|
195
|
+
This function creates separate casa images for each image type found in the data_groups attribute of the xr.Dataset.
|
|
196
|
+
|
|
197
|
+
Parameters
|
|
198
|
+
----------
|
|
199
|
+
xds : xr.Dataset
|
|
200
|
+
The xradio xr.Dataset containing multiple images and associated data.
|
|
201
|
+
image_store_name : str
|
|
202
|
+
The base name or path for storing the output CASA images.
|
|
203
|
+
If only one image is written, it will be named image_store_name, esle the images
|
|
204
|
+
will be named image_store_name.<image_type> where <image_type> is sky, residual, point_spread_function, etc.
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
data_vars_name_set = set(xds.data_vars.keys())
|
|
208
|
+
|
|
209
|
+
data_group_keys = list(get_data_group_keys(schema_name="image").keys())
|
|
210
|
+
internal_image_types_to_exclude = [
|
|
211
|
+
"flag",
|
|
212
|
+
"beam_fit_params_sky",
|
|
213
|
+
"beam_fit_params_point_spread_function",
|
|
214
|
+
]
|
|
215
|
+
n_image_written = 0
|
|
216
|
+
last_image_written = ""
|
|
217
|
+
for data_group in xds.attrs["data_groups"].keys():
|
|
218
|
+
for image_type in data_group_keys:
|
|
219
|
+
if (image_type in xds.attrs["data_groups"][data_group]) and (
|
|
220
|
+
image_type not in internal_image_types_to_exclude
|
|
221
|
+
):
|
|
222
|
+
image_name = xds.attrs["data_groups"][data_group][image_type]
|
|
223
|
+
if image_name in data_vars_name_set:
|
|
224
|
+
image_to_write_xds = xr.Dataset()
|
|
225
|
+
image_to_write_xds.attrs = xds.attrs.copy()
|
|
226
|
+
|
|
227
|
+
if image_type == "aperture":
|
|
228
|
+
image_to_write_xds["APERTURE"] = xds[image_name]
|
|
229
|
+
else:
|
|
230
|
+
image_to_write_xds["SKY"] = xds[image_name]
|
|
231
|
+
|
|
232
|
+
# This code handles adding internal masks and beam fit params if they exist.
|
|
233
|
+
if image_type == "sky":
|
|
234
|
+
if (
|
|
235
|
+
"beam_fit_params_sky"
|
|
236
|
+
in xds.attrs["data_groups"][data_group]
|
|
237
|
+
):
|
|
238
|
+
beam_fit_params_name = xds.attrs["data_groups"][data_group][
|
|
239
|
+
"beam_fit_params_sky"
|
|
240
|
+
]
|
|
241
|
+
image_to_write_xds["BEAM_FIT_PARAMS"] = xds[
|
|
242
|
+
beam_fit_params_name
|
|
243
|
+
]
|
|
244
|
+
|
|
245
|
+
if "flag" in xds.attrs["data_groups"][data_group]:
|
|
246
|
+
mask_sky_name = xds.attrs["data_groups"][data_group]["flag"]
|
|
247
|
+
image_to_write_xds["MASK_0"] = xds[mask_sky_name]
|
|
248
|
+
image_to_write_xds["SKY"].attrs["flag"] = "MASK_0"
|
|
249
|
+
|
|
250
|
+
if image_type == "point_spread_function":
|
|
251
|
+
if (
|
|
252
|
+
"beam_fit_params_point_spread_function"
|
|
253
|
+
in xds.attrs["data_groups"][data_group]
|
|
254
|
+
):
|
|
255
|
+
beam_fit_params_name = xds.attrs["data_groups"][data_group][
|
|
256
|
+
"beam_fit_params_point_spread_function"
|
|
257
|
+
]
|
|
258
|
+
image_to_write_xds["BEAM_FIT_PARAMS"] = xds[
|
|
259
|
+
beam_fit_params_name
|
|
260
|
+
]
|
|
261
|
+
outname = image_store_name + "." + image_type
|
|
262
|
+
_xds_to_casa_image(image_to_write_xds, outname)
|
|
263
|
+
if not os.path.exists(outname):
|
|
264
|
+
raise IOError(f"Failed to write CASA image {outname}")
|
|
265
|
+
n_image_written += 1
|
|
266
|
+
last_image_written = outname
|
|
267
|
+
data_vars_name_set.remove(image_name)
|
|
268
|
+
if n_image_written == 0:
|
|
269
|
+
raise ValueError("No valid image types found in xds to write to CASA images.")
|
|
270
|
+
if n_image_written == 1:
|
|
271
|
+
# rename the single written image to what the user requested
|
|
272
|
+
os.rename(last_image_written, image_store_name)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _xds_to_casa_image(xds: xr.Dataset, image_store_name: str) -> None:
|
|
276
|
+
image_full_path = os.path.expanduser(image_store_name)
|
|
119
277
|
_write_casa_data(xds, image_full_path)
|
|
120
278
|
# create coordinates
|
|
121
279
|
ap_sky = _aperture_or_sky(xds)
|
xradio/image/_util/common.py
CHANGED
|
@@ -7,6 +7,7 @@ from typing import Dict, List
|
|
|
7
7
|
import xarray as xr
|
|
8
8
|
from xradio._utils.coord_math import _deg_to_rad
|
|
9
9
|
from xradio._utils.dict_helpers import make_quantity
|
|
10
|
+
import toolviper.utils.logger as logger
|
|
10
11
|
|
|
11
12
|
_c = 2.99792458e08 * u.m / u.s
|
|
12
13
|
# OPTICAL = Z
|
|
@@ -17,17 +18,18 @@ _doppler_types = [
|
|
|
17
18
|
"beta",
|
|
18
19
|
"gamma",
|
|
19
20
|
]
|
|
20
|
-
_image_type = "
|
|
21
|
+
_image_type = "type"
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
def _aperture_or_sky(xds: xr.Dataset) -> str:
|
|
24
25
|
return "SKY" if "SKY" in xds.data_vars or "l" in xds.coords else "APERTURE"
|
|
25
26
|
|
|
26
27
|
|
|
27
|
-
def _get_xds_dim_order(has_sph: bool) -> list:
|
|
28
|
+
def _get_xds_dim_order(has_sph: bool, image_type: str) -> list:
|
|
28
29
|
dimorder = ["time", "frequency", "polarization"]
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
if image_type.upper() != "VISIBILITY_NORMALIZATION":
|
|
31
|
+
dir_lin = ["l", "m"] if has_sph else ["u", "v"]
|
|
32
|
+
dimorder.extend(dir_lin)
|
|
31
33
|
return dimorder
|
|
32
34
|
|
|
33
35
|
|
|
@@ -252,10 +254,10 @@ def _compute_sky_reference_pixel(xds: xr.Dataset) -> np.ndarray:
|
|
|
252
254
|
|
|
253
255
|
def _l_m_attr_notes() -> Dict[str, str]:
|
|
254
256
|
return {
|
|
255
|
-
"l": "l is the angle measured from the
|
|
256
|
-
"So l = x*cdelt, where x is the number of pixels from the
|
|
257
|
+
"l": "l is the angle measured from the reference direction to the east. "
|
|
258
|
+
"So l = x*cdelt, where x is the number of pixels from the reference direction. "
|
|
257
259
|
"See AIPS Memo #27, Section III.",
|
|
258
|
-
"m": "m is the angle measured from the
|
|
259
|
-
"So m = y*cdelt, where y is the number of pixels from the
|
|
260
|
+
"m": "m is the angle measured from the reference direction to the north. "
|
|
261
|
+
"So m = y*cdelt, where y is the number of pixels from the reference direction. "
|
|
260
262
|
"See AIPS Memo #27, Section III.",
|
|
261
263
|
}
|