Skip to content

Commit

Permalink
Merge pull request #31 from lsst/tickets/DM-47440
Browse files Browse the repository at this point in the history
DM-47440: Allow Piff to be configured by a YAML string block
  • Loading branch information
arunkannawadi authored Nov 13, 2024
2 parents e33fd44 + 3bd4a5e commit 6f65fb9
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 23 deletions.
62 changes: 39 additions & 23 deletions python/lsst/meas/extensions/piff/piffPsfDeterminer.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import galsim
import re
import logging
import yaml

import lsst.utils.logging
from lsst.afw.cameraGeom import PIXELS, FIELD_ANGLE
Expand Down Expand Up @@ -72,24 +73,29 @@ def _validateGalsimInterpolant(name: str) -> bool:

class PiffPsfDeterminerConfig(BasePsfDeterminerTask.ConfigClass):
spatialOrder = pexConfig.Field[int](
doc="specify spatial order for PSF kernel creation",
doc="Spatial order for PSF kernel creation. "
"Ignored if piffPsfConfigYaml is set.",
default=2,
)
samplingSize = pexConfig.Field[float](
doc="Resolution of the internal PSF model relative to the pixel size; "
"e.g. 0.5 is equal to 2x oversampling",
"e.g. 0.5 is equal to 2x oversampling. This affects only the size of "
"the PSF model stamp if piffPsfConfigYaml is set.",
default=1,
)
modelSize = pexConfig.Field[int](
doc="Internal model size for PIFF (typically odd, but not enforced)",
doc="Internal model size for PIFF (typically odd, but not enforced). "
"Partially ignored if piffPsfConfigYaml is set.",
default=25,
)
outlierNSigma = pexConfig.Field[float](
doc="n sigma for chisq outlier rejection",
doc="n sigma for chisq outlier rejection. "
"Ignored if piffPsfConfigYaml is set.",
default=4.0
)
outlierMaxRemove = pexConfig.Field[float](
doc="Max fraction of stars to remove as outliers each iteration",
doc="Max fraction of stars to remove as outliers each iteration. "
"Ignored if piffPsfConfigYaml is set.",
default=0.05
)
maxSNR = pexConfig.Field[float](
Expand All @@ -109,7 +115,8 @@ class PiffPsfDeterminerConfig(BasePsfDeterminerTask.ConfigClass):
doc="GalSim interpolant name for Piff to use. "
"Options include 'Lanczos(N)', where N is an integer, along with "
"galsim.Cubic, galsim.Delta, galsim.Linear, galsim.Nearest, "
"galsim.Quintic, and galsim.SincInterpolant.",
"galsim.Quintic, and galsim.SincInterpolant. Ignored if "
"piffPsfConfigYaml is set.",
check=_validateGalsimInterpolant,
default="Lanczos(11)",
)
Expand All @@ -134,6 +141,12 @@ class PiffPsfDeterminerConfig(BasePsfDeterminerTask.ConfigClass):
min=0,
max=3,
)
piffPsfConfigYaml = pexConfig.Field[str](
doc="Configuration file for PIFF in YAML format. This overrides many "
"other settings in this config and is not validated ahead of runtime.",
default=None,
optional=True,
)

def setDefaults(self):
super().setDefaults()
Expand Down Expand Up @@ -410,24 +423,27 @@ def determinePsf(
)
stars.append(piff.Star(data, None))

piffConfig = {
'type': "Simple",
'model': {
'type': 'PixelGrid',
'scale': scale * self.config.samplingSize,
'size': self.config.modelSize,
'interp': self.config.interpolant
},
'interp': {
'type': 'BasisPolynomial',
'order': self.config.spatialOrder
},
'outliers': {
'type': 'Chisq',
'nsigma': self.config.outlierNSigma,
'max_remove': self.config.outlierMaxRemove
if self.config.piffPsfConfigYaml is None:
piffConfig = {
'type': 'Simple',
'model': {
'type': 'PixelGrid',
'scale': scale * self.config.samplingSize,
'size': self.config.modelSize,
'interp': self.config.interpolant,
},
'interp': {
'type': 'BasisPolynomial',
'order': self.config.spatialOrder,
},
'outliers': {
'type': 'Chisq',
'nsigma': self.config.outlierNSigma,
'max_remove': self.config.outlierMaxRemove,
}
}
}
else:
piffConfig = yaml.safe_load(self.config.piffPsfConfigYaml)

piffResult = piff.PSF.process(piffConfig)
wcs = {0: gswcs}
Expand Down
32 changes: 32 additions & 0 deletions tests/test_psf.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ def setupDeterminer(
modelSize=25,
debugStarData=False,
useCoordinates='pixel',
piffPsfConfigYaml=None,
downsample=False,
withlog=False,
):
Expand Down Expand Up @@ -284,6 +285,7 @@ def setupDeterminer(

psfDeterminerConfig.debugStarData = debugStarData
psfDeterminerConfig.useCoordinates = useCoordinates
psfDeterminerConfig.piffPsfConfigYaml = piffPsfConfigYaml
if downsample:
psfDeterminerConfig.maxCandidates = 10
if withlog:
Expand Down Expand Up @@ -474,6 +476,10 @@ def testPiffDeterminer_default(self):
def testPiffDeterminer_stampSize27(self):
"""Test Piff with a psf stampSize of 27."""
self.checkPiffDeterminer(stampSize=27)
self.assertEqual(
self.exposure.psf.computeKernelImage(self.exposure.getBBox().getCenter()).getDimensions(),
geom.Extent2I(27, 27),
)

def testPiffDeterminer_debugStarData(self):
"""Test Piff with debugStarData=True."""
Expand Down Expand Up @@ -518,6 +524,32 @@ def testPiffDeterminer_skyCoords_failure(self, angle_degrees=135):
self.checkPiffDeterminer(useCoordinates='sky', stampSize=15)


class piffPsfConfigYamlTestCase(SpatialModelPsfTestCase):
"""A test case to trigger the codepath that uses piffPsfConfigYaml."""

def checkPiffDeterminer(self, **kwargs):
# Docstring inherited.
if "piffPsfConfigYaml" not in kwargs:
piffPsfConfigYaml = """
# A minimal Piff config corresponding to the defaults.
type: Simple
model:
type: PixelGrid
scale: 0.2
size: 25
interp: Lanczos(11)
interp:
type: BasisPolynomial
order: 1
outliers:
type: Chisq
nsigma: 4.0
max_remove: 0.05
"""
kwargs["piffPsfConfigYaml"] = piffPsfConfigYaml
return super().checkPiffDeterminer(**kwargs)


class PiffConfigTestCase(lsst.utils.tests.TestCase):
"""A test case to check for valid Piff config"""
def testValidateGalsimInterpolant(self):
Expand Down

0 comments on commit 6f65fb9

Please sign in to comment.