Skip to content

Commit

Permalink
Merge pull request #12 from soar-telescope/fix_error_on_incomplete_data
Browse files Browse the repository at this point in the history
- Now the program will not crash and will return None, I'm not fully convinced this is the best solution for this but and idea is described in #13
  • Loading branch information
simontorres authored Apr 25, 2023
2 parents 4e6cae6 + 3876dda commit 4501b8d
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 28 deletions.
3 changes: 2 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
'matplotlib.sphinxext.plot_directive',
]

autoclass_content = 'both'
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

Expand All @@ -55,7 +56,7 @@
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'test_', 'tests']


# -- Options for HTML output -------------------------------------------------
Expand Down
7 changes: 7 additions & 0 deletions docs/source/modules.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
triplespec_focus
================

.. toctree::
:maxdepth: 4

triplespec_focus
45 changes: 45 additions & 0 deletions docs/source/triplespec_focus.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
triplespec\_focus package
=========================

Subpackages
-----------

.. toctree::
:maxdepth: 4

triplespec_focus.tests

Submodules
----------

triplespec\_focus.entrypoints module
------------------------------------

.. automodule:: triplespec_focus.entrypoints
:members:
:undoc-members:
:show-inheritance:

triplespec\_focus.triplespec\_focus module
------------------------------------------

.. automodule:: triplespec_focus.triplespec_focus
:members:
:undoc-members:
:show-inheritance:

triplespec\_focus.utils module
------------------------------

.. automodule:: triplespec_focus.utils
:members:
:undoc-members:
:show-inheritance:

Module contents
---------------

.. automodule:: triplespec_focus
:members:
:undoc-members:
:show-inheritance:
7 changes: 5 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ docs =
sphinx
sphinx-rtd-theme

[coverage:run]
omit=*/tests/*,*__init__.py

[options.entry_points]
triplespec_focus = triplespec_focus.run_triplespec_focus

Expand All @@ -71,6 +74,6 @@ triplespec_focus = triplespec_focus.run_triplespec_focus
# W504 line break after binary operator
# W505 doc line too long
[flake8]
max-line-length = 100
max-doc-length = 79
max-line-length = 120
max-doc-length = 120
ignore = E126,E127,E128,E201,E202,E203,E221,E225,E226,E227,E231,E251,E501,E731,E741,W503,W504,W505
5 changes: 1 addition & 4 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ setenv =
NPY_LAPACK_ORDER=

# Pass through the following environment variables which may be needed for the CI
passenv = HOME WINDIR LC_ALL LC_CTYPE CC CI
passenv = HOME,WINDIR,LC_ALL,LC_CTYPE,CC,CI

# Run the tests in a temporary directory to make sure that we don't import
# this package from the source tree
Expand Down Expand Up @@ -66,7 +66,6 @@ extras =
alldeps: all

commands =
pip freeze
!cov: pytest --pyargs triplespec_focus {toxinidir}/docs {posargs}
cov: pytest --pyargs triplespec_focus {toxinidir}/docs --cov triplespec_focus --cov-config={toxinidir}/setup.cfg {posargs}

Expand All @@ -75,15 +74,13 @@ changedir = docs
description = invoke sphinx-build to build the HTML docs
extras = docs
commands =
pip freeze
sphinx-build -W -b html . _build/html

[testenv:linkcheck]
changedir = docs
description = check the links in the HTML docs
extras = docs
commands =
pip freeze
sphinx-build -W -b linkcheck . _build/html

[testenv:codestyle]
Expand Down
12 changes: 10 additions & 2 deletions triplespec_focus/tests/test_triplespec_focus.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ def test_get_focus_with_data(self):
self.assertIsInstance(self.tspec_focus.fitted_model, Model)
self.assertIsInstance(results, list)

def test_get_focus_with_fwhm_monotonically_increasing(self):
focus_values = [-1300, -1200, -1100, -1000, -900, -800, -700]
fwhm_values = [1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7]
data = {'focus': focus_values, 'fwhm': fwhm_values}
df = DataFrame(data=data)
self.assertIsNone(self.tspec_focus.best_focus)
self.assertIsNone(self.tspec_focus.fitted_model)
results = self.tspec_focus.get_best_focus(df=df, x_axis_size=2000)
self.assertIsNone(results)

def test_detect_sources(self):
ccd = CCDData.read(self.test_file_name, unit='adu')
self.tspec_focus.show_mask = True
Expand All @@ -89,5 +99,3 @@ def test_call_with_file_list(self):
results = self.tspec_focus(file_list=sorted(data_path.glob(pattern='*.fits')))
self.assertIsInstance(results, dict)
self.assertEqual(len(results), 11)


55 changes: 37 additions & 18 deletions triplespec_focus/triplespec_focus.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
from pandas import DataFrame
from pandas import concat
from pathlib import Path
from photutils import DAOStarFinder
from photutils import CircularAperture
from photutils.detection import DAOStarFinder
from photutils.aperture import CircularAperture

from scipy import optimize
from typing import List, Union
Expand Down Expand Up @@ -59,7 +59,8 @@ def __init__(self,
n_brightest (int): Number of the brightest sources to use for measuring source statistics. Default 5.
saturation (float): Data value at which the detector saturates. Default 40000.
plot_results (bool): If set to True will display information plots at the end. Default False.
debug_plots (bool): If set to True will display several plots useful for debugging or viewing the process. Default False.
debug_plots (bool): If set to True will display several plots useful for debugging or viewing the process.
Default False.
"""

self.best_fwhm = None
Expand Down Expand Up @@ -113,12 +114,15 @@ def __call__(self,
file_list (List, None): Optional file list with files to be used to obtain best focus. Default None.
source_fwhm (float): Full width at half maximum to use for source detection and statistics.
det_threshold (float): Number of standard deviation above median to use as detection threshold. Default 5.0.
mask_threshold (float): Number of standard deviation below median to use as a threshold for masking values. Default 1.
mask_threshold (float): Number of standard deviation below median to use as a threshold for masking values.
Default 1.
n_brightest (int): Number of the brightest sources to use for measuring source statistics. Default 5.
saturation_level (float): Data value at which the detector saturates. Default 40000.
show_mask (bool): If set to True will display masked values in red when debug_plots is also True: Default False.
show_mask (bool): If set to True will display masked values in red when debug_plots is also True:
Default False.
plot_results (bool): If set to True will display information plots at the end. Default False.
debug_plots (bool): If set to True will display several plots useful for debugging or viewing the process. Default False.
debug_plots (bool): If set to True will display several plots useful for debugging or viewing the process.
Default False.
print_all_data (bool): If set to True will print the entire dataset at the end.
Returns:
Expand Down Expand Up @@ -200,17 +204,20 @@ def __call__(self,
for star_id in star_ids:
star_phot = self.sources_df[self.sources_df['id'] == star_id]
interpolated_data = self.get_best_focus(df=star_phot)
all_stars_photometry.append([star_phot, interpolated_data, self.best_focus])
all_focus.append(self.best_focus)
all_fwhm.append(self.best_fwhm)
if interpolated_data:
all_stars_photometry.append([star_phot, interpolated_data, self.best_focus])
if self.best_focus and self.best_fwhm:
all_focus.append(self.best_focus)
all_fwhm.append(self.best_fwhm)

mean_focus = np.mean(all_focus)
median_focus = np.median(all_focus)
focus_std = np.std(all_focus)
mean_fwhm = np.mean(all_fwhm)

best_image_overall = CCDData.read(self.best_image_overall, unit='adu')
self.best_image_fwhm = self.sources_df[self.sources_df['filename'] == os.path.basename(self.best_image_overall)]['fwhm'].mean()
self.best_image_fwhm = self.sources_df[self.sources_df['filename'] == os.path.basename(
self.best_image_overall)]['fwhm'].mean()

focus_data = []
fwhm_data = []
Expand Down Expand Up @@ -276,7 +283,8 @@ def detect_sources(self, ccd: CCDData, debug_plots: bool = False) -> QTable:
Args:
ccd (CCDData): An image with point sources.
debug_plots (bool): If set to True will display the image with the sources. Default False.
debug_plots (bool): If set to True will display the image with the sources.
Default False.
Returns:
An Astropy's QTable containing ids, centroids, focus value and image name.
Expand Down Expand Up @@ -337,11 +345,13 @@ def get_best_focus(self, df: DataFrame, x_axis_size: int = 2000) -> List[np.ndar
"""Obtains the best focus for a single source
Args:
df (DataFrame): Pandas DataFrame containing at least a 'focus' and a 'fwhm' column. The data should belong to a single source.
df (DataFrame): Pandas DataFrame containing at least a 'focus' and a 'fwhm' column.
The data should belong to a single source.
x_axis_size (int): Size of the x-axis used to sample the fitted model. Is not an interpolation size.
Returns:
A list with the x-axis and the sampled data using the fitted model
A list with the x-axis and the sampled data using the fitted model, None if it is not possible to find the
focus.
"""
focus_start = df['focus'].min()
Expand All @@ -353,11 +363,20 @@ def get_best_focus(self, df: DataFrame, x_axis_size: int = 2000) -> List[np.ndar
modeled_data = self.fitted_model(x_axis)
index_of_minimum = np.argmin(modeled_data)
middle_point = x_axis[index_of_minimum]

self.best_focus = optimize.brent(self.fitted_model, brack=(focus_start, middle_point, focus_end))
self.best_fwhm = modeled_data[index_of_minimum]

return [x_axis, modeled_data]
if middle_point == focus_start or middle_point == focus_end:
self.log.warning("The focus vs FWHM curve does not seem to have a V or U shape. Trying by forcing the "
"mean focus as the middle point for Brent's optimization bracket definition.")
middle_point = (focus_start + focus_end) / 2.

self.log.debug(f"Brent optimization bracket, Start (xa): {focus_start} Middle (xb): {middle_point} End (xc): {focus_end}")

try:
self.best_focus = optimize.brent(self.fitted_model, brack=(focus_start, middle_point, focus_end))
self.best_fwhm = modeled_data[index_of_minimum]
self.log.info(f"Found best focus at {self.best_focus} with a fwhm of {self.best_fwhm}")
return [x_axis, modeled_data]
except ValueError as error:
self.log.error(error)


if __name__ == '__main__':
Expand Down
2 changes: 1 addition & 1 deletion triplespec_focus/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from pandas import DataFrame
from typing import Union, List

from photutils import CircularAperture, CircularAnnulus, ApertureStats
from photutils.aperture import CircularAperture, CircularAnnulus, ApertureStats

log = logging.getLogger(__name__)

Expand Down

0 comments on commit 4501b8d

Please sign in to comment.