Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: filler for FIELD table #24

Merged
merged 3 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions src/nro45data/psw/ms2/filler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from .antenna import fill_antenna
from .data_description import fill_data_description
from .feed import fill_feed
from .field import _fill_field_columns, _get_field_columns
from .field import fill_field
from .observation import _fill_observation_columns, _get_observation_columns
from .polarization import _fill_polarization_columns, _get_polarization_columns
from .main import fill_main
Expand All @@ -26,11 +26,6 @@
LOG = logging.getLogger(__name__)


def fill_field(msfile: str, hdu: BinTableHDU):
columns = _get_field_columns(hdu)
_fill_field_columns(msfile, columns)


def fill_observation(msfile: str, hdu: BinTableHDU):
columns = _get_observation_columns(hdu)
_fill_observation_columns(msfile, columns)
Expand Down
71 changes: 42 additions & 29 deletions src/nro45data/psw/ms2/filler/field.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
from __future__ import annotations

import logging
from typing import TYPE_CHECKING
from typing import Generator, TYPE_CHECKING

import numpy as np

from .._casa import open_table
from .._casa import datestr2mjd
from .._casa import convert_str_angle_to_rad
from .utils import fix_nrow_to
from .utils import fill_ms_table

if TYPE_CHECKING:
from astropy.io.fits.hdu.BinTableHDU import BinTableHDU

LOG = logging.getLogger(__name__)


def _get_field_columns(hdu: "BinTableHDU") -> dict:
def _get_field_row(hdu: BinTableHDU) -> Generator[dict, None, dict]:
"""Provide field row information.

Args:
hdu: NRO45m psw data in the form of BinTableHDU object.

Yields:
Dictionary containing field row information.

Returns:
Dictionary containing data-dependent column keywords.
"""
# NAME
field_name = hdu.header["OBJECT"].strip()
LOG.debug("field_name: %s", field_name)
Expand All @@ -25,8 +38,11 @@ def _get_field_columns(hdu: "BinTableHDU") -> dict:
# use start time of the observation
history_cards = hdu.header["HISTORY"]
start_time_card = [x for x in history_cards if x.startswith("NEWSTAR START-TIME")]
field_time = float(start_time_card[0].split("=")[-1].strip(" '"))
LOG.debug("field_time: %s", field_time)
sstr = start_time_card[0].split("=")[-1].strip(" '")
LOG.debug("sstr: %s", sstr)
datestr = sstr[0:4] + "/" + sstr[4:6] + "/" + sstr[6:8] + " " + sstr[8:10] + ":" + sstr[10:12] + ":" + sstr[12:14]
LOG.debug("formatted sstr: %s", datestr)
field_time = datestr2mjd(datestr) - 9 * 3600

# NUM_POLY
num_poly = 0
Expand Down Expand Up @@ -62,37 +78,34 @@ def _get_field_columns(hdu: "BinTableHDU") -> dict:
# FLAG_ROW
flag_row = False

columns = {
row = {
"NAME": field_name,
"CODE": field_code,
"TIME": field_time,
"NUM_POLY": num_poly,
"FIELD_EPOCH": field_epoch,
"DELAY_DIR": delay_dir,
"PHASE_DIR": phase_dir,
"REFERENCE_DIR": reference_dir,
"SOURCE_ID": source_id,
"FLAG_ROW": flag_row,
}

return columns


def _fill_field_columns(msfile: str, columns: dict):
with open_table(msfile + "/FIELD", read_only=False) as tb:
fix_nrow_to(1, tb)

tb.putcell("NAME", 0, columns["NAME"])
tb.putcell("CODE", 0, columns["CODE"])
tb.putcell("TIME", 0, columns["TIME"])
tb.putcell("NUM_POLY", 0, columns["NUM_POLY"])
tb.putcell("DELAY_DIR", 0, columns["DELAY_DIR"])
colkeywords = tb.getcolkeywords("DELAY_DIR")
colkeywords["MEASINFO"]["Ref"] = columns["FIELD_EPOCH"]
tb.putcolkeywords("DELAY_DIR", colkeywords)
tb.putcell("PHASE_DIR", 0, columns["PHASE_DIR"])
tb.putcolkeywords("PHASE_DIR", colkeywords)
tb.putcell("REFERENCE_DIR", 0, columns["REFERENCE_DIR"])
tb.putcolkeywords("REFERENCE_DIR", colkeywords)
tb.putcell("SOURCE_ID", 0, columns["SOURCE_ID"])
tb.putcell("FLAG_ROW", 0, columns["FLAG_ROW"])
yield row

column_keywords = {
"DELAY_DIR": {"MEASINFO": {"Ref": field_epoch}},
"PHASE_DIR": {"MEASINFO": {"Ref": field_epoch}},
"REFERENCE_DIR": {"MEASINFO": {"Ref": field_epoch}}
}

return column_keywords # noqa


def fill_field(msfile: str, hdu: BinTableHDU):
"""Fill MS FIELD table.

Args:
msfile: Name of MS file.
hdu: NRO45m psw data in the form of BinTableHDU object.
"""
fill_ms_table(msfile, hdu, "FIELD", _get_field_row)
63 changes: 34 additions & 29 deletions src/nro45data/psw/ms2/filler/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import logging
import pprint
from typing import Any, Callable, Optional, TYPE_CHECKING
from typing import Callable, TYPE_CHECKING

import numpy as np

Expand Down Expand Up @@ -281,8 +281,7 @@ def fill_ms_table(
msfile: str,
hdu: BinTableHDU,
table_name: str,
row_generator: Callable,
column_keywords: Optional[dict[str, dict[str, Any]]] = None
row_generator: Callable
):
"""Fill MS table.

Expand All @@ -291,36 +290,42 @@ def fill_ms_table(
hdu: NRO45m psw data in the form of BinTableHDU object.
table_name: Name of subtable or "MAIN" for MAIN table.
row_generator: Generator to yield table row.
column_keywords: Optional information to update column
keywords. Defaults to None.
Key is column name and value is a dictionary of
column keywords to be updated.
"""
if table_name.upper() == "MAIN":
table_path = msfile
else:
table_path = f"{msfile}/{table_name.upper()}"

with open_table(table_path, read_only=False) as tb:
# update table column keywords
if column_keywords is None:
column_keywords = {}

for colname, colkeywords_new in column_keywords.items():
colkeywords = tb.getcolkeywords(colname)
for key, value_new in colkeywords_new.items():
if key in colkeywords and isinstance(value_new, dict):
colkeywords[key].update(value_new)
else:
colkeywords[key] = value_new
tb.putcolkeywords(colname, colkeywords)

# fill table rows
for row_id, row in enumerate(row_generator(hdu)):
if tb.nrows() <= row_id:
tb.addrows(tb.nrows() - row_id + 1)

for key, value in row.items():
LOG.debug("row %d key %s", row_id, key)
tb.putcell(key, row_id, value)
LOG.debug("%s table %d row %s", table_name, row_id, row)
# iterator should provide row dictionary in order,
# and then, if necessary, column keywords dictionary
# should be returned additionally
iterator = enumerate(row_generator(hdu))
try:
# fill table rows
while True:
row_id, row = next(iterator)
if tb.nrows() <= row_id:
tb.addrows(tb.nrows() - row_id + 1)

for key, value in row.items():
LOG.debug("row %d key %s", row_id, key)
tb.putcell(key, row_id, value)
LOG.debug("%s table %d row %s", table_name, row_id, row)

except StopIteration as s:
# update column keywords if necessary
column_keywords = s.value
LOG.debug("return value: %s", s.value)
if isinstance(column_keywords, dict):
LOG.debug("column_keywords: %s", column_keywords)
for colname, colkeywords_new in column_keywords.items():
colkeywords = tb.getcolkeywords(colname)
for key, value_new in colkeywords_new.items():
if key in colkeywords and isinstance(value_new, dict):
LOG.debug("updating column keyword %s: %s, %s", colname, key, value_new)
colkeywords[key].update(value_new)
else:
LOG.debug("adding column keyword %s: %s, %s", colname, key, value_new)
colkeywords[key] = value_new
tb.putcolkeywords(colname, colkeywords)
25 changes: 25 additions & 0 deletions tests/ms2_filler/test_forest.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,31 @@ def test_forest_ms2_structure(msfile):
assert receptor_angle.shape == (2,)
assert np.all(receptor_angle == 0.0)

with open_table(os.path.join(msfile, "FIELD")) as tb:
assert tb.nrows() == 1
assert tb.getcell("NAME", 0) == "NML-Tau"
assert tb.getcell("CODE", 0) == ""
# start time: 2024/09/30 17:49:19
field_time = mjd2datetime(tb.getcell("TIME", 0))
time_expected = datetime.datetime(2024, 9, 30, 17, 49, 19, tzinfo=datetime.timezone.utc)
assert abs((field_time - time_expected).total_seconds()) < 1e-3
assert tb.getcell("NUM_POLY", 0) == 0
# RA = 03:53:28.860
# DEC = +11:24:22.40
# Epoch = J2000
ra_expected = 1.0187530477122202 # rad
dec_expected = 2.9861419948788317 # rad
for col in ["DELAY_DIR", "PHASE_DIR", "REFERENCE_DIR"]:
meas_info = tb.getcolkeyword(col, "MEASINFO")
assert "Ref" in meas_info
assert meas_info["Ref"] == "J2000"
direction = tb.getcell(col, 0)
assert direction.shape == (1, 2)
assert abs(direction[0, 0] - ra_expected) / ra_expected < 1e-6
assert abs(direction[0, 1] - dec_expected) / dec_expected < 1e-6
assert tb.getcell("SOURCE_ID", 0) == 0
assert tb.getcell("FLAG_ROW", 0) is False

with open_table(os.path.join(msfile, "STATE")) as tb:
intents_map = dict((i, v) for i, v in enumerate(tb.getcol("OBS_MODE")))

Expand Down
25 changes: 25 additions & 0 deletions tests/ms2_filler/test_h40.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,31 @@ def test_h40_ms2_structure(msfile):
assert receptor_angle.shape == (2,)
assert np.all(receptor_angle == 0.0)

with open_table(os.path.join(msfile, "FIELD")) as tb:
assert tb.nrows() == 1
assert tb.getcell("NAME", 0) == "NML-Tau"
assert tb.getcell("CODE", 0) == ""
# start time: 2024/09/25 15:58:54
field_time = mjd2datetime(tb.getcell("TIME", 0))
time_expected = datetime.datetime(2024, 9, 25, 15, 58, 54, tzinfo=datetime.timezone.utc)
assert abs((field_time - time_expected).total_seconds()) < 1e-3
assert tb.getcell("NUM_POLY", 0) == 0
# RA = 03:53:28.860
# DEC = +11:24:22.40
# Epoch = J2000
ra_expected = 1.0187530477122202 # rad
dec_expected = 2.9861419948788317 # rad
for col in ["DELAY_DIR", "PHASE_DIR", "REFERENCE_DIR"]:
meas_info = tb.getcolkeyword(col, "MEASINFO")
assert "Ref" in meas_info
assert meas_info["Ref"] == "J2000"
direction = tb.getcell(col, 0)
assert direction.shape == (1, 2)
assert abs(direction[0, 0] - ra_expected) / ra_expected < 1e-6
assert abs(direction[0, 1] - dec_expected) / dec_expected < 1e-6
assert tb.getcell("SOURCE_ID", 0) == 0
assert tb.getcell("FLAG_ROW", 0) is False

with open_table(os.path.join(msfile, "STATE")) as tb:
intents_map = dict((i, v) for i, v in enumerate(tb.getcol("OBS_MODE")))

Expand Down
25 changes: 25 additions & 0 deletions tests/ms2_filler/test_z45.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,31 @@ def test_z45_ms2_structure(msfile):
assert receptor_angle.shape == (2,)
assert np.all(receptor_angle == 0.0)

with open_table(os.path.join(msfile, "FIELD")) as tb:
assert tb.nrows() == 1
assert tb.getcell("NAME", 0) == "NML-Tau"
assert tb.getcell("CODE", 0) == ""
# start time: 2024/09/30 18:00:32
field_time = mjd2datetime(tb.getcell("TIME", 0))
time_expected = datetime.datetime(2024, 9, 30, 18, 0, 32, tzinfo=datetime.timezone.utc)
assert abs((field_time - time_expected).total_seconds()) < 1e-3
assert tb.getcell("NUM_POLY", 0) == 0
# RA = 03:53:28.860
# DEC = +11:24:22.40
# Epoch = J2000
ra_expected = 1.0187530477122202 # rad
dec_expected = 2.9861419948788317 # rad
for col in ["DELAY_DIR", "PHASE_DIR", "REFERENCE_DIR"]:
meas_info = tb.getcolkeyword(col, "MEASINFO")
assert "Ref" in meas_info
assert meas_info["Ref"] == "J2000"
direction = tb.getcell(col, 0)
assert direction.shape == (1, 2)
assert abs(direction[0, 0] - ra_expected) / ra_expected < 1e-6
assert abs(direction[0, 1] - dec_expected) / dec_expected < 1e-6
assert tb.getcell("SOURCE_ID", 0) == 0
assert tb.getcell("FLAG_ROW", 0) is False

with open_table(os.path.join(msfile, "STATE")) as tb:
intents_map = dict((i, v) for i, v in enumerate(tb.getcol("OBS_MODE")))

Expand Down
Loading