LSST Applications  21.0.0+04719a4bac,21.0.0-1-ga51b5d4+f5e6047307,21.0.0-11-g2b59f77+a9c1acf22d,21.0.0-11-ga42c5b2+86977b0b17,21.0.0-12-gf4ce030+76814010d2,21.0.0-13-g1721dae+760e7a6536,21.0.0-13-g3a573fe+768d78a30a,21.0.0-15-g5a7caf0+f21cbc5713,21.0.0-16-g0fb55c1+b60e2d390c,21.0.0-19-g4cded4ca+71a93a33c0,21.0.0-2-g103fe59+bb20972958,21.0.0-2-g45278ab+04719a4bac,21.0.0-2-g5242d73+3ad5d60fb1,21.0.0-2-g7f82c8f+8babb168e8,21.0.0-2-g8f08a60+06509c8b61,21.0.0-2-g8faa9b5+616205b9df,21.0.0-2-ga326454+8babb168e8,21.0.0-2-gde069b7+5e4aea9c2f,21.0.0-2-gecfae73+1d3a86e577,21.0.0-2-gfc62afb+3ad5d60fb1,21.0.0-25-g1d57be3cd+e73869a214,21.0.0-3-g357aad2+ed88757d29,21.0.0-3-g4a4ce7f+3ad5d60fb1,21.0.0-3-g4be5c26+3ad5d60fb1,21.0.0-3-g65f322c+e0b24896a3,21.0.0-3-g7d9da8d+616205b9df,21.0.0-3-ge02ed75+a9c1acf22d,21.0.0-4-g591bb35+a9c1acf22d,21.0.0-4-g65b4814+b60e2d390c,21.0.0-4-gccdca77+0de219a2bc,21.0.0-4-ge8a399c+6c55c39e83,21.0.0-5-gd00fb1e+05fce91b99,21.0.0-6-gc675373+3ad5d60fb1,21.0.0-64-g1122c245+4fb2b8f86e,21.0.0-7-g04766d7+cd19d05db2,21.0.0-7-gdf92d54+04719a4bac,21.0.0-8-g5674e7b+d1bd76f71f,master-gac4afde19b+a9c1acf22d,w.2021.13
LSST Data Management Base Package
diaSourceAnalysis.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 #
4 # LSST Data Management System
5 # Copyright 2008-2016 LSST Corporation.
6 #
7 # This product includes software developed by the
8 # LSST Project (http://www.lsst.org/).
9 #
10 # This program is free software: you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation, either version 3 of the License, or
13 # (at your option) any later version.
14 #
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
19 #
20 # You should have received a copy of the LSST License Statement and
21 # the GNU General Public License along with this program. If not,
22 # see <http://www.lsstcorp.org/LegalNotices/>.
23 #
24 
25 __all__ = ["DiaSourceAnalystConfig", "DiaSourceAnalyst"]
26 
27 import lsst.afw.image as afwImage
28 from lsst.log import Log
29 import numpy as num
30 import lsst.pex.config as pexConfig
31 
32 scaling = 5
33 
34 
35 class DiaSourceAnalystConfig(pexConfig.Config):
36  srcBadMaskPlanes = pexConfig.ListField(
37  dtype=str,
38  doc="""Mask planes that lead to an invalid detection.
39  Options: NO_DATA EDGE SAT BAD CR INTRP
40  E.g. : NO_DATA SAT BAD allows CR-masked and interpolated pixels""",
41  default=("NO_DATA", "EDGE", "SAT", "BAD")
42  )
43  fBadPixels = pexConfig.Field(
44  dtype=float,
45  doc="Fraction of bad pixels allowed in footprint",
46  default=0.1
47  )
48  fluxPolarityRatio = pexConfig.Field(
49  dtype=float,
50  doc="Minimum fraction of flux in correct-polarity pixels",
51  default=0.75
52  )
53  nPolarityRatio = pexConfig.Field(
54  dtype=float,
55  doc="Minimum fraction of correct-polarity pixels in unmasked subset",
56  default=0.7
57  )
58  nMaskedRatio = pexConfig.Field(
59  dtype=float,
60  doc="Minimum fraction of correct-polarity unmasked to masked pixels",
61  default=0.6,
62  )
63  nGoodRatio = pexConfig.Field(
64  dtype=float,
65  doc="Minimum fraction of correct-polarity unmasked to all pixels",
66  default=0.5
67  )
68 
69 
71 
72  def __init__(self, config):
73  self.configconfig = config
74  self.loglog = Log.getLogger("ip.diffim.DiaSourceAnalysis")
75 
76  self.bitMaskbitMask = 0
77  srcBadMaskPlanes = self.configconfig.srcBadMaskPlanes
78  for maskPlane in srcBadMaskPlanes:
79  self.bitMaskbitMask |= afwImage.Mask.getPlaneBitMask(maskPlane)
80 
81  def countDetected(self, mask):
82  idxP = num.where(mask & afwImage.Mask.getPlaneBitMask("DETECTED"))
83  idxN = num.where(mask & afwImage.Mask.getPlaneBitMask("DETECTED_NEGATIVE"))
84  return len(idxP[0]), len(idxN[0])
85 
86  def countMasked(self, mask):
87  idxM = num.where(mask & self.bitMaskbitMask)
88  return len(idxM[0])
89 
90  def countPolarity(self, mask, pixels):
91  unmasked = ((mask & self.bitMaskbitMask) == 0)
92  idxP = num.where((pixels >= 0) & unmasked)
93  idxN = num.where((pixels < 0) & unmasked)
94  fluxP = num.sum(pixels[idxP])
95  fluxN = num.sum(pixels[idxN])
96  return len(idxP[0]), len(idxN[0]), fluxP, fluxN
97 
98  def testSource(self, source, subMi):
99  imArr, maArr, varArr = subMi.getArrays()
100  flux = source.getApFlux()
101 
102  nPixels = subMi.getWidth() * subMi.getHeight()
103  nPos, nNeg, fPos, fNeg = self.countPolaritycountPolarity(maArr, imArr)
104  nDetPos, nDetNeg = self.countDetectedcountDetected(maArr)
105  nMasked = self.countMaskedcountMasked(maArr)
106  assert(nPixels == (nMasked + nPos + nNeg))
107 
108  # 1) Too many pixels in the detection are masked
109  fMasked = (nMasked / nPixels)
110  fMaskedTol = self.configconfig.fBadPixels
111  if fMasked > fMaskedTol:
112  self.loglog.debug("Candidate %d : BAD fBadPixels %.2f > %.2f", source.getId(), fMasked, fMaskedTol)
113  return False
114 
115  if flux > 0:
116  # positive-going source
117  fluxRatio = fPos / (fPos + abs(fNeg))
118  ngoodRatio = nPos / nPixels
119  maskRatio = nPos / (nPos + nMasked)
120  npolRatio = nPos / (nPos + nNeg)
121  else:
122  # negative-going source
123  fluxRatio = abs(fNeg) / (fPos + abs(fNeg))
124  ngoodRatio = nNeg / nPixels
125  maskRatio = nNeg / (nNeg + nMasked)
126  npolRatio = nNeg / (nNeg + nPos)
127 
128  # 2) Not enough flux in unmasked correct-polarity pixels
129  fluxRatioTolerance = self.configconfig.fluxPolarityRatio
130  if fluxRatio < fluxRatioTolerance:
131  self.loglog.debug("Candidate %d : BAD flux polarity %.2f < %.2f (pos=%.2f neg=%.2f)",
132  source.getId(), fluxRatio, fluxRatioTolerance, fPos, fNeg)
133  return False
134 
135  # 3) Not enough unmasked pixels of correct polarity
136  polarityTolerance = self.configconfig.nPolarityRatio
137  if npolRatio < polarityTolerance:
138  self.loglog.debug("Candidate %d : BAD polarity count %.2f < %.2f (pos=%d neg=%d)",
139  source.getId(), npolRatio, polarityTolerance, nPos, nNeg)
140  return False
141 
142  # 4) Too many masked vs. correct polarity pixels
143  maskedTolerance = self.configconfig.nMaskedRatio
144  if maskRatio < maskedTolerance:
145  self.loglog.debug("Candidate %d : BAD unmasked count %.2f < %.2f (pos=%d neg=%d mask=%d)",
146  source.getId(), maskRatio, maskedTolerance, nPos, nNeg, nMasked)
147  return False
148 
149  # 5) Too few unmasked, correct polarity pixels
150  ngoodTolerance = self.configconfig.nGoodRatio
151  if ngoodRatio < ngoodTolerance:
152  self.loglog.debug("Candidate %d : BAD good pixel count %.2f < %.2f (pos=%d neg=%d tot=%d)",
153  source.getId(), ngoodRatio, ngoodTolerance, nPos, nNeg, nPixels)
154  return False
155 
156  self.loglog.debug("Candidate %d : OK flux=%.2f nPos=%d nNeg=%d nTot=%d nDetPos=%d nDetNeg=%d "
157  "fPos=%.2f fNeg=%2f",
158  source.getId(), flux, nPos, nNeg, nPixels, nDetPos, nDetNeg, fPos, fNeg)
159  return True
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
Definition: Log.h:706
Angle abs(Angle const &a)
Definition: Angle.h:106