Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
8c43c12
adding non linear spectral dispersion code
A-Derks Apr 30, 2026
cfd3d4e
adding chagelog
A-Derks Apr 30, 2026
2e45f28
linting
A-Derks Apr 30, 2026
de99978
Merge branch 'main' into non_linear_spectral_dispersion
A-Derks May 27, 2026
e8cadfd
renaming variables, adding better docstrongs
A-Derks Jun 1, 2026
a3c8938
making new class to hold grating function
A-Derks Jun 3, 2026
774fd73
Merge branch 'main' into non_linear_spectral_dispersion
A-Derks Jun 9, 2026
5893f99
moving generate_grating_spectral_transform stuff out of this repo. It…
A-Derks Jun 9, 2026
19a35c7
moving alpha_in and alpha_out computations here
A-Derks Jun 11, 2026
86d9515
refactoring models stuff into smaller functions
A-Derks Jun 11, 2026
7f9ba37
adding extra grism params
A-Derks Jun 15, 2026
3dd823f
Update grating spectral wrapper for new gwcs grating-mode inputs
A-Derks Jun 15, 2026
543f6cb
Restore composed alpha_out grating transforms in dkist against the up…
A-Derks Jun 16, 2026
67e66ae
Potential fix for pull request finding
A-Derks Jun 16, 2026
caef8c4
Potential fix for pull request finding
A-Derks Jun 16, 2026
5913d79
Merge branch 'main' into non_linear_spectral_dispersion
A-Derks Jun 16, 2026
f1aaae2
moved refracted_angle_sine_model guts to gwcs repo, now there is just…
A-Derks Jun 18, 2026
e49ed4f
Merge branch 'main' into non_linear_spectral_dispersion
A-Derks Jun 18, 2026
375cf78
moved refracted_angle_sine_model guts to gwcs repo, now there is just…
A-Derks Jun 18, 2026
368cb6b
removed computing of reference_refracted_angle and grism_parameter_pe…
A-Derks Jun 30, 2026
0540894
Merge branch 'main' into non_linear_spectral_dispersion
A-Derks Jun 30, 2026
c4dc59d
removed unused refracted_angle_sine_model, added documentation about …
A-Derks Jun 30, 2026
58e262c
Merge branch 'main' into non_linear_spectral_dispersion
A-Derks Jun 30, 2026
4633621
Merge branch 'non_linear_spectral_dispersion' of https://github.com/D…
A-Derks Jun 30, 2026
d566b1f
removed unused refracted_angle_sine_model, added documentation about …
A-Derks Jun 30, 2026
569b0a3
removing unnecessary addition to build_grating_spectral_transform doc…
A-Derks Jul 1, 2026
b102d5d
Merge branch 'main' into non_linear_spectral_dispersion
A-Derks Jul 6, 2026
2fc780b
Let us all be more tolerant of numbers very close to zero (#739)
SolarDrew Jul 7, 2026
0dbf7ee
Bump OpenAstronomy/github-actions-workflows/.github/workflows/publish…
dependabot[bot] Jul 7, 2026
24929e6
Forward ignore_version_mismatch for string, iterable and Results inpu…
astrofrog Jul 7, 2026
1f307bd
Bump actions/checkout from 6 to 7 (#732)
dependabot[bot] Jul 7, 2026
2dbe78c
Bump CodspeedHQ/action from 4.17.0 to 4.18.1 (#733)
dependabot[bot] Jul 7, 2026
436a055
[pre-commit.ci] pre-commit autoupdate (#740)
pre-commit-ci[bot] Jul 17, 2026
1f149ed
WavelengthFromGratingEquation --> WavelengthFromGrismEquaion (change …
A-Derks Jul 20, 2026
381cf27
Merge branch 'main' into non_linear_spectral_dispersion
A-Derks Aug 26, 2026
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
6 changes: 3 additions & 3 deletions .github/workflows/build_workshop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ jobs:
if: ${{ !startsWith(github.ref_name, 'v') }}
run: echo 'BRANCHNAME=latest' >> "$GITHUB_ENV"

- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
persist-credentials: false

- name: Setup Python 3.13
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.13"

Expand All @@ -50,7 +50,7 @@ jobs:
run: pip install -U pipx

- name: Clone workshop repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: true
repository: 'DKISTDC/DKIST-Workshop'
Expand Down
1 change: 1 addition & 0 deletions changelog/713.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added `dkist.wcs.models.build_grating_spectral_transform()` to build FITS ``-GRA``/``-GRI`` spectral transforms by composing a constant incident-angle term, `~gwcs.spectroscopy.RefractedAngleSineModel`, and `~gwcs.spectroscopy.WavelengthFromGrismEquation` using the FITS-facing grating parameters.
53 changes: 53 additions & 0 deletions dkist/wcs/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import astropy.units as u
from astropy.modeling import CompoundModel, Model, Parameter, separable
from astropy.utils.decorators import deprecated_renamed_argument
from gwcs.spectroscopy import RefractedAngleSineModel, WavelengthFromGrismEquation

from dkist.utils.decorators import deprecated
from dkist.utils.exceptions import DKISTDeprecationWarning
Expand All @@ -30,11 +31,63 @@
"VaryingCelestialTransform",
"VaryingCelestialTransform2D",
"VaryingCelestialTransform3D",
"build_grating_spectral_transform",
"generate_celestial_transform",
"varying_celestial_transform_from_tables",
]


def build_grating_spectral_transform(
reference_pixel: float,
reference_wavelength: u.Quantity,
dispersion: u.Quantity,
groove_density: u.Quantity,
spectral_order: u.Quantity,
incident_angle: u.Quantity,
refractive_index: u.Quantity = 1 * u.one,
refractive_index_derivative: u.Quantity = 0 / u.m,
out_of_plane_angle: u.Quantity = 0 * u.deg,
camera_angle: u.Quantity = 0 * u.deg,
Comment thread
eigenbrot marked this conversation as resolved.
) -> CompoundModel:
"""
Build a FITS grating spectral transform from header-derived parameters.

Composes a constant incident-angle sine term (`~astropy.modeling.models.Const1D`),
a pixel-dependent refracted-angle sine model
(`~gwcs.spectroscopy.RefractedAngleSineModel`), and
`~gwcs.spectroscopy.WavelengthFromGrismEquation`, following the FITS
grating/grism spectral-coordinate formalism described by Greisen et al.
(2006). The input and output angles are computed from the Greisen
relations within the component models:
https://scixplorer.org/abs/2006A%26A...446..747G/abstract
"""
model = WavelengthFromGrismEquation(
groove_density=groove_density,
spectral_order=spectral_order,
reference_wavelength=reference_wavelength,
refractive_index=refractive_index,
refractive_index_derivative=refractive_index_derivative,
out_of_plane_angle=out_of_plane_angle,
)

alpha_in = m.Const1D(amplitude=np.sin(incident_angle))

alpha_out = RefractedAngleSineModel(
reference_pixel=reference_pixel,
reference_wavelength=reference_wavelength,
dispersion=dispersion,
groove_density=groove_density,
spectral_order=spectral_order,
incident_angle=incident_angle,
refractive_index=refractive_index,
refractive_index_derivative=refractive_index_derivative,
out_of_plane_angle=out_of_plane_angle,
camera_angle=camera_angle,
)

return m.Mapping((0, 0)) | (alpha_in & alpha_out) | model


def generate_celestial_transform(
crpix: Iterable[float] | u.Quantity,
cdelt: Iterable[float] | u.Quantity,
Expand Down
48 changes: 47 additions & 1 deletion dkist/wcs/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
from astropy.coordinates.matrix_utilities import rotation_matrix
from astropy.modeling import CompoundModel
from astropy.modeling.models import Tabular1D
from astropy.wcs import WCS
from gwcs.spectroscopy import RefractedAngleSineModel

from dkist.wcs.models import (AsymmetricMapping, Ravel, Unravel, VaryingCelestialTransform,
VaryingCelestialTransform2D, VaryingCelestialTransform3D,
generate_celestial_transform, update_celestial_transform_parameters,
build_grating_spectral_transform, generate_celestial_transform,
update_celestial_transform_parameters,
varying_celestial_transform_from_tables)


Expand Down Expand Up @@ -52,6 +55,49 @@ def test_generate_celestial_unitless():
assert u.allclose(shift1.offset, 0)


def test_build_grating_spectral_transform() -> None:
header = {
"CTYPE1": "AWAV-GRA",
"CUNIT1": "nm",
"CRPIX1": 218,
"CRVAL1": 854.1738582455826,
"CDELT1": 0.0022975580183395555,
"PV1_0": 23000.0,
"PV1_1": 90,
"PV1_2": 65.696,
"PV1_3": 1.25,
"PV1_4": 1000.0,
"PV1_5": 1.5,
"PV1_6": 0.8,
}
transform = build_grating_spectral_transform(
reference_pixel=header["CRPIX1"] - 1,
reference_wavelength=header["CRVAL1"] * u.nm,
dispersion=header["CDELT1"] * u.nm / u.pix,
groove_density=header["PV1_0"] / u.m,
spectral_order=header["PV1_1"] * u.one,
incident_angle=header["PV1_2"] * u.deg,
refractive_index=header["PV1_3"] * u.one,
refractive_index_derivative=header["PV1_4"] / u.m,
out_of_plane_angle=header["PV1_5"] * u.deg,
camera_angle=header["PV1_6"] * u.deg,
)

pixels = np.array([0, 100, 217, 300, 511], dtype=float)
expected = WCS(header).spectral.pixel_to_world(pixels)
result = transform(pixels)

assert isinstance(transform, CompoundModel)
# The transform should contain the gwcs refracted-angle model.
assert any(
isinstance(sm, RefractedAngleSineModel)
for sm in transform.traverse_postorder()
)
np.testing.assert_allclose(
result.to_value(u.nm), expected.to_value(u.nm), rtol=1e-10, atol=1e-10
)


def test_update_celestial():
trsfm = generate_celestial_transform(
crpix=[0, 0] * u.pix,
Expand Down
Loading