Skip to content
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Add residual_stats to PointingResidualTable

Revision ID: a1b2c3d4e5f6
Revises: cd9bc4ba5bc0
Create Date: 2026-04-23

"""

from typing import Sequence, Union

Check failure on line 9 in mapcat/alembic/versions/a1b2c3d4e5f6_add_residual_stats_to_pointing_residuals.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (UP035)

mapcat/alembic/versions/a1b2c3d4e5f6_add_residual_stats_to_pointing_residuals.py:9:1: UP035 Import from `collections.abc` instead: `Sequence` help: Import from `collections.abc`

Check failure on line 9 in mapcat/alembic/versions/a1b2c3d4e5f6_add_residual_stats_to_pointing_residuals.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (UP035)

mapcat/alembic/versions/a1b2c3d4e5f6_add_residual_stats_to_pointing_residuals.py:9:1: UP035 Import from `collections.abc` instead: `Sequence` help: Import from `collections.abc`

import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision: str = "a1b2c3d4e5f6"
down_revision: Union[str, None] = "cd9bc4ba5bc0"

Check failure on line 16 in mapcat/alembic/versions/a1b2c3d4e5f6_add_residual_stats_to_pointing_residuals.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (UP007)

mapcat/alembic/versions/a1b2c3d4e5f6_add_residual_stats_to_pointing_residuals.py:16:16: UP007 Use `X | Y` for type annotations help: Convert to `X | Y`

Check failure on line 16 in mapcat/alembic/versions/a1b2c3d4e5f6_add_residual_stats_to_pointing_residuals.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (UP007)

mapcat/alembic/versions/a1b2c3d4e5f6_add_residual_stats_to_pointing_residuals.py:16:16: UP007 Use `X | Y` for type annotations help: Convert to `X | Y`
branch_labels: Union[str, Sequence[str], None] = None

Check failure on line 17 in mapcat/alembic/versions/a1b2c3d4e5f6_add_residual_stats_to_pointing_residuals.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (UP007)

mapcat/alembic/versions/a1b2c3d4e5f6_add_residual_stats_to_pointing_residuals.py:17:16: UP007 Use `X | Y` for type annotations help: Convert to `X | Y`

Check failure on line 17 in mapcat/alembic/versions/a1b2c3d4e5f6_add_residual_stats_to_pointing_residuals.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (UP007)

mapcat/alembic/versions/a1b2c3d4e5f6_add_residual_stats_to_pointing_residuals.py:17:16: UP007 Use `X | Y` for type annotations help: Convert to `X | Y`
depends_on: Union[str, Sequence[str], None] = None

Check failure on line 18 in mapcat/alembic/versions/a1b2c3d4e5f6_add_residual_stats_to_pointing_residuals.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (UP007)

mapcat/alembic/versions/a1b2c3d4e5f6_add_residual_stats_to_pointing_residuals.py:18:13: UP007 Use `X | Y` for type annotations help: Convert to `X | Y`

Check failure on line 18 in mapcat/alembic/versions/a1b2c3d4e5f6_add_residual_stats_to_pointing_residuals.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (UP007)

mapcat/alembic/versions/a1b2c3d4e5f6_add_residual_stats_to_pointing_residuals.py:18:13: UP007 Use `X | Y` for type annotations help: Convert to `X | Y`


def upgrade() -> None:
with op.batch_alter_table("depth_one_pointing_residuals") as batch:
batch.add_column(sa.Column("residual_stats", sa.JSON(), nullable=True))


def downgrade() -> None:
with op.batch_alter_table("depth_one_pointing_residuals") as batch:
batch.drop_column("residual_stats")
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@

"""

from typing import Sequence, Union

Check failure on line 9 in mapcat/alembic/versions/cd9bc4ba5bc0_change_pointing_model_to_pydantic_model.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (UP035)

mapcat/alembic/versions/cd9bc4ba5bc0_change_pointing_model_to_pydantic_model.py:9:1: UP035 Import from `collections.abc` instead: `Sequence` help: Import from `collections.abc`

Check failure on line 9 in mapcat/alembic/versions/cd9bc4ba5bc0_change_pointing_model_to_pydantic_model.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (UP035)

mapcat/alembic/versions/cd9bc4ba5bc0_change_pointing_model_to_pydantic_model.py:9:1: UP035 Import from `collections.abc` instead: `Sequence` help: Import from `collections.abc`

import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision: str = "cd9bc4ba5bc0"
down_revision: Union[str, None] = "6ce7e94dfd2d"

Check failure on line 16 in mapcat/alembic/versions/cd9bc4ba5bc0_change_pointing_model_to_pydantic_model.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (UP007)

mapcat/alembic/versions/cd9bc4ba5bc0_change_pointing_model_to_pydantic_model.py:16:16: UP007 Use `X | Y` for type annotations help: Convert to `X | Y`

Check failure on line 16 in mapcat/alembic/versions/cd9bc4ba5bc0_change_pointing_model_to_pydantic_model.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (UP007)

mapcat/alembic/versions/cd9bc4ba5bc0_change_pointing_model_to_pydantic_model.py:16:16: UP007 Use `X | Y` for type annotations help: Convert to `X | Y`
branch_labels: Union[str, Sequence[str], None] = None

Check failure on line 17 in mapcat/alembic/versions/cd9bc4ba5bc0_change_pointing_model_to_pydantic_model.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (UP007)

mapcat/alembic/versions/cd9bc4ba5bc0_change_pointing_model_to_pydantic_model.py:17:16: UP007 Use `X | Y` for type annotations help: Convert to `X | Y`

Check failure on line 17 in mapcat/alembic/versions/cd9bc4ba5bc0_change_pointing_model_to_pydantic_model.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (UP007)

mapcat/alembic/versions/cd9bc4ba5bc0_change_pointing_model_to_pydantic_model.py:17:16: UP007 Use `X | Y` for type annotations help: Convert to `X | Y`
depends_on: Union[str, Sequence[str], None] = None

Check failure on line 18 in mapcat/alembic/versions/cd9bc4ba5bc0_change_pointing_model_to_pydantic_model.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (UP007)

mapcat/alembic/versions/cd9bc4ba5bc0_change_pointing_model_to_pydantic_model.py:18:13: UP007 Use `X | Y` for type annotations help: Convert to `X | Y`

Check failure on line 18 in mapcat/alembic/versions/cd9bc4ba5bc0_change_pointing_model_to_pydantic_model.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (UP007)

mapcat/alembic/versions/cd9bc4ba5bc0_change_pointing_model_to_pydantic_model.py:18:13: UP007 Use `X | Y` for type annotations help: Convert to `X | Y`


def upgrade() -> None:
Expand All @@ -25,14 +25,10 @@
batch.drop_column("ra_offset")
batch.drop_column("dec_offset")

pass


def downgrade() -> None:
with op.batch_alter_table("depth_one_pointing_residuals") as batch:
batch.add_column(sa.Column("ra_offset", sa.Float, nullable=False))
batch.add_column(sa.Column("dec_offset", sa.Float, nullable=False))

batch.drop_column("residual_model")

pass
18 changes: 10 additions & 8 deletions mapcat/database/pointing_residual.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,30 @@
Table containing pointing residuals.
"""

from sqlmodel import Field, Relationship, SQLModel

from mapcat.pointing.const import ConstantPointingModel

from mapcat.pointing.base import PointingModelStats

from .depth_one_map import DepthOneMapTable
from .json import JSONEncodedPydantic

Check failure on line 12 in mapcat/database/pointing_residual.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (I001)

mapcat/database/pointing_residual.py:5:1: I001 Import block is un-sorted or un-formatted help: Organize imports

Check failure on line 12 in mapcat/database/pointing_residual.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (I001)

mapcat/database/pointing_residual.py:5:1: I001 Import block is un-sorted or un-formatted help: Organize imports


class PointingResidualTable(SQLModel, table=True):
"""
Table for tracking Pointing error for a depth one map,
computed by comparing positions of PSes in that map to
their known possitions
their known positions

Attributes
----------
id : str
Internal ID of the pointing error
map_name : str
Name of depth 1 map being tracked. Foreign into DepthOneMap
residual_model: PointingModel
map_id : int
Internal ID of the depth one map
residual_model: ConstantPointingModel
The pointing model to actually store in the database.

residual_stats: PointingModelStats
Statistics about the pointing residuals, such as mean and stddev of RA and Dec offsets
"""

__tablename__ = "depth_one_pointing_residuals"
Expand All @@ -36,8 +37,9 @@
foreign_key="depth_one_maps.map_id",
ondelete="CASCADE",
)

residual_model: ConstantPointingModel = Field(
discriminator="model_type", sa_type=JSONEncodedPydantic(ConstantPointingModel)
)
residual_stats: PointingModelStats | None = Field( nullable=True, sa_type=JSONEncodedPydantic(PointingModelStats)
)
map: DepthOneMapTable = Relationship(back_populates="pointing_residual")
12 changes: 11 additions & 1 deletion mapcat/pointing/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,30 @@
Base pointing model.
"""

from abc import ABC, abstractmethod
from typing import Literal

from astropy.coordinates import SkyCoord
from astropydantic import AstroPydanticQuantity
from astropy import units as u
from pydantic import BaseModel

Check failure on line 11 in mapcat/pointing/base.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (I001)

mapcat/pointing/base.py:5:1: I001 Import block is un-sorted or un-formatted help: Organize imports

Check failure on line 11 in mapcat/pointing/base.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (I001)

mapcat/pointing/base.py:5:1: I001 Import block is un-sorted or un-formatted help: Organize imports


class PointingModelStats(BaseModel):
mean_ra_offset: AstroPydanticQuantity[u.deg] | None = None
mean_dec_offset: AstroPydanticQuantity[u.deg] | None = None
stddev_ra_offset: AstroPydanticQuantity[u.deg] | None = None
stddev_dec_offset: AstroPydanticQuantity[u.deg] | None = None
n_sources: int | None = None


class PointingModelProtocol(ABC, BaseModel):
model_type: Literal["protocol"] = "protocol"

@abstractmethod
def predict(self, pos: SkyCoord) -> SkyCoord:
"""
Use the pointing model to predict the underlying sky coordinate
from the one intiially predicted in the map.
from the one initially predicted in the map.
"""
...
7 changes: 3 additions & 4 deletions mapcat/pointing/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,16 @@
from typing import Literal

from astropy.coordinates import SkyCoord
from astropy.units import deg
from astropy import units as u
from astropydantic import AstroPydanticQuantity

from mapcat.pointing.base import PointingModelProtocol


class ConstantPointingModel(PointingModelProtocol):
model_type: Literal["constant"] = "constant"

ra_offset: AstroPydanticQuantity[deg]
dec_offset: AstroPydanticQuantity[deg]
ra_offset: AstroPydanticQuantity[u.deg]
dec_offset: AstroPydanticQuantity[u.deg]

def predict(self, pos: SkyCoord) -> SkyCoord:
ra = pos.ra + self.ra_offset
Expand Down
39 changes: 35 additions & 4 deletions mapcat/toolkit/reset.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,20 @@
from sqlalchemy import select
from sqlalchemy.orm import sessionmaker

from mapcat.database import DepthOneMapTable, TimeDomainProcessingTable
from mapcat.database import (
DepthOneMapTable,
PointingResidualTable,
TimeDomainProcessingTable,
)

VALID_STATUSES = ["failed", "completed", "permafail"]

HELP_TEXT = """Use this utility to reset processing statuses in the
TimeDomainProcessingTable. Statuses can be set to 'failed', 'completed', or
'permafail' (which tells the pipeline not to retry the map due to a
pathological failure), or removed entirely by not specifying a target status.
TimeDomainProcessingTable.
Also deletes any associated PointingResidualTable entries if status is None (default).
Statuses can be set to 'failed', 'completed', or 'permafail'
(which tells the pipeline not to retry the map due to a pathological failure),
or removed entirely by not specifying a target status.

Entries to reset can be filtered by map ID, time range (using the map's ctime),
and/or current processing status.
Expand Down Expand Up @@ -80,6 +86,31 @@ def core(session: sessionmaker, args: ap.Namespace):
if args.status is None:
for entry in entries:
cur_session.delete(entry)

# remove the associated pointing residuals as well
pr_stmt = select(PointingResidualTable)
if args.map_id:
pr_stmt = pr_stmt.where(PointingResidualTable.map_id.in_(args.map_id))
if args.from_status is not None:
pr_stmt = pr_stmt.join(
TimeDomainProcessingTable,
PointingResidualTable.map_id == TimeDomainProcessingTable.map_id,
).where(
TimeDomainProcessingTable.processing_status == args.from_status
)
if args.start_time is not None or args.end_time is not None:
pr_stmt = pr_stmt.join(
DepthOneMapTable,
PointingResidualTable.map_id == DepthOneMapTable.map_id,
)
if args.start_time is not None:
pr_stmt = pr_stmt.where(DepthOneMapTable.ctime >= args.start_time)
if args.end_time is not None:
pr_stmt = pr_stmt.where(DepthOneMapTable.ctime <= args.end_time)

pointing_residuals = cur_session.execute(pr_stmt).scalars().all()
for pr in pointing_residuals:
cur_session.delete(pr)
Comment thread
axf295 marked this conversation as resolved.
else:
for entry in entries:
entry.processing_status = args.status
Expand Down
19 changes: 14 additions & 5 deletions tests/test_mapcat.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Test the core functions
"""

from astropy import units as u
import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
Expand All @@ -16,6 +17,7 @@
TimeDomainProcessingTable,
TODDepthOneTable,
)
from mapcat.pointing.const import ConstantPointingModel


def run_migration(database_path: str):
Expand Down Expand Up @@ -98,9 +100,12 @@ def test_create_depth_one(database_sessionmaker):
processing_status="done",
map_id=map_id,
)

pointing_model = ConstantPointingModel(
ra_offset=1.2 * u.deg, dec_offset=-0.8 * u.deg
)
pointing_residual = PointingResidualTable(
ra_offset=1.2, dec_offset=-0.8, map_id=map_id
map_id=map_id,
residual_model=pointing_model,
Comment thread
axf295 marked this conversation as resolved.
)

tod = TODDepthOneTable(
Expand Down Expand Up @@ -170,8 +175,8 @@ def test_create_depth_one(database_sessionmaker):

assert point.pointing_residual_id == point_id
assert point.map_id == map_id
assert point.ra_offset == 1.2
assert point.dec_offset == -0.8
assert point.residual_model.ra_offset == 1.2 * u.deg
assert point.residual_model.dec_offset == -0.8 * u.deg

assert tod.tod_id == tod_id
assert tod.pwv == 0.7
Expand Down Expand Up @@ -218,6 +223,7 @@ def test_add_remove_child_tables(database_sessionmaker):
# Create a depth one map
with database_sessionmaker() as session:
dmap = DepthOneMapTable(
map_id=42,
map_name="myDepthOne2",
map_path="/PATH/TO/DEPTH/ONE2",
tube_slot="OTi1",
Expand All @@ -234,8 +240,11 @@ def test_add_remove_child_tables(database_sessionmaker):
map=dmap,
)

pointing_model = ConstantPointingModel(
ra_offset=1.2 * u.deg, dec_offset=-0.8 * u.deg
)
pointing_residual = PointingResidualTable(
ra_offset=1.2, dec_offset=-0.8, map=dmap
map_id=dmap.map_id, residual_model=pointing_model
Comment thread
axf295 marked this conversation as resolved.
)

tod = TODDepthOneTable(
Expand Down
Loading