diff --git a/CHANGES.rst b/CHANGES.rst
index 9569b69fb..882d91991 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -75,7 +75,7 @@ scripts
source_detection
----------------
-- Support for PSF fitting (optional) for accurate centroids. [#841]
+- Support for PSF fitting (optional) for accurate centroids. [#841, #984]
- Save source catalog to a structured array. [#987]
diff --git a/docs/conf.py b/docs/conf.py
index f49764ae1..eeb0eedf4 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -69,6 +69,8 @@ def check_sphinx_version(expected_version):
"matplotlib": ("http://matplotlib.org/", None),
"gwcs": ("https://gwcs.readthedocs.io/en/latest/", None),
"astropy": ("https://docs.astropy.org/en/stable/", None),
+ "photutils": ("https://photutils.readthedocs.io/en/stable/", None),
+ "webbpsf": ("https://webbpsf.readthedocs.io/en/latest/", None),
}
if sys.version_info[0] == 2:
diff --git a/docs/roman/source_detection/arguments.rst b/docs/roman/source_detection/arguments.rst
index edffce08e..7323cca7f 100644
--- a/docs/roman/source_detection/arguments.rst
+++ b/docs/roman/source_detection/arguments.rst
@@ -1,11 +1,13 @@
Arguments
=========
+
The source detection fitting step has several arguments. These can be specified
by the user by passing them to the step in a Python session, or setting them
in a parameter file.
* ``--kernel_fwhm``: A parameter for DAOStarFinder: size of Gaussian kernel in
- pixels. Default is 2.0.
+ pixels. By default the FWHM is assumed to be the diffraction
+ limited PSF, given the filter used for this observation.
* ``--sharplo``: A parameter for DAOStarFinder: lower bound for sharpness.
Default is 0.0.
* ``--sharphi``: A parameter for DAOStarFinder: upper bound for sharpness.
@@ -42,7 +44,7 @@ in a parameter file.
* ``--bkg_boxsize``: If using `calc_threshold_img` size of box in pixels for
2D background / threshold images and if using
calc_threshold_2d, the size of the box used when detecting
- sources. Default is 3.
+ sources. Default is 9.
* ``--bkg_sigma``: If using `calc_threshold_img`, n sigma for sigma clipping
for background calculation. Default is 2.0.
* ``--bkg_filter_size``: If using `calc_threshold_img` or `calc_threshold_2d`,
@@ -55,3 +57,5 @@ in a parameter file.
will be saved as a numpy array with four dimensions.
In order, these represent source ID, x centroid
position, y centroid position, and flux.
+* ``--fit_psf``: If True, fit a PSF model to each detected source for more precise
+ source centroids and fluxes.
diff --git a/docs/roman/source_detection/description.rst b/docs/roman/source_detection/description.rst
index 9f8a07963..21b37c150 100644
--- a/docs/roman/source_detection/description.rst
+++ b/docs/roman/source_detection/description.rst
@@ -3,14 +3,35 @@ Description
The source detection step produces catalogs of point-like sources for use by the
Tweakreg step for image alignment. It uses DAOStarFinder to detect point sources
-in the image.
+in the image, with an option to subsequently fit PSF models to the detected
+sources for more precise centroids and fluxes.
+Detecting Sources
+-----------------
+
+Sources are detected using `~photutils.detection.DAOStarFinder` from
+`photutils `_, which is an
+implementation of the method `DAOFIND` from
+`Stetson (1987) `_.
+The algorithm can be provided limits on the source flux, radius, roundness,
+sharpness, and background.
+
+PSF Fitting
+-----------
+
+Star finding algorithms like `~photutils.detection.DAOStarFinder` provide
+approximate stellar centroids. More precise centroids may be inferred by
+fitting model PSFs to the observations. Setting the SourceDetectionStep's
+option `fit_psf` to True will generate model Roman PSFs with
+`WebbPSF `_, and fit
+those models to each of the sources detected by
+`~photutils.detection.DAOStarFinder`. More details are in :doc:`psf`.
Outputs / Returns
-=================
+-----------------
By default, the resulting source catalog will be temporarily attached to the
-output ImageModel in the `meta.source_catalog.tweakreg_catalog` attribute as 4D
+output ImageModel in the `meta.source_catalog.tweakreg_catalog` attribute as
numpy array representing, in order, source ID, x centroid position, y centroid
position, and flux. This catalog will then be deleted from the model in the
Tweakreg step.
@@ -28,7 +49,7 @@ only be saved if it does not contain an attached catalog - to do this, use the
separately.
Options for Thresholding
-========================
+------------------------
The DAOStarFinder routine detects point-like sources in an image that are above
a certain, specified floating point threshold. This step provides several options
@@ -44,10 +65,10 @@ threshold value for the entire image based on the sigma-clipped average
(mean, median, or mode) background level of the whole image.
Other Options
-=============
+-------------
Limiting maximum number of sources
-----------------------------------
+++++++++++++++++++++++++++++++++++
By default, all detected sources will be returned in the final output catalog.
If you wish to limit the number of sources, this can be done with the
diff --git a/docs/roman/source_detection/index.rst b/docs/roman/source_detection/index.rst
index 2dc35264f..31aa52bfd 100644
--- a/docs/roman/source_detection/index.rst
+++ b/docs/roman/source_detection/index.rst
@@ -9,5 +9,8 @@ Source Detection
description.rst
arguments.rst
+ psf.rst
.. automodapi:: romancal.source_detection
+
+.. automodapi:: romancal.lib.psf
diff --git a/docs/roman/source_detection/psf.rst b/docs/roman/source_detection/psf.rst
new file mode 100644
index 000000000..56f4af506
--- /dev/null
+++ b/docs/roman/source_detection/psf.rst
@@ -0,0 +1,29 @@
+PSF Fitting
+===========
+
+A few PSF fitting utilities are included to interface between observations
+within Roman datamodels and methods within dependencies that generate and
+fit PSF models to observations.
+
+Create PSF models
+-----------------
+
+`~romancal.lib.psf.create_gridded_psf_model` computes a gridded PSF model for
+a given detector using `~webbpsf.gridded_library.CreatePSFLibrary` from
+`WebbPSF `_. The defaults are chosen to
+balance more accurate PSF models with the cost of increased runtime. For
+further reading on the WebbPSF approach to ePSFs, see the WebbPSF docs on
+`Using PSF Grids `_.
+
+Fit model PSFs to an ImageModel
+-------------------------------
+
+Once PSF models are generated, you can fit those models to observations
+in an ImageModel with `~romancal.lib.psf.fit_psf_to_image_model` using
+`photutils `_.
+By default the fits are done with `~photutils.psf.PSFPhotometry`, and
+crowded fields may benefit from using `~photutils.psf.IterativePSFPhotometry`.
+For neighboring sources that are near one another on the detector, grouping
+the sources and fitting their PSFs simultaneously improves the fit quality.
+Initial guesses for target centroids can be given or source
+detection can be performed with, e.g., `~photutils.detection.DAOStarFinder`.
diff --git a/romancal/lib/psf.py b/romancal/lib/psf.py
index 99e4733a7..227d6c4b2 100644
--- a/romancal/lib/psf.py
+++ b/romancal/lib/psf.py
@@ -24,6 +24,12 @@
from romancal.lib.dqflags import pixel as roman_dq_flag_map
+__all__ = [
+ "create_gridded_psf_model",
+ "fit_psf_to_image_model",
+ "dq_to_boolean_mask",
+]
+
# set loggers to debug level by default:
log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)
@@ -68,7 +74,7 @@ def create_gridded_psf_model(
):
"""
Compute a gridded PSF model for one SCA via
- `webbpsf.gridded_library.CreatePSFLibrary`.
+ `~webbpsf.gridded_library.CreatePSFLibrary`.
Parameters
----------
@@ -210,7 +216,7 @@ def fit_psf_to_image_model(
exclude_out_of_bounds=True,
):
"""
- Fit PSF models to an ImageModel.
+ Fit PSF models to an ``ImageModel``.
Parameters
----------