Commits

don't convert to J2000, write phasecenter vals using same frame as in the PHASE_DIR metadata, CAS-13616
No tags

casatasks/src/private/task_phaseshift.py

Modified
1 -from typing import Optional, Tuple, Union
1 +from typing import Dict, Optional, Tuple, Union
2 2
3 3 import numpy as np
4 4 from .mstools import write_history
5 5 from casatools import table, ms, mstransformer
6 6 from casatools import measures as me
7 7 from casatasks import casalog
8 8 from .parallel.parallel_data_helper import ParallelDataHelper
9 9
10 10
11 11 def phaseshift(
133 133 try:
134 134 tblocal.open(vis)
135 135 colnames = tblocal.colnames()
136 136 finally:
137 137 tblocal.done()
138 138 return colnames
139 139
140 140
141 141 def _update_field_subtable(outputvis: str, field: str, phasecenter: Union[str, dict]):
142 142 """Update MS/FIELD subtable with shifted center(s)."""
143 +
143 144 try:
144 145 tblocal = table()
145 146 # modify FIELD table
146 147 tblocal.open(outputvis + "/FIELD", nomodify=False)
147 148 pcol = tblocal.getcol("PHASE_DIR")
148 149
150 + field_frames = _find_field_ref_frames(tblocal)
149 151 if isinstance(phasecenter, str):
150 - thenewra_rad, thenewdec_rad = _convert_to_ra_dec_j2000(phasecenter)
151 152 if field:
152 153 try:
153 154 field_id = int(field)
154 155 except ValueError as _exc:
155 156 fnames = tblocal.getcol("NAME")
156 157 field_id = np.where(fnames == field)[0][0]
158 + thenewra_rad, thenewdec_rad = _convert_to_ref_frame(phasecenter, field_frames[field_id])
157 159 pcol[0][0][field_id] = thenewra_rad
158 160 pcol[1][0][field_id] = thenewdec_rad
159 161 else:
160 162 for row in range(0, tblocal.nrows()):
163 + thenewra_rad, thenewdec_rad = _convert_to_ref_frame(phasecenter, field_frames[row])
161 164 pcol[0][0][row] = thenewra_rad
162 165 pcol[1][0][row] = thenewdec_rad
163 166
164 167 elif isinstance(phasecenter, dict):
165 168 for field_id, field_center in phasecenter.items():
166 - thenewra_rad, thenewdec_rad = _convert_to_ra_dec_j2000(field_center)
167 169 field_iidx = int(field_id)
170 + thenewra_rad, thenewdec_rad = _convert_to_ref_frame(field_center, field_frames[field_iidx])
168 171 pcol[0][0][field_iidx] = thenewra_rad
169 172 pcol[1][0][field_iidx] = thenewdec_rad
170 173
171 174 tblocal.putcol("PHASE_DIR", pcol)
172 175
173 176 except Exception as instance:
174 177 casalog.post(f"*** Error '%s' updating FIELD subtable {instance}", "WARN")
175 178 raise RuntimeError(str(instance))
176 179 finally:
177 180 tblocal.done()
178 181
179 182
180 -def _convert_to_ra_dec_j2000(phasecenter: str) -> Tuple[float, float]:
181 - """Parse phase center string to obtain ra/dec (in rad)"""
183 +def _find_field_ref_frames(tblocal: table) -> Dict[int, str]:
184 + """
185 + Given an open FIELD subtable, returns a dict of {field: reference_frame} for the PHASE_DIR
186 + column, where field is of type int, and reference_frame is of type str.
187 +
188 + This handles:
189 + - simple metadata where the reference frame is in the keywords of the PHASE_DIR column (same
190 + ref frame for all fields)
191 + - variable (per-field) reference frame metadata, usually in a PhaseDir_Ref additional column
192 + """
193 +
194 + dir_col = "PHASE_DIR"
195 + metainfo = tblocal.getcolkeyword(dir_col, "MEASINFO")
196 + nrows = tblocal.nrows()
197 + if "Ref" in metainfo:
198 + ref_frame = metainfo["Ref"]
199 + field_ref_frames = {field_id: ref_frame for field_id in range(0, nrows)}
200 + elif "Ref" not in metainfo and ("VarRefCol" in metainfo and "TabRefTypes" in metainfo and
201 + "TabRefCodes" in metainfo):
202 + col = metainfo["VarRefCol"] # presumably PhaseDir_Ref
203 + ref_frame_codes = tblocal.getcol(col)
204 + ref_codes_to_frame_idx = {code: idx for idx, code in enumerate(metainfo["TabRefCodes"])}
205 + ref_frame_names = [metainfo["TabRefTypes"][ref_codes_to_frame_idx[code]] for code in
206 + ref_frame_codes]
207 + field_ref_frames = {field_id: ref_frame_names[field_id] for field_id in np.arange(0, nrows)}
208 + else:
209 + raise RuntimeError("Error when retrieving reference frames from the metadata of column "
210 + f"{dir_col}. The field 'Ref' is not present but could not find the "
211 + "fields 'VarRefCol', 'TabRefTypes', and 'TabRefCodes'")
212 +
213 + return field_ref_frames
214 +
215 +
216 +def _convert_to_ref_frame(phasecenter: str, output_ref_frame: str) -> Tuple[float, float]:
217 + """
218 + Converts one phasecenter (presumably given as input to this task) to another reference
219 + frame (presumably the frame used in the (output) MS for the relevant field).
220 +
221 + When applying phaseshift, the output MS has the same reference frames as the input MS, as
222 + propagated by mstransform.
223 +
224 + Returns the v0,v1 (RA,Dec) values in units of radians and using the requested frame, ready to be
225 + written to a FIELD subtable.
226 + """
182 227 def parse_phasecenter(center: str) -> Tuple[str, str, str]:
183 228 """
184 - Splits the:
229 + Parse phase center string to obtain ra/dec (in rad). Splits the:
185 230 - (optional) frame,
186 231 - v0 (typically RA),
187 232 - v1 (typically Dec)
188 233 in a phasecenter string.
189 234 """
190 235 dirstr = center.split(" ")
191 236 if 3 == len(dirstr):
192 237 dir_frame, dir_v0, dir_v1 = dirstr[0], dirstr[1], dirstr[2]
193 238 elif 2 == len(dirstr):
194 239 dir_frame = "" # J2000 will be default in me.direction, etc.
195 240 dir_v0, dir_v1 = dirstr[0], dirstr[1]
196 241 else:
197 242 raise AttributeError(f"Wrong phasecenter string: '{center}'. It must have 3 or 2 "
198 243 "items separated by space(s): 'reference_frame RA Dec' or 'RA Dec'")
199 244
200 245 return dir_frame, dir_v0, dir_v1
201 246
202 247 try:
203 248 melocal = me()
204 249 dir_frame, dir_v0, dir_v1 = parse_phasecenter(phasecenter)
250 +
251 + # Note: even if the input frame is the same as output_ref_frame, we need to ensure units of
252 + # radians for the FIELD subtable
205 253 if dir_frame:
206 254 thedir = melocal.direction(dir_frame, dir_v0, dir_v1)
207 255 else:
208 256 thedir = melocal.direction(v0=dir_v0, v1=dir_v1)
209 257 if not thedir:
210 258 raise RuntimeError(
211 259 f"measures.direction() failed for phasecenter string: {phasecenter}"
212 260 )
213 - if dir_frame != "J2000":
214 - # Convert to J2000
215 - thedir = melocal.measure(thedir, "J2000")
261 +
262 + if dir_frame != output_ref_frame:
263 + thedir = melocal.measure(thedir, output_ref_frame)
216 264 thenewra_rad = thedir["m0"]["value"]
217 265 thenewdec_rad = thedir["m1"]["value"]
218 266 except Exception as instance:
219 267 casalog.post(
220 268 f"*** Error {instance} when interpreting parameter 'phasecenter': ",
221 269 "SEVERE",
222 270 )
223 271 raise RuntimeError(str(instance))
224 272 finally:
225 273 melocal.done()

Everything looks good. We'll let you know here if there's anything you should know about.

Add shortcut