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

Initial implementation of line matching #182

Open
wants to merge 40 commits into
base: main
Choose a base branch
from

Conversation

tepickering
Copy link
Contributor

This PR adds functions to find and centroid lines in a calibration, e.g. arc lamp, spectrum and then match pixel positions to wavelengths using an input WCS. More work is needed to close the loop between this and the fitting process for wavelength calibration, but this is a start.

This PR also contains come code cleanups and the addition of typing in several places. To take advantage of the significant improvements in typing and type handling in newer versions of python, the minimum python version has been upped to 3.10. The CI and tox configuration has been updated to reflect this.

…d-coding Moffat1D. need Gaussian1D for some test cases
…ct that types/defaults are now self-documented by the code
…input as np.array which needs to happen, anyway
@codecov
Copy link

codecov bot commented Jun 21, 2023

Codecov Report

Attention: 5 lines in your changes are missing coverage. Please review.

Comparison is base (aea9d50) 81.56% compared to head (8994bb9) 82.04%.

Files Patch % Lines
specreduce/__init__.py 50.00% 2 Missing ⚠️
specreduce/line_matching.py 95.45% 2 Missing ⚠️
specreduce/calibration_data.py 97.61% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #182      +/-   ##
==========================================
+ Coverage   81.56%   82.04%   +0.48%     
==========================================
  Files          10       11       +1     
  Lines         998     1047      +49     
==========================================
+ Hits          814      859      +45     
- Misses        184      188       +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

specreduce/line_matching.py Outdated Show resolved Hide resolved
specreduce/tests/test_linelists.py Outdated Show resolved Hide resolved
specreduce/utils/synth_data.py Outdated Show resolved Hide resolved
specreduce/line_matching.py Outdated Show resolved Hide resolved
specreduce/tests/test_line_matching.py Show resolved Hide resolved
@tepickering
Copy link
Contributor Author

i think i successfully merged in the significant changes from #202. the python 3.8 and 3.9 tests did fail as expected so i bumped requires-python to 3.10 as we agreed to initially. i tweaked tox.ini and the workflows accordingly to get the tests to all pass.

@tepickering
Copy link
Contributor Author

this PR has been languishing for a while. i'd like to finally get this in to at least add the type annotation work and up the minimum python to 3.10. there's more work to be done on the line matching and especially wavelength calibration, but at this point it's better off done in new PRs.

Copy link
Contributor

@cshanahan1 cshanahan1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still testing out the functionality, but some initial comments on code.

I noticed that throughout, you've removed some of the numpy style docstrings that describe input parameter type and default, and are instead annotating the function. I think that the annotations are a helpful addition, but that the docstrings should retain the same format as before, making sure defaults are also described.

from specreduce.core import * # noqa
from specreduce.wavelength_calibration import * # noqa

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this necessary? specreduce.__version__ seems to be correct already

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it may not be. i just moved it over from _astropy_init.py as-is so there'd be no surprises. kind of a vestige of the old astropy-helpers, but other coordinated packages like photutils have this in their __init__.py as well, fwiw.

specreduce/line_matching.py Show resolved Hide resolved
# Extra sanity handling to make sure the input Sequence can be converted to an np.array
try:
pixel_positions = np.array(pixel_positions, dtype=float)
except ValueError as e:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a small test for this case to raise the error. Also, as part of sanity checking, maybe add in a check that wcs exists and its spectral.

widths = []
amplitudes = []
for r in detected_lines:
g_init = models.Gaussian1D(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will cause an error when the model is evaluated unless stddev, mean, and amp are all in the same unit. Stddev is in pixels but it looks like amplitude will have the same unit as flux?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's units-aware and is ultimately getting the unit information from the Spectrum1D instance. i was, however, incorrect to assume fwhm should be pixels if not specified. it should be in whatever the spectral_axis unit is in the input spectrum.

@@ -88,44 +90,47 @@
]


def get_available_line_catalogs() -> dict:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would maybe add a call to this to one of the existing tests (like in test_line_matching.py) to make sure this function is covered in tests.

@@ -2,21 +2,23 @@
Utilities for defining, loading, and handling spectroscopic calibration data
"""

import os
import warnings
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sort import alphabetically (move warnings import down, and astropy.coordinates up)

'pypeit': PYPEIT_CALIBRATION_LINELISTS
}


def get_reference_file_path(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused by this function. If you don't give it a path, it quietly does nothing. It's also not clear if something is being overwritten or not. Also, it seems to only allow you to download something to a .specreduce directory (which it creates), or if you set cache to False it puts it in /var/. Would it make sense to allow specifying an output directory? Probably out of the scope of this, but I had never used this before reviewing this PR and it stood out to me as confusing.


catalog_pixels = spectral_wcs.world_to_pixel(catalog_wavelengths)
separations = pixel_positions[:, np.newaxis] - catalog_pixels
matched_loc = np.where(np.abs(separations) < tolerance)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe raise a warning when there are no matches? it might be more informative than just returning an empty table

@@ -0,0 +1,110 @@
import pytest
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alphabetize imports

'CRVAL2': 0, # Reference value
'CDELT2': 1 # Spatial units per pixel
}
non_linear_wcs = WCS(header=non_linear_header)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it make sense to also test this with GWCS since the function can accept either? im not sure if thats overkill

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i am working on a draft PR to formally add gwcs as a dependency (it is currently an import in one place, but not a defined dependency in pyproject.toml), add tests to cover its use in addition to astropy.wcs, and add better examples of its use in contexts like this. so, yes, it definitely makes sense, but it's out of scope for this PR.

@tepickering
Copy link
Contributor Author

still testing out the functionality, but some initial comments on code.

I noticed that throughout, you've removed some of the numpy style docstrings that describe input parameter type and default, and are instead annotating the function. I think that the annotations are a helpful addition, but that the docstrings should retain the same format as before, making sure defaults are also described.

this was a very intentional change. now that type annotations are supported, they should be the preferred way to document typing and defaults. it is a much more flexible and powerful way of doing so. e.g., it empowers compilers to optimize the code using the given information which is important in contexts such as GPUs. what we definitely DO NOT WANT is to have redundant information in two places because it is guaranteed to create inconsistencies in the future.

@cshanahan1
Copy link
Contributor

still testing out the functionality, but some initial comments on code.
I noticed that throughout, you've removed some of the numpy style docstrings that describe input parameter type and default, and are instead annotating the function. I think that the annotations are a helpful addition, but that the docstrings should retain the same format as before, making sure defaults are also described.

this was a very intentional change. now that type annotations are supported, they should be the preferred way to document typing and defaults. it is a much more flexible and powerful way of doing so. e.g., it empowers compilers to optimize the code using the given information which is important in contexts such as GPUs. what we definitely DO NOT WANT is to have redundant information in two places because it is guaranteed to create inconsistencies in the future.

That makes sense not to keep in in two places. So this is the new best practice then? If so maybe we can make a PR to do this across the whole package so its not in the diff here, that should be fairly quick to do

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants