dfpsf is a Python package for performing PSF matching using the Stochastic Gradient Langevin Dynamics (SGLD) algorithm. The primary use-case envisaged is the matching of two point spread functions (PSFs) in astronomical imaging; the code will find the optimal convolutional kernels that minimize the difference between the two PSFs. Mathematically, we can formulate this problem as trying to solve the following equation:
where we are solving for PyTorch in order to reduce computational costs and allow for GPU acceleration. For information on the algorithm behind this code, please see our paper.
In order to use this code, please do the following after downloading this repo.
- Create the conda environment
conda env create -f environment.yml
- Install the package
pip install -e .
Assuming that you have already computed PSFs from two images, you can use dfpsf to find the optimal kernels with the following command:
from dfpsf.doubleKernel.matchingPSF import optimize_psf_langevin
best_params, _ = optimize_psf_langevin(source_psf, target_psf, cen=source_psf.shape[1] / 2, n_steps=2000, device='cuda')This command will return the parameters of the optimal kernels.
For a more indepth demonstration of the standard dfpsf workflow, please see demo/demo1.ipynb.
In demo, we have several examples demonstrating how the SGLD algorithm can solve instructive toy problems in both the single kernel and double kernel cases.
We have the following double kernel examples (see demo/doubleKernel):
-
Both PSFs are identical and simple Gaussians. The convolutional kernels are points.
-
PSF 2 is a puffier version of PSF 1.
-
PSF 2 is a convolution of PSF 1 with an elliptical Gaussian with non-trivial stretching and position angle
-
PSF 1 and PSF 2 are both puffy versions of the original Gaussian source. PSF 2 is still a convolution of PSF 1 with an elliptical Gaussian with non-trivial streching and position angle
-
PSF 1 and and PSF 2 are convolved by different elliptical Gaussian kernels with non-trivial stretching and position angles.
Similarly, we repeat toy problems 1-3 for the single kernel case (see demo/singleKernel).
In order to use the PSF matching tool in real data, we need a method for extracting the PSF from an image (or a tile of an image).
To do this, we use the photutils library.
The code works as follows:
- Using DAOStarFinder, find all the stars in the image
- Use
photutils.psf.extract_starsto extract the psfs from the stars - Use
photutils.psf.EPSFBuilderto create the final ePSF
After running the code ePSF.create_espf.py, you will have a psf for a tiled region.
Please note that we assume a constant background over the tiled region when creating the PSFs.
You can find a demo of this functionality in demo/ePSF-Creation.
- In case the user is interested in solving the classical single PSF convolution problem, we provide the required functionality in
dfpsf.singleKernel. The function can be called with the following:
from dfpsf.singleKernel.matchingPSF_single import optimize_psf_langevin
best_params, _ = optimize_psf_langevin(source_psf, target_psf, cen=source_psf.shape[1] / 2, n_steps=2000, device='cuda')The package includes comprehensive unit and integration tests that are automatically run via GitHub Actions on every push and pull request. To run the tests locally:
# Run all tests
pytest
# Run only unit tests
pytest tests/unit
# Run only integration tests
pytest tests/integrationCI Pipeline: Unit tests run first with coverage checking (minimum 80%). If they pass, integration tests are then executed. Integration tests are based on the toy problems in the demo/ directory.
We used Claude.ai to help generate the unit tests. However, all the source code was written by us.