xradio 0.0.56__py3-none-any.whl → 0.0.58__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/__init__.py +2 -2
- xradio/_utils/_casacore/casacore_from_casatools.py +12 -2
- xradio/_utils/_casacore/tables.py +1 -0
- xradio/_utils/coord_math.py +22 -23
- xradio/_utils/dict_helpers.py +76 -11
- xradio/_utils/schema.py +5 -2
- xradio/_utils/zarr/common.py +1 -73
- xradio/image/_util/_casacore/xds_from_casacore.py +49 -33
- xradio/image/_util/_casacore/xds_to_casacore.py +41 -14
- xradio/image/_util/_fits/xds_from_fits.py +146 -35
- xradio/image/_util/casacore.py +4 -3
- xradio/image/_util/common.py +4 -4
- xradio/image/_util/image_factory.py +8 -8
- xradio/image/image.py +45 -5
- xradio/measurement_set/__init__.py +19 -9
- xradio/measurement_set/_utils/__init__.py +1 -3
- xradio/measurement_set/_utils/_msv2/__init__.py +0 -0
- xradio/measurement_set/_utils/_msv2/_tables/read.py +17 -76
- xradio/measurement_set/_utils/_msv2/_tables/read_main_table.py +2 -685
- xradio/measurement_set/_utils/_msv2/conversion.py +123 -145
- xradio/measurement_set/_utils/_msv2/create_antenna_xds.py +9 -16
- xradio/measurement_set/_utils/_msv2/create_field_and_source_xds.py +125 -221
- xradio/measurement_set/_utils/_msv2/msv2_to_msv4_meta.py +1 -2
- xradio/measurement_set/_utils/_msv2/msv4_info_dicts.py +8 -7
- xradio/measurement_set/_utils/_msv2/msv4_sub_xdss.py +27 -72
- xradio/measurement_set/_utils/_msv2/partition_queries.py +1 -261
- xradio/measurement_set/_utils/_msv2/subtables.py +0 -107
- xradio/measurement_set/_utils/_utils/interpolate.py +60 -0
- xradio/measurement_set/_utils/_zarr/encoding.py +2 -7
- xradio/measurement_set/convert_msv2_to_processing_set.py +0 -2
- xradio/measurement_set/load_processing_set.py +2 -2
- xradio/measurement_set/measurement_set_xdt.py +14 -14
- xradio/measurement_set/open_processing_set.py +1 -3
- xradio/measurement_set/processing_set_xdt.py +41 -835
- xradio/measurement_set/schema.py +95 -122
- xradio/schema/check.py +91 -97
- xradio/schema/dataclass.py +159 -22
- xradio/schema/export.py +99 -0
- xradio/schema/metamodel.py +51 -16
- xradio/schema/typing.py +5 -5
- {xradio-0.0.56.dist-info → xradio-0.0.58.dist-info}/METADATA +2 -1
- xradio-0.0.58.dist-info/RECORD +65 -0
- {xradio-0.0.56.dist-info → xradio-0.0.58.dist-info}/WHEEL +1 -1
- xradio/image/_util/fits.py +0 -13
- xradio/measurement_set/_utils/_msv2/_tables/load.py +0 -66
- xradio/measurement_set/_utils/_msv2/_tables/load_main_table.py +0 -490
- xradio/measurement_set/_utils/_msv2/_tables/read_subtables.py +0 -398
- xradio/measurement_set/_utils/_msv2/_tables/write.py +0 -323
- xradio/measurement_set/_utils/_msv2/_tables/write_exp_api.py +0 -388
- xradio/measurement_set/_utils/_msv2/chunks.py +0 -115
- xradio/measurement_set/_utils/_msv2/descr.py +0 -165
- xradio/measurement_set/_utils/_msv2/msv2_msv3.py +0 -7
- xradio/measurement_set/_utils/_msv2/partitions.py +0 -392
- xradio/measurement_set/_utils/_utils/cds.py +0 -40
- xradio/measurement_set/_utils/_utils/xds_helper.py +0 -404
- xradio/measurement_set/_utils/_zarr/read.py +0 -263
- xradio/measurement_set/_utils/_zarr/write.py +0 -329
- xradio/measurement_set/_utils/msv2.py +0 -106
- xradio/measurement_set/_utils/zarr.py +0 -133
- xradio-0.0.56.dist-info/RECORD +0 -78
- {xradio-0.0.56.dist-info → xradio-0.0.58.dist-info}/licenses/LICENSE.txt +0 -0
- {xradio-0.0.56.dist-info → xradio-0.0.58.dist-info}/top_level.txt +0 -0
|
@@ -1,398 +0,0 @@
|
|
|
1
|
-
import toolviper.utils.logger as logger
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
from typing import Dict, Tuple, Union
|
|
4
|
-
|
|
5
|
-
import dask
|
|
6
|
-
import numpy as np
|
|
7
|
-
import pandas as pd
|
|
8
|
-
import xarray as xr
|
|
9
|
-
|
|
10
|
-
try:
|
|
11
|
-
from casacore import tables
|
|
12
|
-
except ImportError:
|
|
13
|
-
import xradio._utils._casacore.casacore_from_casatools as tables
|
|
14
|
-
|
|
15
|
-
from .table_query import open_query, open_table_ro
|
|
16
|
-
from .read import (
|
|
17
|
-
read_col_chunk,
|
|
18
|
-
convert_casacore_time,
|
|
19
|
-
extract_table_attributes,
|
|
20
|
-
add_units_measures,
|
|
21
|
-
table_exists,
|
|
22
|
-
load_generic_table,
|
|
23
|
-
)
|
|
24
|
-
from .write import revert_time
|
|
25
|
-
from xradio._utils.list_and_array import unique_1d
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def read_ephemerides(
|
|
29
|
-
infile: str,
|
|
30
|
-
) -> Union[xr.Dataset, None]:
|
|
31
|
-
"""
|
|
32
|
-
Read ephemerides info from MSv2 FIELD/EPHEMi_....tab subtables
|
|
33
|
-
|
|
34
|
-
Parameters
|
|
35
|
-
----------
|
|
36
|
-
infile : str
|
|
37
|
-
path to MS
|
|
38
|
-
|
|
39
|
-
Returns
|
|
40
|
-
-------
|
|
41
|
-
Union[xr.Dataset, None]
|
|
42
|
-
ephemerides xds with metainfo as in the MSv3/EPHEMERIDES subtable
|
|
43
|
-
"""
|
|
44
|
-
field_subt = Path(infile, "FIELD")
|
|
45
|
-
subdirs = [
|
|
46
|
-
sdir
|
|
47
|
-
for sdir in field_subt.iterdir()
|
|
48
|
-
if "EPHEM" in sdir.name and sdir.is_dir() and table_exists(str(sdir))
|
|
49
|
-
]
|
|
50
|
-
ephem = []
|
|
51
|
-
for sdir in subdirs:
|
|
52
|
-
logger.debug(f"Reading ephemerides info from: FIELD / {sdir.name}")
|
|
53
|
-
# One "EPHEM_*.tab" (each with a difference ephemeris_id) to concatenate
|
|
54
|
-
ephem.append(
|
|
55
|
-
load_generic_table(infile, str(Path(*sdir.parts[-2:])), timecols=["MJD"])
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
if ephem:
|
|
59
|
-
ephem = xr.concat(ephem, dim="ephemeris_id")
|
|
60
|
-
else:
|
|
61
|
-
ephem = None
|
|
62
|
-
|
|
63
|
-
return ephem
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def read_delayed_pointing_table(
|
|
67
|
-
infile: str,
|
|
68
|
-
rename_ids: Dict[str, str] = None,
|
|
69
|
-
chunks: Tuple = (10000, 100, 2, 20),
|
|
70
|
-
time_slice=None,
|
|
71
|
-
) -> xr.Dataset:
|
|
72
|
-
"""
|
|
73
|
-
Read MS pointing subtable in delayed arrays into an xr.Dataset
|
|
74
|
-
|
|
75
|
-
Parameters
|
|
76
|
-
----------
|
|
77
|
-
infile : str
|
|
78
|
-
path to pointing table
|
|
79
|
-
rename_ids : Dict[str, str] (Default value = None)
|
|
80
|
-
dict with dimension renaming mapping
|
|
81
|
-
chunks : Tuple (Default value = (10000, 100, 2, 20))
|
|
82
|
-
chunks for the arrays. Chunks tuple: time, antenna, data_vars_dim_1, data_vars_dim_2
|
|
83
|
-
time_slice: slice
|
|
84
|
-
time bounds
|
|
85
|
-
|
|
86
|
-
Returns
|
|
87
|
-
-------
|
|
88
|
-
xr.Dataset
|
|
89
|
-
pointing dataset
|
|
90
|
-
"""
|
|
91
|
-
|
|
92
|
-
with open_table_ro(infile) as mtable:
|
|
93
|
-
taql_time = ""
|
|
94
|
-
if time_slice:
|
|
95
|
-
times = normalize_time_slice(mtable, time_slice)
|
|
96
|
-
taql_time = f"where TIME BETWEEN {times.start} AND {times.stop}"
|
|
97
|
-
else:
|
|
98
|
-
times = None
|
|
99
|
-
taql_all = f"select * from $mtable {taql_time}"
|
|
100
|
-
with open_query(mtable, taql_all) as query_all:
|
|
101
|
-
if query_all.nrows() == 0:
|
|
102
|
-
mtable.close()
|
|
103
|
-
note = ""
|
|
104
|
-
if taql_time:
|
|
105
|
-
note = (
|
|
106
|
-
" within the selected time range: {times.start} - {times.stop}"
|
|
107
|
-
)
|
|
108
|
-
logger.warning(f"POINTING subtable has no data{note}")
|
|
109
|
-
return xr.Dataset()
|
|
110
|
-
|
|
111
|
-
# pointing table uses time x antenna_id
|
|
112
|
-
antennas = unique_1d(query_all.getcol("ANTENNA_ID", 0, -1))
|
|
113
|
-
taql_times = f"select DISTINCT TIME from $mtable {taql_time}"
|
|
114
|
-
with open_query(None, taql_times) as query_times:
|
|
115
|
-
utimes = unique_1d(query_times.getcol("TIME", 0, -1))
|
|
116
|
-
|
|
117
|
-
tvars = read_delayed_pointing_times(
|
|
118
|
-
infile, antennas, utimes, chunks, query_all, times
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
dims = ["time", "antenna_id", "dim_2", "dim_3"]
|
|
122
|
-
|
|
123
|
-
# now concat all the dask chunks from each time
|
|
124
|
-
mvars = {}
|
|
125
|
-
for var in tvars.keys():
|
|
126
|
-
mvars[var] = xr.DataArray(
|
|
127
|
-
dask.array.concatenate(tvars[var], axis=0),
|
|
128
|
-
dims=dims[: len(tvars[var][0].shape)],
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
mcoords = {}
|
|
132
|
-
mcoords["time"] = xr.DataArray(convert_casacore_time(utimes), dims=["time"])
|
|
133
|
-
mcoords["antenna_id"] = xr.DataArray(np.arange(len(antennas)), dims=["antenna_id"])
|
|
134
|
-
|
|
135
|
-
cc_attrs = extract_table_attributes(infile)
|
|
136
|
-
attrs = {"other": {"msv2": {"ctds_attrs": cc_attrs}}}
|
|
137
|
-
mvars = add_units_measures(mvars, cc_attrs)
|
|
138
|
-
mcoords = add_units_measures(mcoords, cc_attrs)
|
|
139
|
-
|
|
140
|
-
xds = xr.Dataset(mvars, coords=mcoords)
|
|
141
|
-
if rename_ids:
|
|
142
|
-
rename_ids = {k: v for k, v in rename_ids.items() if k in xds.sizes}
|
|
143
|
-
xds = xds.rename_dims(rename_ids)
|
|
144
|
-
xds = xds.assign_attrs(attrs)
|
|
145
|
-
|
|
146
|
-
return xds
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
def normalize_time_slice(mtable: tables.table, time_slice: slice) -> slice:
|
|
150
|
-
"""
|
|
151
|
-
If we get indices, produce the TIME column time value for the
|
|
152
|
-
start/top indices. If we get timestamps, convert them to casacore
|
|
153
|
-
refeference.
|
|
154
|
-
|
|
155
|
-
Parameters
|
|
156
|
-
----------
|
|
157
|
-
mtable : tables.table
|
|
158
|
-
a casacore table from which we are reading a TIME
|
|
159
|
-
column
|
|
160
|
-
time_slice : slice
|
|
161
|
-
slice giving start/stop time. Can be given as
|
|
162
|
-
integer indices or as timestamps (Xarray / pandas reference)
|
|
163
|
-
|
|
164
|
-
Returns
|
|
165
|
-
-------
|
|
166
|
-
slice
|
|
167
|
-
a (start, stop) slice with times in casacore ref frame
|
|
168
|
-
"""
|
|
169
|
-
if type(time_slice.start) == pd.Timestamp and type(time_slice.stop) == pd.Timestamp:
|
|
170
|
-
# Add tol?
|
|
171
|
-
eps = np.finfo(float).eps
|
|
172
|
-
times = slice(
|
|
173
|
-
revert_time(time_slice.start) - eps, revert_time(time_slice.stop) + eps
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
elif (
|
|
177
|
-
int(time_slice.start) == time_slice.start
|
|
178
|
-
and int(time_slice.stop) == time_slice.stop
|
|
179
|
-
):
|
|
180
|
-
# instead of int cast could be operator.index(time_slice.start)
|
|
181
|
-
taql_utimes = "select DISTINCT TIME from $mtable"
|
|
182
|
-
with open_query(mtable, taql_utimes) as query_utimes:
|
|
183
|
-
utimes = unique_1d(query_utimes.getcol("TIME", 0, -1))
|
|
184
|
-
# add a tol around the time ranges returned by taql
|
|
185
|
-
if len(utimes) < 2:
|
|
186
|
-
tol = 1e-5
|
|
187
|
-
else:
|
|
188
|
-
tol = np.diff(utimes).min() / 4
|
|
189
|
-
|
|
190
|
-
nutimes = len(utimes)
|
|
191
|
-
if nutimes == 0:
|
|
192
|
-
times = slice(0, 0)
|
|
193
|
-
else:
|
|
194
|
-
tidxs = slice(
|
|
195
|
-
min(nutimes, int(time_slice.start)),
|
|
196
|
-
min(nutimes, int(time_slice.stop)) - 1,
|
|
197
|
-
)
|
|
198
|
-
times = slice(utimes[tidxs.start] - tol, utimes[tidxs.stop] + tol)
|
|
199
|
-
|
|
200
|
-
else:
|
|
201
|
-
raise ValueError(
|
|
202
|
-
f"Invalid time type. Not a timestamp and cannot use as"
|
|
203
|
-
f" index: {time_slice.start} (type: {type(time_slice.start)})"
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
return times
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
def read_delayed_pointing_times(
|
|
210
|
-
infile: str,
|
|
211
|
-
antennas: np.ndarray,
|
|
212
|
-
utimes: np.array,
|
|
213
|
-
chunks: tuple,
|
|
214
|
-
query_all: tables.table,
|
|
215
|
-
time_slice: slice,
|
|
216
|
-
) -> Dict[str, xr.DataArray]:
|
|
217
|
-
"""
|
|
218
|
-
Read pointing table in delayed time / antenna chunks. Loops over
|
|
219
|
-
time chunks
|
|
220
|
-
|
|
221
|
-
Parameters
|
|
222
|
-
----------
|
|
223
|
-
infile : str
|
|
224
|
-
path to pointing table
|
|
225
|
-
antennas : np.ndarray
|
|
226
|
-
antenna ids
|
|
227
|
-
utimes : np.ndarray
|
|
228
|
-
unique times from table
|
|
229
|
-
chunks : tuple
|
|
230
|
-
chunks for the arrays
|
|
231
|
-
query_all : tables.table
|
|
232
|
-
table to read columns
|
|
233
|
-
time_slice: slice :
|
|
234
|
-
time bounds
|
|
235
|
-
|
|
236
|
-
Returns
|
|
237
|
-
-------
|
|
238
|
-
Dict[str, xr.DataArray]
|
|
239
|
-
dictionary of columns=>variables (read as dask.delayed)
|
|
240
|
-
"""
|
|
241
|
-
|
|
242
|
-
antenna_chunks = range(0, len(antennas), chunks[1])
|
|
243
|
-
|
|
244
|
-
# loop over time chunks
|
|
245
|
-
if time_slice:
|
|
246
|
-
time_chunks = [0]
|
|
247
|
-
logger.debug(
|
|
248
|
-
f"reading single chunk from pointing, with times {time_slice.start} - {time_slice.stop}"
|
|
249
|
-
)
|
|
250
|
-
else:
|
|
251
|
-
time_chunks = range(0, len(utimes), chunks[0])
|
|
252
|
-
logger.debug(
|
|
253
|
-
f"reading pointing table into {len(time_chunks)} time x {len(antenna_chunks)} antenna chunks"
|
|
254
|
-
)
|
|
255
|
-
|
|
256
|
-
tvars = {}
|
|
257
|
-
for tc in time_chunks:
|
|
258
|
-
bvars = read_delayed_pointing_chunks(
|
|
259
|
-
infile, antennas, chunks, utimes, tc, query_all
|
|
260
|
-
)
|
|
261
|
-
|
|
262
|
-
# now concat all the dask chunks from each antenna
|
|
263
|
-
for var in bvars.keys():
|
|
264
|
-
if len(bvars[var]) == 0:
|
|
265
|
-
continue
|
|
266
|
-
if var not in tvars:
|
|
267
|
-
tvars[var] = []
|
|
268
|
-
tvars[var] += [dask.array.concatenate(bvars[var], axis=1)]
|
|
269
|
-
|
|
270
|
-
return tvars
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
def read_delayed_pointing_chunks(
|
|
274
|
-
infile: str,
|
|
275
|
-
antennas: np.ndarray,
|
|
276
|
-
chunks: tuple,
|
|
277
|
-
utimes: np.ndarray,
|
|
278
|
-
tc: int,
|
|
279
|
-
tb_tool: tables.table,
|
|
280
|
-
) -> Dict[str, xr.DataArray]:
|
|
281
|
-
"""
|
|
282
|
-
For one time chunk, read the baseline/antenna chunks. Loops over
|
|
283
|
-
antenna_id chunks and reads all columns as dask.delayed calls.
|
|
284
|
-
|
|
285
|
-
Parameters
|
|
286
|
-
----------
|
|
287
|
-
infile : str
|
|
288
|
-
path to pointing table
|
|
289
|
-
antennas : np.ndarray
|
|
290
|
-
antenna ids
|
|
291
|
-
chunks : tuple
|
|
292
|
-
chunks for the arrays
|
|
293
|
-
utimes : np.ndarray
|
|
294
|
-
unique times from table
|
|
295
|
-
tc : int
|
|
296
|
-
time index
|
|
297
|
-
tb_tool : tables.table
|
|
298
|
-
table to read columns
|
|
299
|
-
|
|
300
|
-
Returns
|
|
301
|
-
-------
|
|
302
|
-
Dict[str, xr.DataArray]
|
|
303
|
-
dictionary of columns=>variables (read as dask.delayed)
|
|
304
|
-
"""
|
|
305
|
-
|
|
306
|
-
# add a tol around the time ranges returned by taql, for the next taql queries
|
|
307
|
-
if len(utimes) < 2:
|
|
308
|
-
tol = 1e-5
|
|
309
|
-
else:
|
|
310
|
-
tol = np.diff(utimes).min() / 4
|
|
311
|
-
|
|
312
|
-
times = (
|
|
313
|
-
utimes[tc] - tol,
|
|
314
|
-
utimes[min(len(utimes) - 1, tc + chunks[0] - 1)] + tol,
|
|
315
|
-
)
|
|
316
|
-
ctlen = min(len(utimes), tc + chunks[0]) - tc # chunk time length
|
|
317
|
-
|
|
318
|
-
antenna_chunks = range(0, len(antennas), chunks[1])
|
|
319
|
-
cols = tb_tool.colnames()
|
|
320
|
-
|
|
321
|
-
bvars = {}
|
|
322
|
-
for bc in antenna_chunks:
|
|
323
|
-
blines = (
|
|
324
|
-
antennas[bc],
|
|
325
|
-
antennas[min(len(antennas) - 1, bc + chunks[1] - 1)],
|
|
326
|
-
)
|
|
327
|
-
cblen = min(len(antennas) - bc, chunks[1])
|
|
328
|
-
|
|
329
|
-
# read the specified chunk of data
|
|
330
|
-
ttql = "TIME BETWEEN %f and %f" % times
|
|
331
|
-
atql = "ANTENNA_ID BETWEEN %i and %i" % blines
|
|
332
|
-
ts_taql = f"select * from $mtable where {ttql} AND {atql}"
|
|
333
|
-
with open_query(None, ts_taql) as ts_tb:
|
|
334
|
-
tidxs = np.searchsorted(utimes, ts_tb.getcol("TIME", 0, -1)) - tc
|
|
335
|
-
bidxs = np.searchsorted(antennas, ts_tb.getcol("ANTENNA_ID", 0, -1)) - bc
|
|
336
|
-
didxs = np.arange(len(bidxs))
|
|
337
|
-
|
|
338
|
-
# loop over each column and create delayed dask arrays
|
|
339
|
-
for col in cols:
|
|
340
|
-
if (col in ["TIME", "ANTENNA_ID"]) or (
|
|
341
|
-
not tb_tool.iscelldefined(col, 0)
|
|
342
|
-
):
|
|
343
|
-
continue
|
|
344
|
-
if col not in bvars:
|
|
345
|
-
bvars[col] = []
|
|
346
|
-
|
|
347
|
-
cdata = tb_tool.getcol(col, 0, 1)[0]
|
|
348
|
-
if isinstance(cdata, str):
|
|
349
|
-
cdata = np.array(cdata)
|
|
350
|
-
if len(cdata.shape) == 0:
|
|
351
|
-
delayed_array = dask.delayed(read_col_chunk)(
|
|
352
|
-
infile,
|
|
353
|
-
ts_taql,
|
|
354
|
-
col,
|
|
355
|
-
(ctlen, cblen),
|
|
356
|
-
tidxs,
|
|
357
|
-
bidxs,
|
|
358
|
-
didxs,
|
|
359
|
-
None,
|
|
360
|
-
None,
|
|
361
|
-
)
|
|
362
|
-
bvars[col] += [
|
|
363
|
-
dask.array.from_delayed(
|
|
364
|
-
delayed_array, (ctlen, cblen), cdata.dtype
|
|
365
|
-
)
|
|
366
|
-
]
|
|
367
|
-
|
|
368
|
-
elif len(cdata.shape) == 2:
|
|
369
|
-
d1_list = []
|
|
370
|
-
for cc in range(0, cdata.shape[0], chunks[2]):
|
|
371
|
-
d1s = (cc, min(cdata.shape[0], cc + chunks[2]) - 1)
|
|
372
|
-
d2_list = []
|
|
373
|
-
for pc in range(0, cdata.shape[1], chunks[3]):
|
|
374
|
-
d2s = (pc, min(cdata.shape[1], pc + chunks[3]) - 1)
|
|
375
|
-
cshape = (
|
|
376
|
-
ctlen,
|
|
377
|
-
cblen,
|
|
378
|
-
) + (d1s[1] - d1s[0] + 1, d2s[1] - d2s[0] + 1)
|
|
379
|
-
delayed_array = dask.delayed(read_col_chunk)(
|
|
380
|
-
infile,
|
|
381
|
-
ts_taql,
|
|
382
|
-
col,
|
|
383
|
-
cshape,
|
|
384
|
-
tidxs,
|
|
385
|
-
bidxs,
|
|
386
|
-
didxs,
|
|
387
|
-
d1s,
|
|
388
|
-
d2s,
|
|
389
|
-
)
|
|
390
|
-
d2_list += [
|
|
391
|
-
dask.array.from_delayed(
|
|
392
|
-
delayed_array, cshape, cdata.dtype
|
|
393
|
-
)
|
|
394
|
-
]
|
|
395
|
-
d1_list += [dask.array.concatenate(d2_list, axis=3)]
|
|
396
|
-
bvars[col] += [dask.array.concatenate(d1_list, axis=2)]
|
|
397
|
-
|
|
398
|
-
return bvars
|
|
@@ -1,323 +0,0 @@
|
|
|
1
|
-
import toolviper.utils.logger as logger, os
|
|
2
|
-
from typing import Tuple
|
|
3
|
-
|
|
4
|
-
import numpy as np
|
|
5
|
-
import xarray as xr
|
|
6
|
-
|
|
7
|
-
try:
|
|
8
|
-
from casacore import tables
|
|
9
|
-
except ImportError:
|
|
10
|
-
import xradio._utils._casacore.casacore_from_casatools as tables
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def revert_time(datetimes: np.ndarray) -> np.ndarray:
|
|
14
|
-
"""
|
|
15
|
-
Convert time back from pandas datetime ref to casacore ref
|
|
16
|
-
(reverse of read.convert_casacore_time).
|
|
17
|
-
|
|
18
|
-
Parameters
|
|
19
|
-
----------
|
|
20
|
-
datetimes : np.ndarray
|
|
21
|
-
times in pandas reference
|
|
22
|
-
|
|
23
|
-
Returns
|
|
24
|
-
-------
|
|
25
|
-
np.ndarray
|
|
26
|
-
times converted to casacore reference
|
|
27
|
-
|
|
28
|
-
"""
|
|
29
|
-
return (datetimes.astype(float) / 10**9) + 3506716800.0
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
#####################################
|
|
33
|
-
# translate numpy dtypes to casacore type strings
|
|
34
|
-
def type_converter(npdtype: str) -> str:
|
|
35
|
-
cctype = "bad"
|
|
36
|
-
if (npdtype == "int64") or (npdtype == "int32"):
|
|
37
|
-
cctype = "int"
|
|
38
|
-
elif npdtype == "bool":
|
|
39
|
-
cctype = "bool"
|
|
40
|
-
elif npdtype == "float32":
|
|
41
|
-
cctype = "float"
|
|
42
|
-
elif (npdtype == "float64") or (npdtype == "datetime64[ns]"):
|
|
43
|
-
cctype = "double"
|
|
44
|
-
elif npdtype == "complex64":
|
|
45
|
-
cctype = "complex"
|
|
46
|
-
elif npdtype == "complex128":
|
|
47
|
-
cctype = "dcomplex"
|
|
48
|
-
elif str(npdtype).startswith("<U"):
|
|
49
|
-
cctype = "string"
|
|
50
|
-
|
|
51
|
-
return cctype
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
####################################
|
|
55
|
-
# create and initialize new output table
|
|
56
|
-
def create_table(
|
|
57
|
-
outfile: str,
|
|
58
|
-
xds: xr.Dataset,
|
|
59
|
-
max_rows: int,
|
|
60
|
-
infile=None,
|
|
61
|
-
cols=None,
|
|
62
|
-
generic=False,
|
|
63
|
-
):
|
|
64
|
-
if os.path.isdir(outfile):
|
|
65
|
-
os.system("rm -fr %s" % outfile)
|
|
66
|
-
|
|
67
|
-
# create column descriptions for table description
|
|
68
|
-
ctds_attrs = {}
|
|
69
|
-
try:
|
|
70
|
-
ctds_attrs = xds.attrs["other"]["msv2"]["ctds_attrs"]
|
|
71
|
-
except KeyError as exc:
|
|
72
|
-
pass
|
|
73
|
-
|
|
74
|
-
if cols is None:
|
|
75
|
-
if ctds_attrs and "column_descriptions" in ctds_attrs:
|
|
76
|
-
cols = {col: col for col in ctds_attrs["column_descriptions"]}
|
|
77
|
-
else:
|
|
78
|
-
cols = {var: var for var in xds.data_vars}
|
|
79
|
-
# Would add all xds data vars regardless of description availability
|
|
80
|
-
# +
|
|
81
|
-
# list(xds.data_vars) +
|
|
82
|
-
|
|
83
|
-
tabledesc = {}
|
|
84
|
-
for col, var_name in cols.items():
|
|
85
|
-
if ("column_descriptions" in ctds_attrs) and (
|
|
86
|
-
col in ctds_attrs["column_descriptions"]
|
|
87
|
-
):
|
|
88
|
-
coldesc = ctds_attrs["column_descriptions"][col]
|
|
89
|
-
# col not in ignore_msv2_cols
|
|
90
|
-
if (
|
|
91
|
-
not generic
|
|
92
|
-
and "DATA" in col
|
|
93
|
-
and "shape" not in coldesc
|
|
94
|
-
and var_name in xds.data_vars
|
|
95
|
-
):
|
|
96
|
-
coldesc["shape"] = tuple(np.clip(xds[var_name].shape[1:], 1, None))
|
|
97
|
-
|
|
98
|
-
if col == "UVW" or (
|
|
99
|
-
(not "shape" in coldesc or type(coldesc["shape"]) == str)
|
|
100
|
-
and var_name in xds.data_vars
|
|
101
|
-
):
|
|
102
|
-
coldesc["shape"] = tuple(np.clip(xds[var_name].shape[1:], 1, None))
|
|
103
|
-
else:
|
|
104
|
-
coldesc = {"valueType": type_converter(xds[col].dtype)}
|
|
105
|
-
if generic or (
|
|
106
|
-
col == "UVW" or col == "DATA"
|
|
107
|
-
): # will be statically shaped even if not originally
|
|
108
|
-
coldesc = {"shape": tuple(np.clip(xds[col].shape[1:], 1, None))}
|
|
109
|
-
elif xds[col].ndim > 1: # make variably shaped
|
|
110
|
-
coldesc = {"ndim": xds[col].ndim - 1}
|
|
111
|
-
coldesc["name"] = col
|
|
112
|
-
coldesc["desc"] = col
|
|
113
|
-
tabledesc[col] = coldesc
|
|
114
|
-
|
|
115
|
-
# fix the fun set of edge cases from casatestdata that cause errors
|
|
116
|
-
if (
|
|
117
|
-
"dataManagerType" in tabledesc[col]
|
|
118
|
-
and tabledesc[col]["dataManagerType"] == "TiledShapeStMan"
|
|
119
|
-
) and (tabledesc[col]["ndim"] == 1):
|
|
120
|
-
tabledesc[col]["dataManagerType"] = ""
|
|
121
|
-
|
|
122
|
-
if generic:
|
|
123
|
-
tb_tool = tables.table(
|
|
124
|
-
outfile,
|
|
125
|
-
tabledesc=tabledesc,
|
|
126
|
-
nrow=max_rows,
|
|
127
|
-
readonly=False,
|
|
128
|
-
lockoptions={"option": "permanentwait"},
|
|
129
|
-
ack=False,
|
|
130
|
-
)
|
|
131
|
-
else:
|
|
132
|
-
tb_tool = tables.default_ms(outfile, tabledesc)
|
|
133
|
-
tb_tool.addrows(max_rows)
|
|
134
|
-
# if 'DATA_DESC_ID' in cols: tb_tool.putcol('DATA_DESC_ID',
|
|
135
|
-
# np.zeros((max_rows), dtype='int32') - 1, 0, max_rows)
|
|
136
|
-
|
|
137
|
-
# write xds attributes to table keywords, skipping certain reserved attributes
|
|
138
|
-
existing_keywords = tb_tool.getkeywords()
|
|
139
|
-
for attr in ctds_attrs:
|
|
140
|
-
if attr in [
|
|
141
|
-
"other",
|
|
142
|
-
"history",
|
|
143
|
-
"info",
|
|
144
|
-
] + list(existing_keywords.keys()):
|
|
145
|
-
continue
|
|
146
|
-
tb_tool.putkeyword(attr, ctds_attrs[attr])
|
|
147
|
-
if "info" in ctds_attrs:
|
|
148
|
-
tb_tool.putinfo(ctds_attrs["info"])
|
|
149
|
-
|
|
150
|
-
# copy subtables and add to main table
|
|
151
|
-
if infile:
|
|
152
|
-
subtables = [
|
|
153
|
-
ss.path
|
|
154
|
-
for ss in os.scandir(infile)
|
|
155
|
-
if ss.is_dir() and ("SORTED_TABLE" not in ss.path)
|
|
156
|
-
]
|
|
157
|
-
os.system("cp -r %s %s" % (" ".join(subtables), outfile))
|
|
158
|
-
for subtable in subtables:
|
|
159
|
-
if not tables.tableexists(
|
|
160
|
-
os.path.join(outfile, subtable[subtable.rindex("/") + 1 :])
|
|
161
|
-
):
|
|
162
|
-
continue
|
|
163
|
-
sub_tbl = tables.table(
|
|
164
|
-
os.path.join(outfile, subtable[subtable.rindex("/") + 1 :]),
|
|
165
|
-
readonly=False,
|
|
166
|
-
lockoptions={"option": "permanentwait"},
|
|
167
|
-
ack=False,
|
|
168
|
-
)
|
|
169
|
-
tb_tool.putkeyword(
|
|
170
|
-
subtable[subtable.rindex("/") + 1 :], sub_tbl, makesubrecord=True
|
|
171
|
-
)
|
|
172
|
-
sub_tbl.close()
|
|
173
|
-
|
|
174
|
-
tb_tool.close()
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
############################################################################
|
|
178
|
-
##
|
|
179
|
-
## write_generic_table() - write any xds to generic casacore table format
|
|
180
|
-
##
|
|
181
|
-
############################################################################
|
|
182
|
-
def write_generic_table(xds: xr.Dataset, outfile: str, subtable="", cols=None):
|
|
183
|
-
"""
|
|
184
|
-
Write generic xds contents back to casacore table format on disk
|
|
185
|
-
|
|
186
|
-
Parameters
|
|
187
|
-
----------
|
|
188
|
-
xds : xr.Dataset
|
|
189
|
-
|
|
190
|
-
outfile : str
|
|
191
|
-
|
|
192
|
-
subtable : str (Default value = "")
|
|
193
|
-
|
|
194
|
-
cols : List[str] (Default value = None)
|
|
195
|
-
|
|
196
|
-
Returns
|
|
197
|
-
-------
|
|
198
|
-
|
|
199
|
-
"""
|
|
200
|
-
outfile = os.path.expanduser(outfile)
|
|
201
|
-
logger.debug("writing {os.path.join(outfile, subtable)}")
|
|
202
|
-
|
|
203
|
-
try:
|
|
204
|
-
ctds_attrs = {}
|
|
205
|
-
ctds_attrs = xds.attrs["other"]["msv2"]["ctds_attrs"]
|
|
206
|
-
except KeyError as exc:
|
|
207
|
-
pass
|
|
208
|
-
|
|
209
|
-
if cols is None:
|
|
210
|
-
cols = {var.upper(): var for var in xds.data_vars}
|
|
211
|
-
cols.update({coo.upper(): coo for coo in xds.coords if coo not in xds.dims})
|
|
212
|
-
# Would add cols with a description regardless of presence in xds
|
|
213
|
-
# + (
|
|
214
|
-
# list(
|
|
215
|
-
# ctds_attrs["column_descriptions"].keys()
|
|
216
|
-
# if "column_descriptions" in ctds_attrs
|
|
217
|
-
# else []
|
|
218
|
-
# )
|
|
219
|
-
# )
|
|
220
|
-
max_rows = xds.row.shape[0] if "row" in xds.dims else 0
|
|
221
|
-
create_table(
|
|
222
|
-
os.path.join(outfile, subtable),
|
|
223
|
-
xds,
|
|
224
|
-
max_rows,
|
|
225
|
-
infile=None,
|
|
226
|
-
cols=cols,
|
|
227
|
-
generic=True,
|
|
228
|
-
)
|
|
229
|
-
|
|
230
|
-
tb_tool = tables.table(
|
|
231
|
-
os.path.join(outfile, subtable),
|
|
232
|
-
readonly=False,
|
|
233
|
-
lockoptions={"option": "permanentwait"},
|
|
234
|
-
ack=False,
|
|
235
|
-
)
|
|
236
|
-
try:
|
|
237
|
-
for dv, col in cols.items():
|
|
238
|
-
if (dv not in xds) or (np.prod(xds[dv].shape) == 0):
|
|
239
|
-
continue
|
|
240
|
-
values = (
|
|
241
|
-
xds[dv].values
|
|
242
|
-
if xds[dv].dtype != "datetime64[ns]"
|
|
243
|
-
else revert_time(xds[dv].values)
|
|
244
|
-
)
|
|
245
|
-
tb_tool.putcol(col, values, 0, values.shape[0], 1)
|
|
246
|
-
except Exception:
|
|
247
|
-
print(
|
|
248
|
-
"ERROR: exception in write generic table - %s, %s, %s, %s"
|
|
249
|
-
% (os.path.join(outfile, subtable), dv, str(values.shape), tb_tool.nrows())
|
|
250
|
-
)
|
|
251
|
-
|
|
252
|
-
# now we have to add this subtable to the main table keywords (assuming a main table already exists)
|
|
253
|
-
if len(subtable) > 0:
|
|
254
|
-
main_tbl = tables.table(
|
|
255
|
-
outfile, readonly=False, lockoptions={"option": "permanentwait"}, ack=False
|
|
256
|
-
)
|
|
257
|
-
main_tbl.putkeyword(subtable, tb_tool, makesubrecord=True)
|
|
258
|
-
main_tbl.done()
|
|
259
|
-
tb_tool.close()
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
###################################
|
|
263
|
-
# local helper
|
|
264
|
-
def write_main_table_slice(
|
|
265
|
-
xda: xr.DataArray,
|
|
266
|
-
outfile: str,
|
|
267
|
-
ddi: int,
|
|
268
|
-
col: str,
|
|
269
|
-
full_shape: Tuple,
|
|
270
|
-
starts: Tuple,
|
|
271
|
-
):
|
|
272
|
-
"""
|
|
273
|
-
Write an xds row chunk to the corresponding main table slice
|
|
274
|
-
|
|
275
|
-
Parameters
|
|
276
|
-
----------
|
|
277
|
-
xda : xr.DataArray
|
|
278
|
-
|
|
279
|
-
outfile : str
|
|
280
|
-
|
|
281
|
-
ddi : int
|
|
282
|
-
|
|
283
|
-
col : str
|
|
284
|
-
|
|
285
|
-
full_shape : Tuple
|
|
286
|
-
|
|
287
|
-
starts : Tuple
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
Returns
|
|
291
|
-
-------
|
|
292
|
-
|
|
293
|
-
"""
|
|
294
|
-
# trigger the DAG for this chunk and return values while the table is unlocked
|
|
295
|
-
values = xda.compute().values
|
|
296
|
-
if xda.dtype == "datetime64[ns]":
|
|
297
|
-
values = revert_time(values)
|
|
298
|
-
|
|
299
|
-
tbs = tables.table(
|
|
300
|
-
outfile, readonly=False, lockoptions={"option": "permanentwait"}, ack=True
|
|
301
|
-
)
|
|
302
|
-
|
|
303
|
-
# try:
|
|
304
|
-
if (
|
|
305
|
-
(values.ndim == 1) or (col == "UVW") or (values.shape[1:] == full_shape)
|
|
306
|
-
): # scalar columns
|
|
307
|
-
tbs.putcol(col, values, starts[0], len(values))
|
|
308
|
-
else:
|
|
309
|
-
if not tbs.iscelldefined(col, starts[0]):
|
|
310
|
-
tbs.putcell(col, starts[0] + np.arange(len(values)), np.zeros((full_shape)))
|
|
311
|
-
tbs.putcolslice(
|
|
312
|
-
col,
|
|
313
|
-
values,
|
|
314
|
-
starts[1 : values.ndim],
|
|
315
|
-
tuple(np.array(starts[1 : values.ndim]) + np.array(values.shape[1:]) - 1),
|
|
316
|
-
[],
|
|
317
|
-
starts[0],
|
|
318
|
-
len(values),
|
|
319
|
-
1,
|
|
320
|
-
)
|
|
321
|
-
# except:
|
|
322
|
-
# print("ERROR: write exception - %s, %s, %s" % (col, str(values.shape), str(starts)))
|
|
323
|
-
tbs.close()
|