tabsim 0.0.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.
- tabsim/__init__.py +0 -0
- tabsim/config.py +904 -0
- tabsim/dask/__init__.py +0 -0
- tabsim/dask/coordinates.py +469 -0
- tabsim/dask/extras.py +60 -0
- tabsim/dask/interferometry.py +572 -0
- tabsim/dask/observation.py +969 -0
- tabsim/data/EDA2.itrf.txt +256 -0
- tabsim/data/MWA.itrf.txt +128 -0
- tabsim/data/MeerKAT.itrf.txt +64 -0
- tabsim/data/__init__.py +0 -0
- tabsim/data/aux_data/calibrator.skymodel +2 -0
- tabsim/data/aux_data/norad_satellite.rfimodel +61711 -0
- tabsim/data/aux_data/satellite.ole +3 -0
- tabsim/data/aux_data/satellite.rfimodel +3 -0
- tabsim/data/aux_data/stationary.loc +4 -0
- tabsim/data/aux_data/stationary.rfimodel +4 -0
- tabsim/data/config_files/sim_config_base.yaml +172 -0
- tabsim/data/config_files/tab_config_base.yaml +86 -0
- tabsim/jax/__init__.py +0 -0
- tabsim/jax/coordinates.py +1082 -0
- tabsim/jax/interferometry.py +667 -0
- tabsim/jax/utils.py +90 -0
- tabsim/plot.py +323 -0
- tabsim/scripts/__init__.py +1 -0
- tabsim/scripts/ast_region.py +56 -0
- tabsim/scripts/flag_data.py +195 -0
- tabsim/scripts/sim_vis.py +136 -0
- tabsim/scripts/tle_sat_region.py +191 -0
- tabsim/sky.py +137 -0
- tabsim/tle.py +753 -0
- tabsim/tools.py +111 -0
- tabsim/write.py +715 -0
- tabsim-0.0.1.dist-info/METADATA +991 -0
- tabsim-0.0.1.dist-info/RECORD +39 -0
- tabsim-0.0.1.dist-info/WHEEL +5 -0
- tabsim-0.0.1.dist-info/entry_points.txt +5 -0
- tabsim-0.0.1.dist-info/licenses/LICENSE +674 -0
- tabsim-0.0.1.dist-info/top_level.txt +1 -0
tabsim/jax/utils.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Progress bar for JAX scan
|
|
2
|
+
# Credit to https://www.jeremiecoullon.com/2021/01/29/jax_progress_bar/
|
|
3
|
+
|
|
4
|
+
from jax import lax, jit
|
|
5
|
+
from jax.experimental import host_callback
|
|
6
|
+
from tqdm import tqdm
|
|
7
|
+
from collections.abc import Callable
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def jit_with_doc(func: Callable) -> Callable:
|
|
11
|
+
jit_func = jit(func)
|
|
12
|
+
jit_func.__doc__ = func.__doc__
|
|
13
|
+
return jit_func
|
|
14
|
+
|
|
15
|
+
jit_with_doc.__doc__ = jit.__doc__
|
|
16
|
+
|
|
17
|
+
def progress_bar_scan(num_samples: int, message=None):
|
|
18
|
+
"Progress bar for a JAX scan"
|
|
19
|
+
if message is None:
|
|
20
|
+
message = f"Running for {num_samples:,} iterations"
|
|
21
|
+
tqdm_bars = {}
|
|
22
|
+
if num_samples > 20:
|
|
23
|
+
print_rate = int(num_samples / 20)
|
|
24
|
+
else:
|
|
25
|
+
print_rate = 1 # if you run the sampler for less than 20 iterations
|
|
26
|
+
|
|
27
|
+
remainder = num_samples % print_rate
|
|
28
|
+
|
|
29
|
+
def _define_tqdm(arg, transform):
|
|
30
|
+
tqdm_bars[0] = tqdm(range(num_samples))
|
|
31
|
+
tqdm_bars[0].set_description(message, refresh=False)
|
|
32
|
+
|
|
33
|
+
def _update_tqdm(arg, transform):
|
|
34
|
+
tqdm_bars[0].update(arg)
|
|
35
|
+
|
|
36
|
+
def _update_progress_bar(iter_num):
|
|
37
|
+
"Updates tqdm progress bar of a JAX scan or loop"
|
|
38
|
+
_ = lax.cond(
|
|
39
|
+
iter_num == 0,
|
|
40
|
+
lambda _: host_callback.id_tap(_define_tqdm, None, result=iter_num),
|
|
41
|
+
lambda _: iter_num,
|
|
42
|
+
operand=None,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
_ = lax.cond(
|
|
46
|
+
# update tqdm every multiple of `print_rate` except at the end
|
|
47
|
+
(iter_num % print_rate == 0) & (iter_num != num_samples - remainder),
|
|
48
|
+
lambda _: host_callback.id_tap(_update_tqdm, print_rate, result=iter_num),
|
|
49
|
+
lambda _: iter_num,
|
|
50
|
+
operand=None,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
_ = lax.cond(
|
|
54
|
+
# update tqdm by `remainder`
|
|
55
|
+
iter_num == num_samples - remainder,
|
|
56
|
+
lambda _: host_callback.id_tap(_update_tqdm, remainder, result=iter_num),
|
|
57
|
+
lambda _: iter_num,
|
|
58
|
+
operand=None,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
def _close_tqdm(arg, transform):
|
|
62
|
+
tqdm_bars[0].close()
|
|
63
|
+
|
|
64
|
+
def close_tqdm(result, iter_num):
|
|
65
|
+
return lax.cond(
|
|
66
|
+
iter_num == num_samples - 1,
|
|
67
|
+
lambda _: host_callback.id_tap(_close_tqdm, None, result=result),
|
|
68
|
+
lambda _: result,
|
|
69
|
+
operand=None,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
def _progress_bar_scan(func):
|
|
73
|
+
"""Decorator that adds a progress bar to `body_fun` used in `lax.scan`.
|
|
74
|
+
Note that `body_fun` must either be looping over `np.arange(num_samples)`,
|
|
75
|
+
or be looping over a tuple who's first element is `np.arange(num_samples)`
|
|
76
|
+
This means that `iter_num` is the current iteration number
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
def wrapper_progress_bar(carry, x):
|
|
80
|
+
if type(x) is tuple:
|
|
81
|
+
iter_num, *_ = x
|
|
82
|
+
else:
|
|
83
|
+
iter_num = x
|
|
84
|
+
_update_progress_bar(iter_num)
|
|
85
|
+
result = func(carry, x)
|
|
86
|
+
return close_tqdm(result, iter_num)
|
|
87
|
+
|
|
88
|
+
return wrapper_progress_bar
|
|
89
|
+
|
|
90
|
+
return _progress_bar_scan
|
tabsim/plot.py
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import matplotlib.pyplot as plt
|
|
2
|
+
import numpy as np
|
|
3
|
+
from numpy.typing import ArrayLike
|
|
4
|
+
|
|
5
|
+
import jax.numpy as jnp
|
|
6
|
+
|
|
7
|
+
from tabsim.dask.observation import Observation
|
|
8
|
+
from tabsim.jax.coordinates import alt_az_of_source
|
|
9
|
+
import os
|
|
10
|
+
|
|
11
|
+
plt.rcParams["font.size"] = 18
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def time_units(times: ArrayLike) -> tuple:
|
|
15
|
+
"""Scale the time axis to hours, minutes or seconds depending on the total range.
|
|
16
|
+
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
times : ArrayLike
|
|
20
|
+
Times to consider.
|
|
21
|
+
|
|
22
|
+
Returns
|
|
23
|
+
-------
|
|
24
|
+
tuple
|
|
25
|
+
Rescaled times array and the scale unit as a string.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
time_range = times[-1] - times[0]
|
|
29
|
+
times = times - times[0]
|
|
30
|
+
if time_range > 3600:
|
|
31
|
+
units = "hr"
|
|
32
|
+
times = times / 3600
|
|
33
|
+
elif time_range > 60:
|
|
34
|
+
units = "min"
|
|
35
|
+
times = times / 60
|
|
36
|
+
else:
|
|
37
|
+
units = "s"
|
|
38
|
+
|
|
39
|
+
return times, units
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def plot_angular_seps(obs: Observation, save_path: str) -> None:
|
|
43
|
+
"""Plot the angular separations between the RFI sources and pointing direction.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
obs : Observation
|
|
48
|
+
Observation object with RFI sources added
|
|
49
|
+
save_path : str
|
|
50
|
+
Path to where to save the plots.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
times, scale = time_units(obs.times_fine)
|
|
54
|
+
n_time = len(times)
|
|
55
|
+
if n_time > 100:
|
|
56
|
+
idx = np.arange(0, n_time, n_time // 100)
|
|
57
|
+
else:
|
|
58
|
+
idx = np.arange(n_time)
|
|
59
|
+
plt.figure(figsize=(10, 7))
|
|
60
|
+
if obs.n_rfi_satellite > 0:
|
|
61
|
+
ang_seps = np.concatenate(obs.rfi_satellite_ang_sep, axis=0).mean(axis=-1).T
|
|
62
|
+
plt.plot(times[idx], ang_seps[idx], label="Satellite")
|
|
63
|
+
if obs.n_rfi_stationary > 0:
|
|
64
|
+
ang_seps = np.concatenate(obs.rfi_stationary_ang_sep, axis=0).mean(axis=-1).T
|
|
65
|
+
plt.plot(times[idx], ang_seps[idx], label="Stationary")
|
|
66
|
+
if obs.n_rfi_tle_satellite > 0:
|
|
67
|
+
ang_seps = np.concatenate(obs.rfi_tle_satellite_ang_sep, axis=0).mean(axis=-1)
|
|
68
|
+
for ang_sep, n_id in zip(np.atleast_2d(ang_seps), obs.norad_ids):
|
|
69
|
+
plt.plot(times[idx], ang_sep[idx], label=n_id.compute())
|
|
70
|
+
plt.xlabel(f"Time [{scale}]")
|
|
71
|
+
plt.ylabel("Angular Separation [deg]")
|
|
72
|
+
plt.legend()
|
|
73
|
+
plt.savefig(os.path.join(save_path, "AngularSeps.png"), format="png", dpi=200)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def plot_src_alt(obs: Observation, save_path: str) -> None:
|
|
77
|
+
"""Plot the target source altitude over the period of the observation.
|
|
78
|
+
|
|
79
|
+
Parameters
|
|
80
|
+
----------
|
|
81
|
+
obs : Observation
|
|
82
|
+
Observation object.
|
|
83
|
+
save_path : str
|
|
84
|
+
Path to where to save the plot.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
times, scale = time_units(obs.times)
|
|
88
|
+
lsa = obs.lsa.compute()
|
|
89
|
+
alt = alt_az_of_source(
|
|
90
|
+
lsa[obs.t_idx], *[x.compute() for x in [obs.latitude, obs.ra, obs.dec]]
|
|
91
|
+
)[:, 0]
|
|
92
|
+
plt.figure(figsize=(10, 7))
|
|
93
|
+
plt.plot(times, alt, ".-")
|
|
94
|
+
plt.xlabel(f"Time [{scale}]")
|
|
95
|
+
plt.ylabel("Source Altitude [deg]")
|
|
96
|
+
plt.savefig(os.path.join(save_path, "SourceAltitude.png"), format="png", dpi=200)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def plot_uv(obs: Observation, save_path: str) -> None:
|
|
100
|
+
"""Plot the uv coverage of the telescope for the given observation.
|
|
101
|
+
|
|
102
|
+
Parameters
|
|
103
|
+
----------
|
|
104
|
+
obs : Observation
|
|
105
|
+
Observation object.
|
|
106
|
+
save_path : str
|
|
107
|
+
Path to where to save the plot.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
plt.figure(figsize=(10, 10))
|
|
111
|
+
if obs.n_time_fine > 100:
|
|
112
|
+
time_step = int(obs.n_time_fine / 100)
|
|
113
|
+
else:
|
|
114
|
+
time_step = 1
|
|
115
|
+
u = obs.bl_uvw[::time_step, :, 0].compute().flatten()
|
|
116
|
+
v = obs.bl_uvw[::time_step, :, 1].compute().flatten()
|
|
117
|
+
max_U = np.max(np.sqrt(u**2 + v**2))
|
|
118
|
+
exp = float(np.floor(np.log10(max_U)))
|
|
119
|
+
mantissa = np.ceil(10 ** (np.log10(max_U) - exp))
|
|
120
|
+
lim = mantissa * 10**exp
|
|
121
|
+
plt.plot(u, v, "k.", ms=1, alpha=0.3)
|
|
122
|
+
plt.plot(-u, -v, "k.", ms=1, alpha=0.3)
|
|
123
|
+
plt.grid()
|
|
124
|
+
plt.xlim(-lim, lim)
|
|
125
|
+
plt.ylim(-lim, lim)
|
|
126
|
+
plt.xlabel("U [m]")
|
|
127
|
+
plt.ylabel("V [m]")
|
|
128
|
+
plt.savefig(os.path.join(save_path, "UV.png"), format="png", dpi=200)
|
|
129
|
+
|
|
130
|
+
plt.rcParams["font.size"] = 16
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def plot_comparison(
|
|
134
|
+
ax, times, mean1, mean2, std1, std2, true1, true2, rmse, diff=False
|
|
135
|
+
):
|
|
136
|
+
times, units = time_units(times)
|
|
137
|
+
for i, a in enumerate(ax):
|
|
138
|
+
a[0].plot(rmse[..., i], "o")
|
|
139
|
+
a[0].set_xlabel("Sample")
|
|
140
|
+
|
|
141
|
+
if diff:
|
|
142
|
+
a[1].plot(times, mean1[i] - true1[i].real, label="Estimate")
|
|
143
|
+
a[1].fill_between(times, -std1[i], std1[i], color="tab:orange", alpha=0.3)
|
|
144
|
+
a[1].fill_between(
|
|
145
|
+
times, -2 * std1[i], 2 * std1[i], color="tab:orange", alpha=0.3
|
|
146
|
+
)
|
|
147
|
+
a[2].plot(times, mean2[i] - true2[i], label="Estimate")
|
|
148
|
+
a[2].fill_between(times, -std2[i], std2[i], color="tab:orange", alpha=0.3)
|
|
149
|
+
a[2].fill_between(
|
|
150
|
+
times, -2 * std2[i], 2 * std2[i], color="tab:orange", alpha=0.3
|
|
151
|
+
)
|
|
152
|
+
else:
|
|
153
|
+
a[1].plot(times, true1[i], label="True")
|
|
154
|
+
a[1].plot(times, mean1[i], label="Estimate")
|
|
155
|
+
a[1].fill_between(
|
|
156
|
+
times,
|
|
157
|
+
mean1[i] - std1[i],
|
|
158
|
+
mean1[i] + std1[i],
|
|
159
|
+
color="tab:orange",
|
|
160
|
+
alpha=0.3,
|
|
161
|
+
)
|
|
162
|
+
a[1].fill_between(
|
|
163
|
+
times,
|
|
164
|
+
mean1[i] - 2 * std1[i],
|
|
165
|
+
mean1[i] + 2 * std1[i],
|
|
166
|
+
color="tab:orange",
|
|
167
|
+
alpha=0.3,
|
|
168
|
+
)
|
|
169
|
+
a[2].plot(times, true2[i], label="True")
|
|
170
|
+
a[2].plot(times, mean2[i], label="Estimate")
|
|
171
|
+
a[2].fill_between(
|
|
172
|
+
times,
|
|
173
|
+
mean2[i] - std2[i],
|
|
174
|
+
mean2[i] + std2[i],
|
|
175
|
+
color="tab:orange",
|
|
176
|
+
alpha=0.3,
|
|
177
|
+
)
|
|
178
|
+
a[2].fill_between(
|
|
179
|
+
times,
|
|
180
|
+
mean2[i] - 2 * std2[i],
|
|
181
|
+
mean2[i] + 2 * std2[i],
|
|
182
|
+
color="tab:orange",
|
|
183
|
+
alpha=0.3,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
a[1].set_xlabel(f"Time [{units}]")
|
|
187
|
+
a[1].legend()
|
|
188
|
+
a[2].set_xlabel(f"Time [{units}]")
|
|
189
|
+
a[2].legend()
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def plot_complex_real_imag(
|
|
193
|
+
times,
|
|
194
|
+
param,
|
|
195
|
+
true,
|
|
196
|
+
rmse,
|
|
197
|
+
name: str,
|
|
198
|
+
save_name: str = None,
|
|
199
|
+
diff: bool = False,
|
|
200
|
+
max_plots: int = 10,
|
|
201
|
+
save_dir: str = "plots/",
|
|
202
|
+
):
|
|
203
|
+
n_params = min(param.shape[1], max_plots)
|
|
204
|
+
# idx = np.random.permutation(param.shape[1])
|
|
205
|
+
# print(param.shape, true.shape)
|
|
206
|
+
# param = param[:, idx]
|
|
207
|
+
# true = true[idx]
|
|
208
|
+
mean_r = param.real.mean(axis=0)
|
|
209
|
+
mean_i = param.imag.mean(axis=0)
|
|
210
|
+
std_r = param.real.std(axis=0)
|
|
211
|
+
std_i = param.imag.std(axis=0)
|
|
212
|
+
|
|
213
|
+
fig, ax = plt.subplots(n_params, 3, figsize=(18, 4.5 * n_params))
|
|
214
|
+
|
|
215
|
+
ax[0, 0].set_title("Root Mean Squared Error")
|
|
216
|
+
ax[0, 1].set_title(f"{name} Real")
|
|
217
|
+
ax[0, 2].set_title(f"{name} Imag")
|
|
218
|
+
|
|
219
|
+
plot_comparison(
|
|
220
|
+
ax, times, mean_r, mean_i, std_r, std_i, true.real, true.imag, rmse, diff=diff
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
if save_name is not None:
|
|
224
|
+
fig.savefig(
|
|
225
|
+
os.path.join(save_dir, f"{save_name}_real_imag.pdf"),
|
|
226
|
+
format="pdf",
|
|
227
|
+
bbox_inches="tight",
|
|
228
|
+
)
|
|
229
|
+
plt.close(fig)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def plot_complex_amp_phase(
|
|
233
|
+
times,
|
|
234
|
+
param,
|
|
235
|
+
true,
|
|
236
|
+
rmse,
|
|
237
|
+
name: str,
|
|
238
|
+
save_name: str = None,
|
|
239
|
+
diff: bool = False,
|
|
240
|
+
max_plots: int = 10,
|
|
241
|
+
save_dir: str = "plots/",
|
|
242
|
+
):
|
|
243
|
+
n_params = min(param.shape[1], max_plots)
|
|
244
|
+
# idx = np.random.permutation(param.shape[1])
|
|
245
|
+
# print(param.shape, true.shape)
|
|
246
|
+
# param = param[:, idx]
|
|
247
|
+
# true = true[idx]
|
|
248
|
+
mean_amp = jnp.abs(param).mean(axis=0)
|
|
249
|
+
mean_phase = jnp.rad2deg(jnp.angle(param)).mean(axis=0)
|
|
250
|
+
std_amp = jnp.abs(param).std(axis=0)
|
|
251
|
+
std_phase = jnp.rad2deg(jnp.angle(param)).std(axis=0)
|
|
252
|
+
|
|
253
|
+
fig, ax = plt.subplots(n_params, 3, figsize=(18, 4.5 * n_params))
|
|
254
|
+
|
|
255
|
+
ax[0, 0].set_title("Root Mean Squared Error")
|
|
256
|
+
ax[0, 1].set_title(f"{name} Magnitude")
|
|
257
|
+
ax[0, 2].set_title(f"{name} Phase")
|
|
258
|
+
|
|
259
|
+
plot_comparison(
|
|
260
|
+
ax,
|
|
261
|
+
times,
|
|
262
|
+
mean_amp,
|
|
263
|
+
mean_phase,
|
|
264
|
+
std_amp,
|
|
265
|
+
std_phase,
|
|
266
|
+
jnp.abs(true),
|
|
267
|
+
jnp.rad2deg(jnp.angle(true)),
|
|
268
|
+
rmse,
|
|
269
|
+
diff=diff,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
if save_name is not None:
|
|
273
|
+
fig.savefig(
|
|
274
|
+
os.path.join(save_dir, f"{save_name}_amp_phase.pdf"),
|
|
275
|
+
format="pdf",
|
|
276
|
+
bbox_inches="tight",
|
|
277
|
+
)
|
|
278
|
+
plt.close(fig)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def plot_predictions(
|
|
282
|
+
times,
|
|
283
|
+
pred,
|
|
284
|
+
args,
|
|
285
|
+
type: str = "",
|
|
286
|
+
model_name: str = "",
|
|
287
|
+
max_plots: int = 10,
|
|
288
|
+
save_dir: str = "plots/",
|
|
289
|
+
):
|
|
290
|
+
plot_complex_real_imag(
|
|
291
|
+
times=times,
|
|
292
|
+
param=pred["ast_vis"],
|
|
293
|
+
true=args["vis_ast_true"],
|
|
294
|
+
rmse=pred["rmse_ast"],
|
|
295
|
+
name="Ast. Vis.",
|
|
296
|
+
save_name=f"{model_name}_{type}_ast_vis",
|
|
297
|
+
max_plots=max_plots,
|
|
298
|
+
save_dir=save_dir,
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
plot_complex_amp_phase(
|
|
302
|
+
times=times,
|
|
303
|
+
param=pred["rfi_vis"],
|
|
304
|
+
true=args["vis_rfi_true"],
|
|
305
|
+
rmse=pred["rmse_rfi"],
|
|
306
|
+
name="RFI Vis.",
|
|
307
|
+
save_name=f"{model_name}_{type}_rfi_vis",
|
|
308
|
+
diff=False, # True,
|
|
309
|
+
max_plots=max_plots,
|
|
310
|
+
save_dir=save_dir,
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
plot_complex_amp_phase(
|
|
314
|
+
times=times,
|
|
315
|
+
param=pred["gains"],
|
|
316
|
+
true=args["gains_true"],
|
|
317
|
+
rmse=pred["rmse_gains"],
|
|
318
|
+
name="Gains",
|
|
319
|
+
save_name=f"{model_name}_{type}_gains",
|
|
320
|
+
diff=False,
|
|
321
|
+
max_plots=max_plots,
|
|
322
|
+
save_dir=save_dir,
|
|
323
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from astropy.coordinates import SkyCoord
|
|
2
|
+
import astropy.units as u
|
|
3
|
+
from regions import CircleSkyRegion, RectangleSkyRegion
|
|
4
|
+
|
|
5
|
+
import xarray as xr
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
|
|
12
|
+
import matplotlib.colors as mcolors
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def main():
|
|
16
|
+
|
|
17
|
+
colors = list(mcolors.TABLEAU_COLORS.values())
|
|
18
|
+
# n_c = len(colors)
|
|
19
|
+
|
|
20
|
+
parser = argparse.ArgumentParser(
|
|
21
|
+
description="Extract astronomical positions to ds9 region file."
|
|
22
|
+
)
|
|
23
|
+
parser.add_argument(
|
|
24
|
+
"-z",
|
|
25
|
+
"--zarr_path",
|
|
26
|
+
required=True,
|
|
27
|
+
help="File path to the tabascal zarr simulation file.",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
args = parser.parse_args()
|
|
31
|
+
zarr_path = args.zarr_path
|
|
32
|
+
|
|
33
|
+
if zarr_path[-1] == "/":
|
|
34
|
+
zarr_path = zarr_path[:-1]
|
|
35
|
+
|
|
36
|
+
region_path = os.path.join(
|
|
37
|
+
os.path.split(zarr_path)[0], "astronomical_positions.ds9"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
xds = xr.open_zarr(zarr_path)
|
|
41
|
+
|
|
42
|
+
ra, dec = xds.ast_p_radec.data.compute().T
|
|
43
|
+
c = SkyCoord(ra, dec, unit="deg", frame="fk5")
|
|
44
|
+
|
|
45
|
+
max_U = np.max(np.linalg.norm(xds.bl_uvw.data[0], axis=-1))
|
|
46
|
+
syn_bw = (3e8 / (max_U * xds.freq.data.max())).compute()
|
|
47
|
+
|
|
48
|
+
with open(region_path, "w") as fp:
|
|
49
|
+
for i in range(len(ra)):
|
|
50
|
+
fp.write(
|
|
51
|
+
CircleSkyRegion(
|
|
52
|
+
c[i], radius=syn_bw * u.rad, visual={"color": colors[0]}
|
|
53
|
+
).serialize(format="ds9")
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
print(f"Region file written to : {region_path}")
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
from daskms import xds_from_ms, xds_to_table
|
|
4
|
+
|
|
5
|
+
import xarray as xr
|
|
6
|
+
import dask.array as da
|
|
7
|
+
import dask
|
|
8
|
+
import os
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import subprocess
|
|
12
|
+
import shutil
|
|
13
|
+
|
|
14
|
+
# aoflagger -strategy ../yaml_configs/target/firstpass.rfis tab1_obs_16A_450T-0440-1338_256I_001F-1.227e+09-1.227e+09_100PAST_000GAST_000EAST_1SAT_0GRD_1.0e+00RFI/tab1_obs_16A_450T-0440-1338_256I_001F-1.227e+09-1.227e+09_100PAST_000GAST_000EAST_1SAT_0GRD_1.0e+00RFI.ms/
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def write_perfect_flags(ms_path: str, n_sigma: float = 3.0):
|
|
18
|
+
|
|
19
|
+
xds = xds_from_ms(ms_path)[0]
|
|
20
|
+
|
|
21
|
+
if n_sigma > 0:
|
|
22
|
+
flags = da.abs(xds.CAL_DATA - xds.AST_MODEL_DATA) > (
|
|
23
|
+
n_sigma * xds.SIGMA * da.sqrt(2)
|
|
24
|
+
)
|
|
25
|
+
else:
|
|
26
|
+
flags = xr.zeros_like(xds.DATA).astype(bool)
|
|
27
|
+
|
|
28
|
+
xds = xds.assign(FLAG=flags)
|
|
29
|
+
cols = ["FLAG"]
|
|
30
|
+
|
|
31
|
+
dask.compute(xds_to_table([xds], ms_path, cols))
|
|
32
|
+
|
|
33
|
+
print()
|
|
34
|
+
print(f"Flag Threshold : {n_sigma: .1f} sigma")
|
|
35
|
+
print(f"Flag Rate : {100*flags.data.mean().compute(): .1f} %")
|
|
36
|
+
|
|
37
|
+
return flags
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def write_to_aoflags(ms_path: str):
|
|
41
|
+
|
|
42
|
+
xds_ms = xds_from_ms(ms_path)[0]
|
|
43
|
+
|
|
44
|
+
xds_ms = xds_ms.assign({"AO_FLAGS": xds_ms["FLAG"]})
|
|
45
|
+
cols = ["AO_FLAGS"]
|
|
46
|
+
|
|
47
|
+
print("Writing AOFlagger flags to 'AO_FLAGS' column in MS file.")
|
|
48
|
+
|
|
49
|
+
dask.compute(xds_to_table([xds_ms], ms_path, cols))
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def write_aoflags_to_flag(ms_path: str):
|
|
53
|
+
|
|
54
|
+
xds_ms = xds_from_ms(ms_path)[0]
|
|
55
|
+
|
|
56
|
+
xds_ms = xds_ms.assign(FLAG=xds_ms["AO_FLAGS"])
|
|
57
|
+
cols = ["FLAG"]
|
|
58
|
+
|
|
59
|
+
print("Writing AOFlagger flags to 'FLAG' column in MS file.")
|
|
60
|
+
|
|
61
|
+
dask.compute(xds_to_table([xds_ms], ms_path, cols))
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def run_aoflagger(
|
|
65
|
+
ms_path: str,
|
|
66
|
+
data_column: str = "CAL_DATA",
|
|
67
|
+
strategy_paths: list = None,
|
|
68
|
+
sif_path: str = None,
|
|
69
|
+
bash_exec: str = "/bin/bash",
|
|
70
|
+
rerun_aoflagger: bool = False,
|
|
71
|
+
):
|
|
72
|
+
|
|
73
|
+
reflagged = False
|
|
74
|
+
|
|
75
|
+
if ms_path[-1] == "/":
|
|
76
|
+
ms_path = ms_path[:-1]
|
|
77
|
+
|
|
78
|
+
if not rerun_aoflagger:
|
|
79
|
+
xds_ms = xds_from_ms(ms_path)
|
|
80
|
+
try:
|
|
81
|
+
aoflags = xds_ms["AO_FLAGS"]
|
|
82
|
+
write_aoflags_to_flag(ms_path)
|
|
83
|
+
print("Using previous AOFlagger run.")
|
|
84
|
+
except:
|
|
85
|
+
aoflags = None
|
|
86
|
+
|
|
87
|
+
if not aoflags or rerun_aoflagger:
|
|
88
|
+
|
|
89
|
+
data_dir, ms_file = os.path.split(os.path.abspath(ms_path))
|
|
90
|
+
|
|
91
|
+
docker_opts = "--rm -v /etc/group:/etc/group -v /etc/passwd:/etc/passwd -v /etc/shadow:/etc/shadow -v/etc/sudoers.d:/etc/sudoers.d -e HOME=${HOME} --user=`id -ur`"
|
|
92
|
+
container_cmd = f"docker run {docker_opts} -v {data_dir}:/data --workdir /data stimela/aoflagger:latest"
|
|
93
|
+
|
|
94
|
+
if sif_path is not None:
|
|
95
|
+
sif_path = os.path.abspath(sif_path)
|
|
96
|
+
container_cmd = (
|
|
97
|
+
f"singularity exec --bind {data_dir}:/data --pwd /data {sif_path}"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
if strategy_paths is not None:
|
|
101
|
+
write_perfect_flags(ms_path, 0)
|
|
102
|
+
for strategy_path in strategy_paths:
|
|
103
|
+
|
|
104
|
+
strategy_path = os.path.abspath(strategy_path)
|
|
105
|
+
shutil.copy(strategy_path, data_dir)
|
|
106
|
+
strategy_file = os.path.split(strategy_path)[1]
|
|
107
|
+
strategy = f"-strategy /data/{strategy_file}"
|
|
108
|
+
|
|
109
|
+
aoflag_cmd = f"{container_cmd} aoflagger -column {data_column} {strategy} /data/{ms_file}"
|
|
110
|
+
subprocess.run(aoflag_cmd, shell=True, executable=bash_exec)
|
|
111
|
+
|
|
112
|
+
print()
|
|
113
|
+
print(
|
|
114
|
+
f"Strategies : {[os.path.split(strategy)[1] for strategy in strategy_paths]}"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
else:
|
|
118
|
+
aoflag_cmd = f"{container_cmd} aoflagger /data/{ms_file}"
|
|
119
|
+
subprocess.run(aoflag_cmd, shell=True, executable=bash_exec)
|
|
120
|
+
print()
|
|
121
|
+
|
|
122
|
+
write_to_aoflags(ms_path)
|
|
123
|
+
reflagged = True
|
|
124
|
+
|
|
125
|
+
flags = xds_from_ms(ms_path)[0].FLAG.data
|
|
126
|
+
print(f"Flag Rate : {100*flags.mean().compute(): .1f} %")
|
|
127
|
+
|
|
128
|
+
return flags, reflagged
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def main():
|
|
132
|
+
|
|
133
|
+
parser = argparse.ArgumentParser(
|
|
134
|
+
description="Flag CAL_DATA lying a certain threshold away from the AST_MODEL_DATA. Requires tabascal simulated MS files."
|
|
135
|
+
)
|
|
136
|
+
parser.add_argument(
|
|
137
|
+
"-m", "--ms_path", required=True, help="File path to the Measurement Set."
|
|
138
|
+
)
|
|
139
|
+
parser.add_argument(
|
|
140
|
+
"-s",
|
|
141
|
+
"--n_sigma",
|
|
142
|
+
default=3.0,
|
|
143
|
+
type=float,
|
|
144
|
+
help="Threshold in number of std of noise given by SIGMA column. 0 unflags everything. Default is 3.0.",
|
|
145
|
+
)
|
|
146
|
+
parser.add_argument(
|
|
147
|
+
"-d",
|
|
148
|
+
"--data_col",
|
|
149
|
+
default="DATA",
|
|
150
|
+
help="Data column to run AOFlagger on. Default is 'DATA'.",
|
|
151
|
+
)
|
|
152
|
+
parser.add_argument(
|
|
153
|
+
"-ao",
|
|
154
|
+
"--aoflagger",
|
|
155
|
+
default=False,
|
|
156
|
+
action=argparse.BooleanOptionalAction,
|
|
157
|
+
help="Whether to use AOFlagger to flag the data. Default is False.",
|
|
158
|
+
)
|
|
159
|
+
parser.add_argument(
|
|
160
|
+
"-rao",
|
|
161
|
+
"--rerun_aoflagger",
|
|
162
|
+
default=False,
|
|
163
|
+
action=argparse.BooleanOptionalAction,
|
|
164
|
+
help="Whether to rerun AOFlagger to flag the data. Default is False.",
|
|
165
|
+
)
|
|
166
|
+
parser.add_argument(
|
|
167
|
+
"-st",
|
|
168
|
+
"--strategy_paths",
|
|
169
|
+
default=None,
|
|
170
|
+
help="list of paths to AOFlagger strategies.",
|
|
171
|
+
)
|
|
172
|
+
parser.add_argument(
|
|
173
|
+
"-sp",
|
|
174
|
+
"--sif_path",
|
|
175
|
+
default=None,
|
|
176
|
+
help="Paths to AOFlagger singularity image.",
|
|
177
|
+
)
|
|
178
|
+
args = parser.parse_args()
|
|
179
|
+
ms_path = args.ms_path
|
|
180
|
+
n_sigma = args.n_sigma
|
|
181
|
+
strategy_paths = args.strategy_paths
|
|
182
|
+
data_col = args.data_col
|
|
183
|
+
|
|
184
|
+
if args.aoflagger:
|
|
185
|
+
if strategy_paths is not None:
|
|
186
|
+
strategy_paths = strategy_paths.split(",")
|
|
187
|
+
flags = run_aoflagger(
|
|
188
|
+
ms_path, data_col, strategy_paths, args.sif_path, args.rerun_aoflagger
|
|
189
|
+
)
|
|
190
|
+
else:
|
|
191
|
+
flags = write_perfect_flags(ms_path, n_sigma)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
if __name__ == "__main__":
|
|
195
|
+
main()
|