Skip to content

Commit

Permalink
Link up available Dipole outputs into DiaSource table.
Browse files Browse the repository at this point in the history
Match ipdiffim catalog typing.

Add new dipole columns to tests.

Add columns and tests for dipFlux

Add isDipole columne

Add length to unittest.

Debug unittest and typing.

Add SNR calculation and test.

Remove SNR calculations and fix up dipSep doc string.
  • Loading branch information
morriscb committed Apr 8, 2019
1 parent c324c4c commit 375c819
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 11 deletions.
18 changes: 18 additions & 0 deletions data/association-flag-map.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,21 @@ columns:
- name: ip_diffim_forced_PsfFlux_flag_edge
bit: 22
doc: Forced PSF flux object was too close to the edge of the image to use the full PSF model.
- name: slot_Shape_flag
bit: 23
doc: General Failure Flag
- name: slot_Shape_flag_unweightedBad
bit: 24
doc: Both weighted and unweighted moments were invalid
- name: slot_Shape_flag_unweighted
bit: 25
doc: Weighted moments converged to an invalid value; using unweighted moments
- name: slot_Shape_flag_shift
bit: 26
doc: centroid shifted by more than the maximum allowed amount
- name: slot_Shape_flag_maxIter
bit: 27
doc: Too many iterations in adaptive moments
- name: slot_Shape_flag_psf
bit: 28
doc: Failure in measuring PSF model shape
4 changes: 4 additions & 0 deletions data/ppdb-ap-pipe-schema-extra.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -584,3 +584,7 @@ columns:
type: INT
nullable: false
description: id number of stored filter.
- name: isDipole
type: BOOL
nullable: true
description: Object determined to be a dipole.
8 changes: 5 additions & 3 deletions python/lsst/ap/association/afwUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -672,15 +672,15 @@ def make_dia_source_schema():
doc='Estimated uncertainty of fpBkgd.')
schema.addField('ixx', type='D',
doc='Adaptive second moment of the source intensity.')
schema.addField('ixxErr', type='D',
schema.addField('ixxErr', type='F',
doc='Uncertainty of ixx.')
schema.addField('iyy', type='D',
doc='Adaptive second moment of the source intensity.')
schema.addField('iyyErr', type='D',
schema.addField('iyyErr', type='F',
doc='Uncertainty of iyy.')
schema.addField('ixy', type='D',
doc='Adaptive second moment of the source intensity.')
schema.addField('ixyErr', type='D',
schema.addField('ixyErr', type='F',
doc='Uncertainty of ixy.')
schema.addField('ixx_iyy_Cov', type='D',
doc='Covariance of ixx and iyy.')
Expand Down Expand Up @@ -714,6 +714,8 @@ def make_dia_source_schema():
schema.addField("filterId", type='L',
doc='Obs package id of the filter this source was '
'observed in.')
schema.addField("isDipole", type='Flag',
doc='Object determined to be a dipole.')
return schema


Expand Down
87 changes: 80 additions & 7 deletions python/lsst/ap/association/mapApData.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,20 @@ class MapDiaSourceConfig(pexConfig.Config):
"slot_ApFlux_instFluxErr": "apFluxErr",
"slot_PsfFlux_instFlux": "psFlux",
"slot_PsfFlux_instFluxErr": "psFluxErr",
"ip_diffim_DipoleFit_orientation": "dipAngle",
"ip_diffim_DipoleFit_chi2dof": "dipChi2",
"ip_diffim_forced_PsfFlux_instFlux": "totFlux",
"ip_diffim_forced_PsfFlux_instFluxErr": "totFluxErr"}
"ip_diffim_forced_PsfFlux_instFluxErr": "totFluxErr",
"ip_diffim_DipoleFit_flag_classification": "isDipole",
"slot_Shape_xx": "ixx",
"slot_Shape_xxErr": "ixxErr",
"slot_Shape_yy": "iyy",
"slot_Shape_yyErr": "iyyErr",
"slot_Shape_xy": "ixy",
"slot_Shape_xyErr": "ixyErr",
"slot_PsfShape_xx": "ixxPSF",
"slot_PsfShape_yy": "iyyPSF",
"slot_PsfShape_xy": "ixyPSF"}
)
calibrateColumns = pexConfig.ListField(
dtype=str,
Expand All @@ -133,6 +145,18 @@ class MapDiaSourceConfig(pexConfig.Config):
"data",
"association-flag-map.yaml"),
)
dipFluxPrefix = pexConfig.Field(
dtype=str,
doc="Prefix of the Dipole measurement column containing negative and "
"positive flux lobes.",
default="ip_diffim_DipoleFit",
)
dipSepColumn = pexConfig.Field(
dtype=str,
doc="Column of the separation of the negative and positive poles of "
"the dipole.",
default="ip_diffim_DipoleFit_separation"
)


class MapDiaSourceTask(MapApDataTask):
Expand Down Expand Up @@ -190,7 +214,7 @@ def run(self, inputCatalog, exposure):
Parameters
----------
inputCatalog: `lsst.afw.table.SourceCatalog`
inputCatalog : `lsst.afw.table.SourceCatalog`
Input catalog with data to be copied into new output catalog.
exposure: `lsst.afw.image.Exposure`
Exposure with containing the PhotoCalib object relevant to this catalog.
Expand All @@ -204,6 +228,7 @@ def run(self, inputCatalog, exposure):
midPointTaiMJD = visit_info.getDate().get(system=DateTime.MJD)
filterId = exposure.getFilter().getId()
filterName = exposure.getFilter().getName()
wcs = exposure.getWcs()

photoCalib = exposure.getPhotoCalib()

Expand All @@ -213,7 +238,9 @@ def run(self, inputCatalog, exposure):
for inputRecord in inputCatalog:
outputRecord = outputCatalog.addNew()
outputRecord.assign(inputRecord, self.mapper)
self.computeSnr(inputRecord, outputRecord)
self.calibrateFluxes(inputRecord, outputRecord, photoCalib)
self.computeDipoleFluxes(inputRecord, outputRecord, photoCalib)
self.bitPackFlags(inputRecord, outputRecord)

outputRecord.set("ccdVisitId", ccdVisitId)
Expand All @@ -231,11 +258,11 @@ def calibrateFluxes(self, inputRecord, outputRecord, photoCalib):
Parameters
----------
inputRecord: `lsst.afw.table.SourceRecord`
inputRecord : `lsst.afw.table.SourceRecord`
Record to copy flux values from.
outputRecord: `lsst.afw.table.SourceRecord`
outputRecord : `lsst.afw.table.SourceRecord`
Record to copy and calibrate values into.
photoCalib: `lsst.afw.image.PhotoCalib`
photoCalib : `lsst.afw.image.PhotoCalib`
Calibration object from the difference exposure.
"""
for col_name in self.config.calibrateColumns:
Expand All @@ -246,15 +273,61 @@ def calibrateFluxes(self, inputRecord, outputRecord, photoCalib):
self.config.copyColumns[col_name + "_instFluxErr"],
meas.error)

def computeDipoleFluxes(self, inputRecord, outputRecord, photoCalib):
"""Calibrate and compute dipole mean flux and diff flux.
Parameters
----------
inputRecord : `lsst.afw.table.SourceRecord`
Record to copy flux values from.
outputRecord : `lsst.afw.table.SourceRecord`
Record to copy and calibrate values into.
photoCalib `lsst.afw.image.PhotoCalib`
Calibration object from the difference exposure.
"""

neg_meas = photoCalib.instFluxToNanojansky(
inputRecord, self.config.dipFluxPrefix + "_neg")
pos_meas = photoCalib.instFluxToNanojansky(
inputRecord, self.config.dipFluxPrefix + "_pos")
outputRecord.set(
"dipMeanFlux",
0.5 * (np.abs(neg_meas.value) + np.abs(pos_meas.value)))
outputRecord.set(
"dipMeanFluxErr",
0.5 * np.sqrt(neg_meas.error ** 2 + pos_meas.error ** 2))
outputRecord.set(
"dipFluxDiff",
np.abs(pos_meas.value) - np.abs(neg_meas.value))
outputRecord.set(
"dipFluxDiffErr",
np.sqrt(neg_meas.error ** 2 + pos_meas.error ** 2))

def computeDipoleSep(self, inputRecord, outputRecord, wcs):
"""Convert the dipole separation from pixels to arcseconds.
Parameters
----------
inputRecord : `lsst.afw.table.SourceRecord`
Record to copy flux values from.
outputRecord : `lsst.afw.table.SourceRecord`
Record to copy and calibrate values into.
wcs : `lsst.afw.geom.SkyWcs`
Wcs of image inputRecords was observed.
"""
pixScale = wcs.getPixelScale(inputRecord.getCentroid())
dipSep = pixScale * inputRecord.get(self.config.dipSepColumn)
outputRecord.set("dipLength", dipSep.asArcseconds())

def bitPackFlags(self, inputRecord, outputRecord):
"""Pack requested flag columns in inputRecord into single columns in
outputRecord.
Parameters
----------
inputRecord: `lsst.afw.table.SourceRecord`
inputRecord : `lsst.afw.table.SourceRecord`
Record to copy flux values from.
outputRecord: `lsst.afw.table.SourceRecord`
outputRecord : `lsst.afw.table.SourceRecord`
Record to copy and calibrate values into.
"""
for outputFlag in self.bit_pack_columns:
Expand Down
20 changes: 19 additions & 1 deletion tests/test_map_ap_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ def make_input_source_catalog(n_objects, add_flags=False):
schema.addField("base_NaiveCentroid_y", type="D")
schema.addField("base_PsfFlux_instFlux", type="D")
schema.addField("base_PsfFlux_instFluxErr", type="D")
schema.addField("ip_diffim_DipoleFit_separation", type="D")
schema.addField("ip_diffim_DipoleFit_orientation", type="D")
schema.addField("ip_diffim_DipoleFit_neg_instFlux", type="D")
schema.addField("ip_diffim_DipoleFit_neg_instFluxErr", type="D")
schema.addField("ip_diffim_DipoleFit_pos_instFlux", type="D")
schema.addField("ip_diffim_DipoleFit_pos_instFluxErr", type="D")
schema.addField("ip_diffim_forced_PsfFlux_instFlux", type="D")
schema.addField("ip_diffim_forced_PsfFlux_instFluxErr", type="D")
if add_flags:
schema.addField("base_PixelFlags_flag", type="Flag")
schema.addField("base_PixelFlags_flag_offimage", type="Flag")
Expand All @@ -67,6 +75,8 @@ def make_input_source_catalog(n_objects, add_flags=False):
for subSchema in schema:
if isinstance(obj.get(subSchema.getKey()), geom.Angle):
obj.set(subSchema.getKey(), 1. * geom.degrees)
elif subSchema.getField().getName() == "ip_diffim_DipoleFit_neg_instFlux":
obj.set(subSchema.getKey(), -1)
else:
obj.set(subSchema.getKey(), 1)
return objects
Expand Down Expand Up @@ -159,6 +169,10 @@ def test_run_dia_source(self):
config=mapApDConfig)
outputCatalog = mapApD.run(self.inputCatalog, self.exposure)

expectedMeanDip = 2.
expectedDiffFlux = 0.
expectedLength = 0.18379083

for inObj, outObj in zip(self.inputCatalog, outputCatalog):
self.assertEqual(
outObj["ccdVisitId"],
Expand All @@ -171,10 +185,14 @@ def test_run_dia_source(self):
outObj["flags"],
1 * 2 ** 0 + 1 * 2 ** 1)
for inputName, outputName in mapApDConfig.copyColumns.items():
if inputName.startswith("slot"):
if inputName.startswith("slot_PsfFlux"):
self._test_calibrated_flux(inObj, outObj)
else:
self.assertEqual(inObj[inputName], outObj[outputName])
self.assertEqual(outObj["dipMeanFlux"], expectedMeanDip)
self.assertEqual(outObj["dipFluxDiff"], expectedDiffFlux)
self.assertAlmostEqual(outObj["dipLength"], expectedLength)
self.assertEqual(outObj["snr"], 1)

def _create_map_dia_source_config(self):
"""Create a test config for use in MapDiaSourceTask.
Expand Down

0 comments on commit 375c819

Please sign in to comment.