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
11 changes: 9 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,15 @@
Release Notes
=============

.. 0.8.13 (unreleased)
===================

0.9.0 (06-Mar-2026)
Comment thread
mcara marked this conversation as resolved.
====================

- Added ``ST_V2V3_WCSCorrector`` class to serve as a base class to support
both JWST and Roman space telescopes' WCS corrections in V2-V3 frame. [#243]

- Added ``RomanWCSCorrector`` class to support Roman WCS corrections. [#243]


0.8.12 (08-Oct-2025)
====================
Expand Down
51 changes: 42 additions & 9 deletions tweakwcs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,45 @@
__version__ = ''


from .correctors import (WCSCorrector, JWSTWCSCorrector, # noqa: F401
FITSWCSCorrector, # noqa: F401
TPWCS, JWSTgWCS, FITSWCS) # noqa: F401
from .matchutils import MatchCatalogs, XYXYMatch, TPMatch # noqa: F401
from .imalign import fit_wcs, align_wcs # noqa: F401
from .wcsimage import (convex_hull, RefCatalog, WCSImageCatalog, # noqa: F401
WCSGroupCatalog) # noqa: F401
from .linalg import inv # noqa: F401
from .linearfit import iter_linear_fit, build_fit_matrix # noqa: F401
from .correctors import (
WCSCorrector,
FITSWCSCorrector,
ST_V2V3_WCSCorrector,
JWSTWCSCorrector,
RomanWCSCorrector,
)
from .matchutils import XYXYMatch, MatchCatalogs, MatchSourceConfusionError
from .imalign import fit_wcs, align_wcs
from .wcsimage import (
convex_hull,
RefCatalog,
WCSImageCatalog,
WCSGroupCatalog,
)
from .linalg import inv
from .linearfit import iter_linear_fit, build_fit_matrix

# import deprecated classes:
from .correctors import TPWCS, JWSTgWCS, FITSWCS # noqa: F401
from .matchutils import TPMatch # noqa: F401


__all__ = [
'FITSWCSCorrector',
'JWSTWCSCorrector',
'MatchCatalogs',
'MatchSourceConfusionError',
'RefCatalog',
'RomanWCSCorrector',
'ST_V2V3_WCSCorrector',
'WCSCorrector',
'WCSGroupCatalog',
'WCSImageCatalog',
'XYXYMatch',
'align_wcs',
'build_fit_matrix',
'convex_hull',
'fit_wcs',
'inv',
'iter_linear_fit',
]
84 changes: 57 additions & 27 deletions tweakwcs/correctors.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@

__author__ = 'Mihai Cara'

__all__ = ['WCSCorrector', 'JWSTWCSCorrector', 'FITSWCSCorrector']
__all__ = [
'FITSWCSCorrector',
'JWSTWCSCorrector',
'RomanWCSCorrector',
'ST_V2V3_WCSCorrector',
'WCSCorrector',
]

log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)
Expand Down Expand Up @@ -557,14 +563,16 @@ def _get_submodel(model, name):
return None


class JWSTWCSCorrector(WCSCorrector):
""" A class for holding ``JWST`` ``gWCS`` information and for managing
tangent-plane corrections. The units of the tangent plane of this
corrector are ``arcsec`` and the axes are not along parallel to the
axes of the detector's coordinate system.
class ST_V2V3_WCSCorrector(WCSCorrector):
""" A class for holding ``GWCS`` information and for managing STScI
``JWST`` and ``Roman`` Space Telescopes V2-V3 tangent-plane corrections.
The units of the tangent plane of this corrector are ``arcsec`` and
the axes are not along parallel to the axes of the detector's
coordinate system.

"""
units = 'arcsec'
corrector_name = 'STScI GWCS V2-V3 tangent-plane linear correction. v1'

def __init__(self, wcs, wcsinfo, meta=None):
"""
Expand Down Expand Up @@ -615,7 +623,7 @@ def __init__(self, wcs, wcsinfo, meta=None):
wcs.pipeline[frms.index('v2v3corr') - 1].transform
)
self._default_tpcorr = None
if not JWSTWCSCorrector._check_tpcorr_structure(self._tpcorr):
if not self._check_tpcorr_structure(self._tpcorr):
raise ValueError("Unsupported tangent-plance correction type.")

# check that transformation parameters are consistent with
Expand All @@ -632,28 +640,29 @@ def __init__(self, wcs, wcsinfo, meta=None):
"WCS/TPCorr parameters 'v2ref', 'v3ref', and/or 'roll' "
"differ from the corresponding reference values."
)
self._partial_tpcorr = JWSTWCSCorrector._v2v3_to_tpcorr_from_full(
self._partial_tpcorr = ST_V2V3_WCSCorrector._v2v3_to_tpcorr_from_full(
self._tpcorr
)

else:
self._v23name = 'v2v3vacorr' if 'v2v3vacorr' in frms else 'v2v3'
self._tpcorr = None
self._default_tpcorr = JWSTWCSCorrector._tpcorr_init(
self._default_tpcorr = ST_V2V3_WCSCorrector._tpcorr_init(
v2_ref=v2_ref / 3600.0,
v3_ref=v3_ref / 3600.0,
roll_ref=roll_ref
roll_ref=roll_ref,
corrector_name=self.corrector_name
)
self._partial_tpcorr = JWSTWCSCorrector._v2v3_to_tpcorr_from_full(
self._partial_tpcorr = ST_V2V3_WCSCorrector._v2v3_to_tpcorr_from_full(
self._default_tpcorr
)

self._update_transformations()

@staticmethod
def _check_tpcorr_structure(tpcorr):
@classmethod
def _check_tpcorr_structure(cls, tpcorr):
# implement a more sophisticated check later
if tpcorr.name != 'JWST tangent-plane linear correction. v1':
if tpcorr.name != cls.corrector_name:
return False
return True

Expand Down Expand Up @@ -694,7 +703,7 @@ def _tpcorr_combine_affines(tpcorr, matrix, shift):
tpcorr.inverse['tp_affine_inv'].translation = -np.dot(invm, t)

@staticmethod
def _tpcorr_init(v2_ref, v3_ref, roll_ref):
def _tpcorr_init(v2_ref, v3_ref, roll_ref, corrector_name="Unnamed"):
s2c = SphericalToCartesian(name='s2c', wrap_lon_at=180)
c2s = CartesianToSpherical(name='c2s', wrap_lon_at=180)

Expand Down Expand Up @@ -730,13 +739,13 @@ def _tpcorr_init(v2_ref, v3_ref, roll_ref):
unit_conv | s2c | rot | c2tan | affine |
tan2c | rot_inv | c2s | unit_conv_inv
)
total_corr.name = 'JWST tangent-plane linear correction. v1'
total_corr.name = corrector_name

inv_total_corr = (
unit_conv | s2c | rot | c2tan | affine_inv |
tan2c | rot_inv | c2s | unit_conv_inv
)
inv_total_corr.name = 'Inverse JWST tangent-plane linear correction. v1'
inv_total_corr.name = f'Inverse {corrector_name}'

# TODO
# re-enable circular inverse definitions once
Expand Down Expand Up @@ -811,9 +820,9 @@ def set_correction(self, matrix=[[1, 0], [0, 1]], shift=[0, 0],
"""
Sets a tangent-plane correction of the GWCS object according to
the provided liniar parameters. In addition, this function updates
the ``meta`` attribute of the `JWSTWCSCorrector` object with the values
of keyword arguments except for the argument ``meta`` which is
*merged* with the *attribute* ``meta``.
the ``meta`` attribute of the `ST_V2V3_WCSCorrector` object with
the values of keyword arguments except for the argument ``meta``
which is *merged* with the *attribute* ``meta``.

Parameters
----------
Expand All @@ -836,7 +845,7 @@ def set_correction(self, matrix=[[1, 0], [0, 1]], shift=[0, 0],
Dictionary that will be merged to the object's ``meta`` fields.

**kwargs: optional parameters
Optional parameters for the WCS corrector. `JWSTWCSCorrector`
Optional parameters for the WCS corrector. `ST_V2V3_WCSCorrector`
ignores these arguments (except for storing them in the ``meta``
attribute).

Expand All @@ -856,19 +865,20 @@ def set_correction(self, matrix=[[1, 0], [0, 1]], shift=[0, 0],
# if original WCS did not have tangent-plane corrections, create
# new correction and add it to the WCs pipeline:
if self._tpcorr is None:
self._tpcorr = JWSTWCSCorrector._tpcorr_init(
self._tpcorr = ST_V2V3_WCSCorrector._tpcorr_init(
v2_ref=self._wcsinfo['v2_ref'] / 3600.0,
v3_ref=self._wcsinfo['v3_ref'] / 3600.0,
roll_ref=self._wcsinfo['roll_ref']
roll_ref=self._wcsinfo['roll_ref'],
corrector_name=self.corrector_name
)

JWSTWCSCorrector._tpcorr_combine_affines(
ST_V2V3_WCSCorrector._tpcorr_combine_affines(
self._tpcorr,
matrix,
_ARCSEC2RAD * np.asarray(shift)
)

self._partial_tpcorr = JWSTWCSCorrector._v2v3_to_tpcorr_from_full(
self._partial_tpcorr = ST_V2V3_WCSCorrector._v2v3_to_tpcorr_from_full(
self._tpcorr
)

Expand All @@ -887,13 +897,13 @@ def set_correction(self, matrix=[[1, 0], [0, 1]], shift=[0, 0],
else:
# combine old and new corrections into a single one and replace
# old transformation with the combined correction transformation:
JWSTWCSCorrector._tpcorr_combine_affines(
ST_V2V3_WCSCorrector._tpcorr_combine_affines(
self._tpcorr,
matrix,
_ARCSEC2RAD * np.asarray(shift)
)

self._partial_tpcorr = JWSTWCSCorrector._v2v3_to_tpcorr_from_full(
self._partial_tpcorr = ST_V2V3_WCSCorrector._v2v3_to_tpcorr_from_full(
self._tpcorr
)

Expand Down Expand Up @@ -1043,6 +1053,26 @@ def bounding_box(self):
return self._owcs.pixel_bounds


class JWSTWCSCorrector(ST_V2V3_WCSCorrector):
""" A class for holding ``JWST`` ``gWCS`` information and for managing
V2-V3 tangent-plane corrections. The units of the tangent plane of this
corrector are ``arcsec`` and the axes are not along/parallel to the
axes of the detector's coordinate system.
"""
units = 'arcsec'
corrector_name = 'JWST tangent-plane linear correction. v1'


class RomanWCSCorrector(ST_V2V3_WCSCorrector):
""" A class for holding ``Roman`` ``GWCS`` information and for managing
V2-V3 tangent-plane corrections. The units of the tangent plane of this
corrector are ``arcsec`` and the axes are not along/parallel to the
axes of the detector's coordinate system.
"""
units = 'arcsec'
corrector_name = 'Roman V2-V3 tangent-plane linear correction. v1'


@deprecated(since='0.8.0', alternative='WCSCorrector')
class TPWCS(WCSCorrector):
pass
Expand Down
28 changes: 21 additions & 7 deletions tweakwcs/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,32 @@
from astropy import wcs as fitswcs
from astropy.table import Table

from tweakwcs.correctors import (
JWSTWCSCorrector,
RomanWCSCorrector,
ST_V2V3_WCSCorrector,
)
from tweakwcs.linearfit import build_fit_matrix
from . helper_correctors import make_mock_jwst_wcs
from . helper_correctors import make_mock_st_wcs


@pytest.fixture(scope='module')
def mock_jwst_wcs():
@pytest.fixture(
scope='module',
params=[ST_V2V3_WCSCorrector, JWSTWCSCorrector, RomanWCSCorrector]
)
def mock_st_wcs(request):
Comment thread
mcara marked this conversation as resolved.
corr_cls = request.param
cd = build_fit_matrix((36, 47), 1e-5)
w = make_mock_jwst_wcs(
v2ref=123.0, v3ref=500.0, roll=115.0, crpix=[512.0, 512.0],
cd=cd, crval=[82.0, 12.0]
w = make_mock_st_wcs(
corr_cls=corr_cls,
v2ref=123.0,
v3ref=500.0,
roll=115.0,
crpix=[512.0, 512.0],
cd=cd,
crval=[82.0, 12.0]
)
return w
return w, corr_cls


@pytest.fixture(scope='function')
Expand Down
Loading
Loading