xradio 0.0.51__py3-none-any.whl → 0.0.53__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/dict_helpers.py +24 -0
- xradio/image/_util/_casacore/xds_from_casacore.py +116 -69
- xradio/image/_util/_casacore/xds_to_casacore.py +53 -32
- xradio/image/_util/_fits/xds_from_fits.py +60 -78
- xradio/image/_util/_zarr/common.py +1 -1
- xradio/image/_util/_zarr/xds_from_zarr.py +37 -20
- xradio/image/_util/_zarr/xds_to_zarr.py +37 -21
- xradio/image/_util/casacore.py +6 -3
- xradio/image/image.py +20 -8
- xradio/measurement_set/_utils/_msv2/conversion.py +12 -0
- xradio/measurement_set/_utils/_msv2/create_field_and_source_xds.py +9 -2
- xradio/measurement_set/_utils/_msv2/msv4_sub_xdss.py +116 -1
- xradio/measurement_set/schema.py +104 -1
- xradio/schema/check.py +3 -2
- {xradio-0.0.51.dist-info → xradio-0.0.53.dist-info}/METADATA +4 -4
- {xradio-0.0.51.dist-info → xradio-0.0.53.dist-info}/RECORD +19 -19
- {xradio-0.0.51.dist-info → xradio-0.0.53.dist-info}/WHEEL +1 -1
- {xradio-0.0.51.dist-info → xradio-0.0.53.dist-info}/licenses/LICENSE.txt +0 -0
- {xradio-0.0.51.dist-info → xradio-0.0.53.dist-info}/top_level.txt +0 -0
|
@@ -17,9 +17,10 @@ from ..common import (
|
|
|
17
17
|
)
|
|
18
18
|
from xradio._utils.coord_math import _deg_to_rad
|
|
19
19
|
from xradio._utils.dict_helpers import (
|
|
20
|
-
make_quantity,
|
|
21
20
|
make_frequency_reference_dict,
|
|
21
|
+
make_quantity,
|
|
22
22
|
make_skycoord_dict,
|
|
23
|
+
make_time_measure_dict,
|
|
23
24
|
)
|
|
24
25
|
import copy
|
|
25
26
|
import dask
|
|
@@ -49,7 +50,7 @@ def _fits_image_to_xds(
|
|
|
49
50
|
sphr_dims = helpers["sphr_dims"]
|
|
50
51
|
ary = _read_image_array(img_full_path, chunks, helpers, verbose)
|
|
51
52
|
dim_order = _get_xds_dim_order(sphr_dims)
|
|
52
|
-
xds = _add_sky_or_aperture(xds, ary, dim_order, helpers, sphr_dims)
|
|
53
|
+
xds = _add_sky_or_aperture(xds, ary, dim_order, header, helpers, sphr_dims)
|
|
53
54
|
xds.attrs = attrs
|
|
54
55
|
xds = _add_coord_attrs(xds, helpers)
|
|
55
56
|
if helpers["has_multibeam"]:
|
|
@@ -70,13 +71,7 @@ def _add_coord_attrs(xds: xr.Dataset, helpers: dict) -> xr.Dataset:
|
|
|
70
71
|
|
|
71
72
|
def _add_time_attrs(xds: xr.Dataset, helpers: dict) -> xr.Dataset:
|
|
72
73
|
time_coord = xds.coords["time"]
|
|
73
|
-
|
|
74
|
-
del meta["value"]
|
|
75
|
-
# meta["units"] = [ meta["units"] ]
|
|
76
|
-
# meta['format'] = 'MJD'
|
|
77
|
-
# meta['time_scale'] = meta['refer']
|
|
78
|
-
# del meta['refer']
|
|
79
|
-
time_coord.attrs = meta
|
|
74
|
+
time_coord.attrs = copy.deepcopy(helpers["obsdate"]["attrs"])
|
|
80
75
|
xds.assign_coords(time=time_coord)
|
|
81
76
|
return xds
|
|
82
77
|
|
|
@@ -87,8 +82,6 @@ def _add_freq_attrs(xds: xr.Dataset, helpers: dict) -> xr.Dataset:
|
|
|
87
82
|
if helpers["has_freq"]:
|
|
88
83
|
meta["rest_frequency"] = make_quantity(helpers["restfreq"], "Hz")
|
|
89
84
|
meta["rest_frequencies"] = [meta["rest_frequency"]]
|
|
90
|
-
# meta["frame"] = helpers["specsys"]
|
|
91
|
-
# meta["units"] = "Hz"
|
|
92
85
|
meta["type"] = "frequency"
|
|
93
86
|
meta["wave_unit"] = "mm"
|
|
94
87
|
freq_axis = helpers["freq_axis"]
|
|
@@ -128,16 +121,6 @@ def _add_l_m_attrs(xds: xr.Dataset, helpers: dict) -> xr.Dataset:
|
|
|
128
121
|
|
|
129
122
|
|
|
130
123
|
def _add_lin_attrs(xds: xr.Dataset, helpers: dict) -> xr.Dataset:
|
|
131
|
-
"""
|
|
132
|
-
if helpers["sphr_dims"]:
|
|
133
|
-
for i, name in zip(helpers["dir_axes"], helpers["sphr_axis_names"]):
|
|
134
|
-
meta = {
|
|
135
|
-
"units": "rad",
|
|
136
|
-
"crval": helpers["crval"][i],
|
|
137
|
-
"cdelt": helpers["cdelt"][i],
|
|
138
|
-
}
|
|
139
|
-
xds.coords[name].attrs = meta
|
|
140
|
-
"""
|
|
141
124
|
if not helpers["sphr_dims"]:
|
|
142
125
|
for i, j in zip(helpers["dir_axes"], ("u", "v")):
|
|
143
126
|
meta = {
|
|
@@ -167,7 +150,7 @@ def _xds_direction_attrs_from_header(helpers: dict, header) -> dict:
|
|
|
167
150
|
direction["projection"] = p0
|
|
168
151
|
helpers["projection"] = p0
|
|
169
152
|
ref_sys = header["RADESYS"]
|
|
170
|
-
ref_eqx = header["EQUINOX"]
|
|
153
|
+
ref_eqx = None if ref_sys.upper() == "ICRS" else header["EQUINOX"]
|
|
171
154
|
if ref_sys == "FK5" and ref_eqx == 2000:
|
|
172
155
|
ref_eqx = "J2000.0"
|
|
173
156
|
helpers["ref_sys"] = ref_sys
|
|
@@ -176,15 +159,6 @@ def _xds_direction_attrs_from_header(helpers: dict, header) -> dict:
|
|
|
176
159
|
direction["reference"] = make_skycoord_dict(
|
|
177
160
|
[0.0, 0.0], units=["rad", "rad"], frame=ref_sys
|
|
178
161
|
)
|
|
179
|
-
"""
|
|
180
|
-
direction["reference"] = {
|
|
181
|
-
"type": "sky_coord",
|
|
182
|
-
"frame": ref_sys,
|
|
183
|
-
"equinox": ref_eqx,
|
|
184
|
-
"units": ["rad", "rad"],
|
|
185
|
-
"value": [0.0, 0.0],
|
|
186
|
-
}
|
|
187
|
-
"""
|
|
188
162
|
dir_axes = helpers["dir_axes"]
|
|
189
163
|
ddata = []
|
|
190
164
|
dunits = []
|
|
@@ -194,9 +168,10 @@ def _xds_direction_attrs_from_header(helpers: dict, header) -> dict:
|
|
|
194
168
|
ddata.append(x.value)
|
|
195
169
|
# direction["reference"]["value"][i] = x.value
|
|
196
170
|
x = helpers["cdelt"][i] * u.Unit(_get_unit(helpers["cunit"][i]))
|
|
197
|
-
dunits.append(
|
|
171
|
+
dunits.append("rad")
|
|
198
172
|
direction["reference"] = make_skycoord_dict(ddata, units=dunits, frame=ref_sys)
|
|
199
|
-
|
|
173
|
+
if ref_eqx is not None:
|
|
174
|
+
direction["reference"]["attrs"]["equinox"] = ref_eqx.lower()
|
|
200
175
|
direction["latpole"] = make_quantity(
|
|
201
176
|
header["LATPOLE"] * _deg_to_rad, "rad", dims=["l", "m"]
|
|
202
177
|
)
|
|
@@ -261,18 +236,21 @@ def _get_telescope_metadata(helpers: dict, header) -> dict:
|
|
|
261
236
|
r = np.sqrt(np.sum(xyz * xyz))
|
|
262
237
|
lat = np.arcsin(z / r)
|
|
263
238
|
long = np.arctan2(y, x)
|
|
264
|
-
tel["
|
|
265
|
-
"
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
239
|
+
tel["location"] = {
|
|
240
|
+
"attrs": {
|
|
241
|
+
"coordinate_system": "geocentric",
|
|
242
|
+
# I haven't seen a FITS keyword for reference frame of telescope posiiton
|
|
243
|
+
"frame": "ITRF",
|
|
244
|
+
"origin_object_name": "earth",
|
|
245
|
+
"type": "location",
|
|
246
|
+
"units": ["rad", "rad", "m"],
|
|
247
|
+
},
|
|
248
|
+
"data": np.array([long, lat, r]),
|
|
270
249
|
}
|
|
271
|
-
helpers["tel_pos"] = tel["position"]
|
|
272
250
|
return tel
|
|
273
251
|
|
|
274
252
|
|
|
275
|
-
def
|
|
253
|
+
def _compute_pointing_center(helpers: dict, header) -> dict:
|
|
276
254
|
# Neither helpers or header is modified
|
|
277
255
|
t_axes = helpers["t_axes"]
|
|
278
256
|
long_unit = header[f"CUNIT{t_axes[0]}"]
|
|
@@ -285,7 +263,9 @@ def _pointing_center_to_metadata(helpers: dict, header) -> dict:
|
|
|
285
263
|
pc_lat = float(header[f"CRVAL{t_axes[1]}"]) * unit[1]
|
|
286
264
|
pc_long = pc_long.to(u.rad).value
|
|
287
265
|
pc_lat = pc_lat.to(u.rad).value
|
|
288
|
-
return
|
|
266
|
+
return make_skycoord_dict(
|
|
267
|
+
[pc_long, pc_lat], units=["rad", "rad"], frame=helpers["ref_sys"]
|
|
268
|
+
)
|
|
289
269
|
|
|
290
270
|
|
|
291
271
|
def _user_attrs_from_header(header) -> dict:
|
|
@@ -294,6 +274,9 @@ def _user_attrs_from_header(header) -> dict:
|
|
|
294
274
|
"ALTRPIX",
|
|
295
275
|
"ALTRVAL",
|
|
296
276
|
"BITPIX",
|
|
277
|
+
"BMAJ",
|
|
278
|
+
"BMIN",
|
|
279
|
+
"BPA",
|
|
297
280
|
"BSCALE",
|
|
298
281
|
"BTYPE",
|
|
299
282
|
"BUNIT",
|
|
@@ -319,21 +302,20 @@ def _user_attrs_from_header(header) -> dict:
|
|
|
319
302
|
]
|
|
320
303
|
regex = r"|".join(
|
|
321
304
|
[
|
|
322
|
-
"^NAXIS
|
|
323
|
-
"^CRVAL
|
|
324
|
-
"^CRPIX
|
|
325
|
-
"^CTYPE
|
|
326
|
-
"^CDELT
|
|
327
|
-
"^CUNIT
|
|
328
|
-
"^OBSGEO-(X|Y|Z)$",
|
|
329
|
-
"^P(C|V)
|
|
305
|
+
r"^NAXIS\d?$",
|
|
306
|
+
r"^CRVAL\d$",
|
|
307
|
+
r"^CRPIX\d$",
|
|
308
|
+
r"^CTYPE\d$",
|
|
309
|
+
r"^CDELT\d$",
|
|
310
|
+
r"^CUNIT\d$",
|
|
311
|
+
r"^OBSGEO-(X|Y|Z)$",
|
|
312
|
+
r"^P(C|V)0?\d_0?\d",
|
|
330
313
|
]
|
|
331
314
|
)
|
|
332
315
|
user = {}
|
|
333
316
|
for k, v in header.items():
|
|
334
|
-
if re.search(regex, k) or k in exclude:
|
|
335
|
-
|
|
336
|
-
user[k.lower()] = v
|
|
317
|
+
if not (re.search(regex, k) or k in exclude):
|
|
318
|
+
user[k.lower()] = v
|
|
337
319
|
return user
|
|
338
320
|
|
|
339
321
|
|
|
@@ -382,7 +364,7 @@ def _create_dim_map(helpers: dict, header) -> dict:
|
|
|
382
364
|
return dim_map
|
|
383
365
|
|
|
384
366
|
|
|
385
|
-
def _fits_header_to_xds_attrs(hdulist: fits.hdu.hdulist.HDUList) ->
|
|
367
|
+
def _fits_header_to_xds_attrs(hdulist: fits.hdu.hdulist.HDUList) -> tuple:
|
|
386
368
|
primary = None
|
|
387
369
|
beams = None
|
|
388
370
|
for hdu in hdulist:
|
|
@@ -418,9 +400,7 @@ def _fits_header_to_xds_attrs(hdulist: fits.hdu.hdulist.HDUList) -> dict:
|
|
|
418
400
|
if dir_axes is not None:
|
|
419
401
|
attrs["direction"] = _xds_direction_attrs_from_header(helpers, header)
|
|
420
402
|
# FIXME read fits data in chunks in case all data too large to hold in memory
|
|
421
|
-
has_mask = da.any(da.isnan(primary.data)).compute()
|
|
422
|
-
attrs["active_mask"] = "MASK0" if has_mask else None
|
|
423
|
-
helpers["has_mask"] = has_mask
|
|
403
|
+
helpers["has_mask"] = da.any(da.isnan(primary.data)).compute()
|
|
424
404
|
beam = _beam_attr_from_header(helpers, header)
|
|
425
405
|
if beam != "mb":
|
|
426
406
|
helpers["beam"] = beam
|
|
@@ -432,24 +412,15 @@ def _fits_header_to_xds_attrs(hdulist: fits.hdu.hdulist.HDUList) -> dict:
|
|
|
432
412
|
helpers["dtype"] = "float64"
|
|
433
413
|
else:
|
|
434
414
|
raise RuntimeError(f'Unhandled data type {header["BITPIX"]}')
|
|
435
|
-
helpers["
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
obsdate["scale"] = header["TIMESYS"]
|
|
443
|
-
obsdate["format"] = "MJD"
|
|
444
|
-
attrs["obsdate"] = obsdate
|
|
445
|
-
helpers["obsdate"] = obsdate
|
|
446
|
-
attrs["observer"] = header["OBSERVER"]
|
|
447
|
-
attrs["pointing_center"] = _pointing_center_to_metadata(helpers, header)
|
|
448
|
-
attrs["description"] = None
|
|
449
|
-
attrs["telescope"] = _get_telescope_metadata(helpers, header)
|
|
415
|
+
helpers["obsdate"] = make_time_measure_dict(
|
|
416
|
+
data=Time(header["DATE-OBS"], format="isot").mjd,
|
|
417
|
+
units=["d"],
|
|
418
|
+
scale=header["TIMESYS"],
|
|
419
|
+
time_format="MJD",
|
|
420
|
+
)
|
|
421
|
+
|
|
450
422
|
# TODO complete _make_history_xds when spec has been finalized
|
|
451
423
|
# attrs['history'] = _make_history_xds(header)
|
|
452
|
-
attrs["user"] = _user_attrs_from_header(header)
|
|
453
424
|
return attrs, helpers, header
|
|
454
425
|
|
|
455
426
|
|
|
@@ -532,7 +503,7 @@ def _create_coords(
|
|
|
532
503
|
|
|
533
504
|
|
|
534
505
|
def _get_time_values(helpers):
|
|
535
|
-
return [helpers["obsdate"]["
|
|
506
|
+
return [helpers["obsdate"]["data"]]
|
|
536
507
|
|
|
537
508
|
|
|
538
509
|
def _get_pol_values(helpers):
|
|
@@ -710,14 +681,22 @@ def _add_sky_or_aperture(
|
|
|
710
681
|
xds: xr.Dataset,
|
|
711
682
|
ary: Union[np.ndarray, da.array],
|
|
712
683
|
dim_order: list,
|
|
684
|
+
header,
|
|
713
685
|
helpers: dict,
|
|
714
686
|
has_sph_dims: bool,
|
|
715
687
|
) -> xr.Dataset:
|
|
716
688
|
xda = xr.DataArray(ary, dims=dim_order)
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
689
|
+
for h, a in zip(
|
|
690
|
+
["BUNIT", "BTYPE", "OBJECT", "OBSERVER"],
|
|
691
|
+
["units", _image_type, "object_name", "observer"],
|
|
692
|
+
):
|
|
693
|
+
if h in header:
|
|
694
|
+
xda.attrs[a] = header[h]
|
|
695
|
+
xda.attrs["obsdate"] = helpers["obsdate"].copy()
|
|
696
|
+
xda.attrs["pointing_center"] = _compute_pointing_center(helpers, header)
|
|
697
|
+
xda.attrs["telescope"] = _get_telescope_metadata(helpers, header)
|
|
698
|
+
xda.attrs["description"] = None
|
|
699
|
+
xda.attrs["user"] = _user_attrs_from_header(header)
|
|
721
700
|
name = "SKY" if has_sph_dims else "APERTURE"
|
|
722
701
|
xda = xda.rename(name)
|
|
723
702
|
xds[xda.name] = xda
|
|
@@ -727,6 +706,9 @@ def _add_sky_or_aperture(
|
|
|
727
706
|
mask.attrs = {}
|
|
728
707
|
mask = mask.rename("MASK0")
|
|
729
708
|
xds["MASK0"] = mask
|
|
709
|
+
xda.attrs["active_mask"] = "MASK0"
|
|
710
|
+
xda = xda.rename(name)
|
|
711
|
+
xds[xda.name] = xda
|
|
730
712
|
return xds
|
|
731
713
|
|
|
732
714
|
|
|
@@ -12,9 +12,9 @@ from xradio._utils.zarr.common import _get_file_system_and_items
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def _read_zarr(
|
|
15
|
-
zarr_store: str,
|
|
15
|
+
zarr_store: str, id_dict: dict, selection: dict = {}
|
|
16
16
|
) -> (xr.Dataset, bool):
|
|
17
|
-
# supported key/values in
|
|
17
|
+
# supported key/values in id_dict are:
|
|
18
18
|
# "dv"
|
|
19
19
|
# what data variables should be returned as.
|
|
20
20
|
# "numpy": numpy arrays
|
|
@@ -29,27 +29,27 @@ def _read_zarr(
|
|
|
29
29
|
do_dask = False
|
|
30
30
|
do_numpy = False
|
|
31
31
|
do_np_coords = False
|
|
32
|
-
if "dv" in
|
|
33
|
-
dv =
|
|
32
|
+
if "dv" in id_dict:
|
|
33
|
+
dv = id_dict["dv"]
|
|
34
34
|
if dv in ["dask", "numpy"]:
|
|
35
35
|
do_dask = dv == "dask"
|
|
36
36
|
do_numpy = not do_dask
|
|
37
37
|
else:
|
|
38
38
|
raise ValueError(
|
|
39
|
-
f"Unsupported value {
|
|
39
|
+
f"Unsupported value {id_dict[dv]} for id_dict[dv]. "
|
|
40
40
|
"Supported values are 'dask' and 'numpy'"
|
|
41
41
|
)
|
|
42
|
-
if "coords" in
|
|
43
|
-
c =
|
|
42
|
+
if "coords" in id_dict:
|
|
43
|
+
c = id_dict["coords"]
|
|
44
44
|
if c == "numpy":
|
|
45
45
|
do_np_coords = True
|
|
46
46
|
else:
|
|
47
47
|
raise ValueError(
|
|
48
|
-
f"Unexpected value {c} for
|
|
48
|
+
f"Unexpected value {c} for id_dict[coords]. "
|
|
49
49
|
"The supported value is 'numpy'"
|
|
50
50
|
)
|
|
51
51
|
# do not pass selection, because that is only for the top level data vars
|
|
52
|
-
xds = _decode(xds, zarr_store,
|
|
52
|
+
xds = _decode(xds, zarr_store, id_dict)
|
|
53
53
|
if do_np_coords:
|
|
54
54
|
xds = _coords_to_numpy(xds)
|
|
55
55
|
if do_dask:
|
|
@@ -59,11 +59,9 @@ def _read_zarr(
|
|
|
59
59
|
return xds
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
def _decode(xds: xr.Dataset, zarr_store: str,
|
|
62
|
+
def _decode(xds: xr.Dataset, zarr_store: str, id_dict: dict) -> xr.Dataset:
|
|
63
63
|
xds.attrs = _decode_dict(xds.attrs, "")
|
|
64
|
-
|
|
65
|
-
for k, v in sub_xdses.items():
|
|
66
|
-
xds.attrs[k] = v
|
|
64
|
+
_decode_sub_xdses(xds, zarr_store, id_dict)
|
|
67
65
|
return xds
|
|
68
66
|
|
|
69
67
|
|
|
@@ -85,26 +83,45 @@ def _decode_dict(my_dict: dict, top_key: str) -> dict:
|
|
|
85
83
|
return my_dict
|
|
86
84
|
|
|
87
85
|
|
|
88
|
-
def _decode_sub_xdses(
|
|
89
|
-
|
|
86
|
+
def _decode_sub_xdses(xarrayObj, top_dir: str, id_dict: dict) -> None:
|
|
87
|
+
# FIXME this also needs to support S3
|
|
88
|
+
# determine immediate subdirs of zarr_store
|
|
89
|
+
entries = os.scandir(top_dir)
|
|
90
|
+
for d in entries:
|
|
91
|
+
path = os.path.join(top_dir, d.name)
|
|
92
|
+
if os.path.isdir(path):
|
|
93
|
+
if d.name.startswith(_top_level_sub_xds):
|
|
94
|
+
ky = d.name[len(_top_level_sub_xds) :]
|
|
95
|
+
xarrayObj.attrs[ky] = _read_zarr(path, id_dict)
|
|
96
|
+
# TODO if attrs that are xdses have attrs that are xdses ...
|
|
97
|
+
else:
|
|
98
|
+
# descend into the directory
|
|
99
|
+
_decode_sub_xdses(xarrayObj[d.name], path, id_dict)
|
|
90
100
|
|
|
91
|
-
fs, store_contents = _get_file_system_and_items(zarr_store)
|
|
92
101
|
|
|
102
|
+
"""
|
|
103
|
+
def _decode_sub_xdses(zarr_store: str, id_dict: dict) -> dict:
|
|
104
|
+
sub_xdses = {}
|
|
105
|
+
fs, store_contents = _get_file_system_and_items(zarr_store)
|
|
93
106
|
if isinstance(fs, s3fs.core.S3FileSystem):
|
|
94
107
|
# could we just use the items as returned from the helper function..?
|
|
95
108
|
store_tree = fs.walk(zarr_store, topdown=True)
|
|
109
|
+
# Q: what is prepend_s3 used for? In this version it is defined but not used.
|
|
96
110
|
prepend_s3 = "s3://"
|
|
97
111
|
else:
|
|
98
112
|
store_tree = os.walk(zarr_store, topdown=True)
|
|
99
113
|
prepend_s3 = ""
|
|
100
|
-
|
|
101
114
|
for root, dirs, files in store_tree:
|
|
115
|
+
relpath = os.path.relpath(root, zarr_store)
|
|
116
|
+
print("rpath", relpath)
|
|
102
117
|
for d in dirs:
|
|
103
118
|
if d.startswith(_top_level_sub_xds):
|
|
104
|
-
xds = _read_zarr(os.sep.join([root, d]),
|
|
119
|
+
xds = _read_zarr(os.sep.join([root, d]), id_dict)
|
|
105
120
|
# for k, v in xds.data_vars.items():
|
|
106
121
|
# xds = xds.drop_vars([k]).assign({k: v.compute()})
|
|
107
|
-
ky = d[len(_top_level_sub_xds)
|
|
122
|
+
ky = d[len(_top_level_sub_xds) :]
|
|
108
123
|
sub_xdses[ky] = xds
|
|
109
|
-
|
|
124
|
+
print(f"Sub xdses: {sub_xdses.keys()}")
|
|
125
|
+
print("return")
|
|
110
126
|
return sub_xdses
|
|
127
|
+
"""
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import dask.array as da
|
|
2
|
+
import logging
|
|
1
3
|
import numpy as np
|
|
2
4
|
import xarray as xr
|
|
3
5
|
import os
|
|
@@ -5,45 +7,59 @@ from .common import _np_types, _top_level_sub_xds
|
|
|
5
7
|
|
|
6
8
|
|
|
7
9
|
def _write_zarr(xds: xr.Dataset, zarr_store: str):
|
|
10
|
+
max_chunk_size = 0.95 * 2**30
|
|
11
|
+
for dv in xds.data_vars:
|
|
12
|
+
obj = xds[dv]
|
|
13
|
+
if isinstance(obj, xr.core.dataarray.DataArray) and isinstance(
|
|
14
|
+
obj.data, da.Array
|
|
15
|
+
):
|
|
16
|
+
# get chunk size to make sure it is small enough to be compressed
|
|
17
|
+
ary = obj.data
|
|
18
|
+
chunk_size_bytes = np.prod(ary.chunksize) * np.dtype(ary.dtype).itemsize
|
|
19
|
+
if chunk_size_bytes > max_chunk_size:
|
|
20
|
+
raise ValueError(
|
|
21
|
+
f"Chunk size of {chunk_size_bytes/1e9} GB for data variable {dv} "
|
|
22
|
+
"bytes is too large for compression. To fix this, "
|
|
23
|
+
"reduce the chunk size of the dask array in the data variable "
|
|
24
|
+
f"by at least a factor of {chunk_size_bytes/max_chunk_size}."
|
|
25
|
+
)
|
|
8
26
|
xds_copy = xds.copy(deep=True)
|
|
9
|
-
|
|
27
|
+
sub_xds_dict = _encode(xds_copy, zarr_store)
|
|
10
28
|
z_obj = xds_copy.to_zarr(store=zarr_store, compute=True)
|
|
11
|
-
|
|
29
|
+
if sub_xds_dict:
|
|
30
|
+
_write_sub_xdses(sub_xds_dict)
|
|
12
31
|
|
|
13
32
|
|
|
14
|
-
def _encode(xds: xr.Dataset):
|
|
33
|
+
def _encode(xds: xr.Dataset, top_path: str) -> dict:
|
|
15
34
|
# encode attrs
|
|
16
|
-
|
|
17
|
-
|
|
35
|
+
sub_xds_dict = {}
|
|
36
|
+
_encode_dict(xds.attrs, top_path, sub_xds_dict)
|
|
37
|
+
for dv in xds.data_vars:
|
|
38
|
+
_encode_dict(xds[dv].attrs, os.sep.join([top_path, dv]), sub_xds_dict)
|
|
39
|
+
logging.debug(f"Encoded sub_xds_dict: {sub_xds_dict}")
|
|
40
|
+
return sub_xds_dict
|
|
18
41
|
|
|
19
42
|
|
|
20
|
-
def _encode_dict(my_dict: dict,
|
|
21
|
-
xds_dict = {}
|
|
43
|
+
def _encode_dict(my_dict: dict, top_path: str, sub_xds_dict) -> tuple:
|
|
22
44
|
del_keys = []
|
|
23
45
|
for k, v in my_dict.items():
|
|
24
46
|
if isinstance(v, dict):
|
|
25
|
-
z = os.sep.join([
|
|
26
|
-
|
|
27
|
-
if ret_xds_dict:
|
|
28
|
-
xds_dict[k] = ret_xds_dict
|
|
47
|
+
z = os.sep.join([top_path, k])
|
|
48
|
+
_encode_dict(v, z, sub_xds_dict)
|
|
29
49
|
elif isinstance(v, np.ndarray):
|
|
30
50
|
my_dict[k] = {}
|
|
31
51
|
my_dict[k]["_type"] = "numpy.ndarray"
|
|
32
52
|
my_dict[k]["_value"] = v.tolist()
|
|
33
53
|
my_dict[k]["_dtype"] = str(v.dtype)
|
|
34
54
|
elif isinstance(v, xr.Dataset):
|
|
35
|
-
|
|
55
|
+
sub_xds_dict[os.sep.join([top_path, f"{_top_level_sub_xds}{k}"])] = v.copy(
|
|
56
|
+
deep=True
|
|
57
|
+
)
|
|
36
58
|
del_keys.append(k)
|
|
37
59
|
for k in del_keys:
|
|
38
60
|
del my_dict[k]
|
|
39
|
-
return my_dict, xds_dict
|
|
40
61
|
|
|
41
62
|
|
|
42
|
-
def _write_sub_xdses(
|
|
43
|
-
for k, v in
|
|
44
|
-
|
|
45
|
-
if isinstance(v, dict):
|
|
46
|
-
_write_sub_xdses(zarr_store, xds_dict[k], my_path)
|
|
47
|
-
elif isinstance(v, xr.Dataset):
|
|
48
|
-
zs = os.sep.join([zarr_store, my_path])
|
|
49
|
-
z_obj = v.to_zarr(store=zs, compute=True)
|
|
63
|
+
def _write_sub_xdses(sub_xds: dict):
|
|
64
|
+
for k, v in sub_xds.items():
|
|
65
|
+
z_obj = v.to_zarr(store=k, compute=True)
|
xradio/image/_util/casacore.py
CHANGED
|
@@ -48,7 +48,9 @@ def _load_casa_image_block(infile: str, block_des: dict, do_sky_coords) -> xr.Da
|
|
|
48
48
|
block = _get_persistent_block(
|
|
49
49
|
image_full_path, shapes, starts, dimorder, transpose_list, new_axes
|
|
50
50
|
)
|
|
51
|
-
xds = _add_sky_or_aperture(
|
|
51
|
+
xds = _add_sky_or_aperture(
|
|
52
|
+
xds, block, dimorder, image_full_path, ret["sphr_dims"], True
|
|
53
|
+
)
|
|
52
54
|
mymasks = _get_mask_names(image_full_path)
|
|
53
55
|
for m in mymasks:
|
|
54
56
|
full_path = os.sep.join([image_full_path, m])
|
|
@@ -57,7 +59,7 @@ def _load_casa_image_block(infile: str, block_des: dict, do_sky_coords) -> xr.Da
|
|
|
57
59
|
)
|
|
58
60
|
# data vars are all caps by convention
|
|
59
61
|
xds = _add_mask(xds, m.upper(), block, dimorder)
|
|
60
|
-
xds.attrs = _casa_image_to_xds_attrs(image_full_path
|
|
62
|
+
xds.attrs = _casa_image_to_xds_attrs(image_full_path)
|
|
61
63
|
mb = _multibeam_array(xds, image_full_path, False)
|
|
62
64
|
if mb is not None:
|
|
63
65
|
selectors = {}
|
|
@@ -86,6 +88,7 @@ def _read_casa_image(
|
|
|
86
88
|
dimorder,
|
|
87
89
|
img_full_path,
|
|
88
90
|
ret["sphr_dims"],
|
|
91
|
+
history,
|
|
89
92
|
)
|
|
90
93
|
if masks:
|
|
91
94
|
mymasks = _get_mask_names(img_full_path)
|
|
@@ -93,7 +96,7 @@ def _read_casa_image(
|
|
|
93
96
|
ary = _read_image_array(img_full_path, chunks, mask=m, verbose=verbose)
|
|
94
97
|
# data var names are all caps by convention
|
|
95
98
|
xds = _add_mask(xds, m.upper(), ary, dimorder)
|
|
96
|
-
xds.attrs = _casa_image_to_xds_attrs(img_full_path
|
|
99
|
+
xds.attrs = _casa_image_to_xds_attrs(img_full_path)
|
|
97
100
|
mb = _multibeam_array(xds, img_full_path, True)
|
|
98
101
|
if mb is not None:
|
|
99
102
|
xds["BEAM"] = mb
|
xradio/image/image.py
CHANGED
|
@@ -7,6 +7,9 @@ import warnings
|
|
|
7
7
|
from typing import List, Union
|
|
8
8
|
import copy
|
|
9
9
|
import numpy as np
|
|
10
|
+
import os
|
|
11
|
+
import shutil
|
|
12
|
+
import toolviper.utils.logger as logger
|
|
10
13
|
import xarray as xr
|
|
11
14
|
|
|
12
15
|
# from .._utils.zarr.common import _load_no_dask_zarr
|
|
@@ -182,27 +185,36 @@ def load_image(infile: str, block_des: dict = {}, do_sky_coords=True) -> xr.Data
|
|
|
182
185
|
|
|
183
186
|
|
|
184
187
|
def write_image(
|
|
185
|
-
xds: xr.Dataset, imagename: str, out_format: str = "casa", overwrite=False
|
|
188
|
+
xds: xr.Dataset, imagename: str, out_format: str = "casa", overwrite: bool = False
|
|
186
189
|
) -> None:
|
|
187
190
|
"""
|
|
188
191
|
Convert an xds image to CASA or zarr image.
|
|
189
192
|
xds : xarray.Dataset
|
|
190
193
|
XDS to convert
|
|
191
194
|
imagename : str
|
|
192
|
-
Path to output
|
|
195
|
+
Path to output image
|
|
193
196
|
out_format : str
|
|
194
197
|
Format of output image, currently "casa" and "zarr" are supported
|
|
198
|
+
overwrite : bool
|
|
199
|
+
If True, overwrite existing image. Default is False.
|
|
195
200
|
Returns
|
|
196
201
|
-------
|
|
197
202
|
None
|
|
198
203
|
"""
|
|
204
|
+
if os.path.exists(imagename):
|
|
205
|
+
if overwrite:
|
|
206
|
+
logger.warning(
|
|
207
|
+
f"Because overwrite=True, removing existing path {imagename}"
|
|
208
|
+
)
|
|
209
|
+
if os.path.isdir(imagename):
|
|
210
|
+
shutil.rmtree(imagename)
|
|
211
|
+
else:
|
|
212
|
+
os.remove(imagename)
|
|
213
|
+
else:
|
|
214
|
+
raise FileExistsError(
|
|
215
|
+
f"Path {imagename} already exists. Set overwrite=True to remove it."
|
|
216
|
+
)
|
|
199
217
|
my_format = out_format.lower()
|
|
200
|
-
|
|
201
|
-
if overwrite:
|
|
202
|
-
import os
|
|
203
|
-
|
|
204
|
-
os.system("rm -rf " + imagename)
|
|
205
|
-
|
|
206
218
|
if my_format == "casa":
|
|
207
219
|
_xds_to_casa_image(xds, imagename)
|
|
208
220
|
elif my_format == "zarr":
|
|
@@ -16,6 +16,7 @@ from xradio.measurement_set._utils._msv2.msv4_sub_xdss import (
|
|
|
16
16
|
create_pointing_xds,
|
|
17
17
|
create_system_calibration_xds,
|
|
18
18
|
create_weather_xds,
|
|
19
|
+
create_phased_array_xds,
|
|
19
20
|
)
|
|
20
21
|
from .msv4_info_dicts import create_info_dicts
|
|
21
22
|
from xradio.measurement_set.schema import MSV4_SCHEMA_VERSION
|
|
@@ -1243,6 +1244,14 @@ def convert_and_write_partition(
|
|
|
1243
1244
|
+ str(time.time() - start)
|
|
1244
1245
|
)
|
|
1245
1246
|
|
|
1247
|
+
# Create phased array xds
|
|
1248
|
+
phased_array_xds = create_phased_array_xds(
|
|
1249
|
+
in_file,
|
|
1250
|
+
ant_xds.antenna_name,
|
|
1251
|
+
ant_xds.receptor_label,
|
|
1252
|
+
ant_xds.polarization_type,
|
|
1253
|
+
)
|
|
1254
|
+
|
|
1246
1255
|
start = time.time()
|
|
1247
1256
|
|
|
1248
1257
|
# Time and frequency should always be increasing
|
|
@@ -1358,6 +1367,9 @@ def convert_and_write_partition(
|
|
|
1358
1367
|
if weather_xds:
|
|
1359
1368
|
ms_xdt["/weather_xds"] = weather_xds
|
|
1360
1369
|
|
|
1370
|
+
if phased_array_xds:
|
|
1371
|
+
ms_xdt["/phased_array_xds"] = phased_array_xds
|
|
1372
|
+
|
|
1361
1373
|
if storage_backend == "zarr":
|
|
1362
1374
|
ms_xdt.to_zarr(store=os.path.join(out_file, ms_v4_name))
|
|
1363
1375
|
elif storage_backend == "netcdf":
|
|
@@ -17,7 +17,8 @@ from xradio.measurement_set._utils._msv2._tables.read import (
|
|
|
17
17
|
make_taql_where_between_min_max,
|
|
18
18
|
load_generic_table,
|
|
19
19
|
)
|
|
20
|
-
from xradio._utils.list_and_array import cast_to_str
|
|
20
|
+
from xradio._utils.list_and_array import cast_to_str, get_pad_value
|
|
21
|
+
|
|
21
22
|
from xradio._utils.coord_math import (
|
|
22
23
|
convert_to_si_units,
|
|
23
24
|
add_position_offsets,
|
|
@@ -547,10 +548,16 @@ def pad_missing_sources(
|
|
|
547
548
|
for source_id in unique_source_ids
|
|
548
549
|
if source_id not in source_xds.coords["SOURCE_ID"]
|
|
549
550
|
]
|
|
551
|
+
if len(missing_source_ids) < 1:
|
|
552
|
+
return source_xds
|
|
550
553
|
|
|
551
554
|
# would like to use the new-ish xr.pad, but it creates issues with indices/coords and is
|
|
552
555
|
# also not free of overheads, as it for example changes all numeric types to float64
|
|
553
|
-
|
|
556
|
+
fill_value = {
|
|
557
|
+
var_name: get_pad_value(var.dtype)
|
|
558
|
+
for var_name, var in source_xds.data_vars.items()
|
|
559
|
+
}
|
|
560
|
+
missing_source_xds = xr.full_like(source_xds.isel(SOURCE_ID=0), fill_value)
|
|
554
561
|
pad_str = "Unknown"
|
|
555
562
|
pad_str_type = "<U9"
|
|
556
563
|
for var in missing_source_xds.data_vars:
|