LSST Applications g0b6bd0c080+a72a5dd7e6,g1182afd7b4+2a019aa3bb,g17e5ecfddb+2b8207f7de,g1d67935e3f+06cf436103,g38293774b4+ac198e9f13,g396055baef+6a2097e274,g3b44f30a73+6611e0205b,g480783c3b1+98f8679e14,g48ccf36440+89c08d0516,g4b93dc025c+98f8679e14,g5c4744a4d9+a302e8c7f0,g613e996a0d+e1c447f2e0,g6c8d09e9e7+25247a063c,g7271f0639c+98f8679e14,g7a9cd813b8+124095ede6,g9d27549199+a302e8c7f0,ga1cf026fa3+ac198e9f13,ga32aa97882+7403ac30ac,ga786bb30fb+7a139211af,gaa63f70f4e+9994eb9896,gabf319e997+ade567573c,gba47b54d5d+94dc90c3ea,gbec6a3398f+06cf436103,gc6308e37c7+07dd123edb,gc655b1545f+ade567573c,gcc9029db3c+ab229f5caf,gd01420fc67+06cf436103,gd877ba84e5+06cf436103,gdb4cecd868+6f279b5b48,ge2d134c3d5+cc4dbb2e3f,ge448b5faa6+86d1ceac1d,gecc7e12556+98f8679e14,gf3ee170dca+25247a063c,gf4ac96e456+ade567573c,gf9f5ea5b4d+ac198e9f13,gff490e6085+8c2580be5c,w.2022.27
LSST Data Management Base Package
setPrimaryFlags.py
Go to the documentation of this file.
1#!/usr/bin/env python
2#
3# LSST Data Management System
4# Copyright 2008-2016 LSST/AURA
5#
6# This product includes software developed by the
7# LSST Project (http://www.lsst.org/).
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the LSST License Statement and
20# the GNU General Public License along with this program. If not,
21# see <http://www.lsstcorp.org/LegalNotices/>.
22#
23import numpy as np
24from lsst.pex.config import Config, Field, ListField
25from lsst.pipe.base import Task
26from lsst.geom import Box2D
27
28
29def getPatchInner(sources, patchInfo):
30 """Set a flag for each source if it is in the innerBBox of a patch.
31
32 Parameters
33 ----------
35 A sourceCatalog with pre-calculated centroids.
36 patchInfo : `lsst.skymap.PatchInfo`
37 Information about a `SkyMap` `Patch`.
38
39 Returns
40 --------
41 isPatchInner : array-like of `bool`
42 `True` for each source that has a centroid
43 in the inner region of a patch.
44 """
45 # Extract the centroid position for all the sources
46 x = sources["slot_Centroid_x"]
47 y = sources["slot_Centroid_y"]
48 centroidFlag = sources["slot_Centroid_flag"]
49
50 # set inner flags for each source and set primary flags for
51 # sources with no children (or all sources if deblend info not available)
52 innerFloatBBox = Box2D(patchInfo.getInnerBBox())
53 inInner = innerFloatBBox.contains(x, y)
54
55 # When the centroider fails, we can still fall back to the peak,
56 # but we don't trust that quite as much -
57 # so we use a slightly smaller box for the patch comparison.
58 shrunkInnerFloatBBox = Box2D(innerFloatBBox)
59 shrunkInnerFloatBBox.grow(-1)
60 inShrunkInner = shrunkInnerFloatBBox.contains(x, y)
61
62 # Flag sources contained in the inner region of a patch
63 isPatchInner = (centroidFlag & inShrunkInner) | (~centroidFlag & inInner)
64 return isPatchInner
65
66
67def getTractInner(sources, tractInfo, skyMap):
68 """Set a flag for each source that the skyMap includes in tractInfo.
69
70 Parameters
71 ----------
73 A sourceCatalog with pre-calculated centroids.
74 tractInfo : `lsst.skymap.TractInfo`
75 Tract object
76 skyMap : `lsst.skymap.BaseSkyMap`
77 Sky tessellation object
78
79 Returns
80 -------
81 isTractInner : array-like of `bool`
82 True if the skyMap.findTract method returns
83 the same tract as tractInfo.
84 """
85 tractId = tractInfo.getId()
86 isTractInner = np.array([skyMap.findTract(s.getCoord()).getId() == tractId for s in sources])
87 return isTractInner
88
89
90def getPseudoSources(sources, pseudoFilterList, schema, log):
91 """Get a flag that marks pseudo sources.
92
93 Some categories of sources, for example sky objects,
94 are not really detected sources and should not be considered primary
95 sources.
96
97 Parameters
98 ----------
100 The catalog of sources for which to identify "pseudo"
101 (e.g. sky) objects.
102 pseudoFilterList : `list` of `str`
103 Names of filters which should never be primary
104
105 Returns
106 -------
107 isPseudo : array-like of `bool`
108 True for each source that is a pseudo source.
109 Note: to remove pseudo sources use `~isPseudo`.
110 """
111 # Filter out sources that should never be primary
112 isPseudo = np.zeros(len(sources), dtype=bool)
113 for filt in pseudoFilterList:
114 try:
115 pseudoFilterKey = schema.find("merge_peak_%s" % filt).getKey()
116 isPseudo |= sources[pseudoFilterKey]
117 except KeyError:
118 log.warning("merge_peak is not set for pseudo-filter %s", filt)
119 return isPseudo
120
121
123 """Get flags generated by the deblender
124
125 scarlet is different than meas_deblender in that it is not
126 (necessarily) flux conserving. For consistency in scarlet,
127 all of the parents with only a single child (isolated sources)
128 need to be deblended. This creates a question: which type
129 of isolated source should we make measurements on, the
130 undeblended "parent" or the deblended child?
131 For that reason we distinguish between a DeblendedSource,
132 which is a source that has no children and uses the
133 isolated parents, and a DeblendedModelSource, which uses
134 the scarlet models for both isolated and blended sources.
135 In the case of meas_deblender, DeblendedModelSource is
136 `None` because it is not contained in the output catalog.
137
138 Parameters
139 ----------
141 A sourceCatalog that has already been deblended using
142 either meas_extensions_scarlet or meas_deblender.
143
144 Returns
145 -------
146 fromBlend : array-like of `bool`
147 True for each source modeled by the deblender from a `Peak`
148 in a parent footprint that contained at least one other `Peak`.
149 While these models can be approximated as isolated,
150 and measurements are made on them as if that's the case,
151 we know deblending to introduce biases in the shape and centroid
152 of objects and it is important to know that the sources that these
153 models are based on are all bleneded in the true image.
154 isIsolated : array-like of `bool`
155 True for isolated sources, regardless of whether or not they
156 were modeled by the deblender.
157 isDeblendedSource : array-like of `bool`
158 True for each source that is a "DeblendedSource" as defined above.
159 isDeblendedModelSource : array-like of `bool`
160 True for each source that is a "DeblendedSourceModel"
161 as defined above.
162 """
163 nChildKey = "deblend_nChild"
164 nChild = sources[nChildKey]
165 parent = sources["parent"]
166
167 if "deblend_scarletFlux" in sources.schema:
168 # The number of peaks in the sources footprint.
169 # This (may be) different than nChild,
170 # the number of deblended sources in the catalog,
171 # because some peaks might have been culled during deblending.
172 nPeaks = sources["deblend_nPeaks"]
173 parentNChild = sources["deblend_parentNChild"]
174 # It is possible for a catalog to contain a hierarchy of sources,
175 # so we mark the leaves (end nodes of the hierarchy tree with no
176 # children).
177 isLeaf = nPeaks == 1
178 fromBlend = parentNChild > 1
179 isIsolated = isLeaf & ((parent == 0) | parentNChild == 1)
180 isDeblendedSource = (fromBlend & isLeaf) | (isIsolated & (parent == 0))
181 isDeblendedModelSource = (parent != 0) & isLeaf
182 else:
183 # Set the flags for meas_deblender
184 fromBlend = parent != 0
185 isIsolated = (nChild == 0) & (parent == 0)
186 isDeblendedSource = nChild == 0
187 isDeblendedModelSource = None
188 return fromBlend, isIsolated, isDeblendedSource, isDeblendedModelSource
189
190
192 nChildKeyName = Field(dtype=str, default="deprecated",
193 doc="Deprecated. This parameter is not used.")
194 pseudoFilterList = ListField(dtype=str, default=['sky'],
195 doc="Names of filters which should never be primary")
196
197
199 """Add isPrimaryKey to a given schema.
200
201 Parameters
202 ----------
203 schema : `lsst.afw.table.Schema`
204 The input schema.
205 isSingleFrame : `bool`
206 Flag specifying if task is operating with single frame imaging.
207 includeDeblend : `bool`
208 Include deblend information in isPrimary and
209 add isDeblendedSource field?
210 kwargs :
211 Keyword arguments passed to the task.
212 """
213
214 ConfigClass = SetPrimaryFlagsConfig
215
216 def __init__(self, schema, isSingleFrame=False, **kwargs):
217 Task.__init__(self, **kwargs)
218 self.schemaschema = schema
219 self.isSingleFrameisSingleFrame = isSingleFrame
220 self.includeDeblendincludeDeblend = False
221 if not self.isSingleFrameisSingleFrame:
222 primaryDoc = ("true if source has no children and is in the inner region of a coadd patch "
223 "and is in the inner region of a coadd tract "
224 "and is not \"detected\" in a pseudo-filter (see config.pseudoFilterList)")
225 self.isPatchInnerKeyisPatchInnerKey = self.schemaschema.addField(
226 "detect_isPatchInner", type="Flag",
227 doc="true if source is in the inner region of a coadd patch",
228 )
229 self.isTractInnerKeyisTractInnerKey = self.schemaschema.addField(
230 "detect_isTractInner", type="Flag",
231 doc="true if source is in the inner region of a coadd tract",
232 )
233 else:
234 primaryDoc = "true if source has no children and is not a sky source"
235 self.isPrimaryKeyisPrimaryKey = self.schemaschema.addField(
236 "detect_isPrimary", type="Flag",
237 doc=primaryDoc,
238 )
239
240 if "deblend_nChild" in schema.getNames():
241 self.includeDeblendincludeDeblend = True
242 self.isDeblendedSourceKeyisDeblendedSourceKey = self.schemaschema.addField(
243 "detect_isDeblendedSource", type="Flag",
244 doc=primaryDoc + " and is either an unblended isolated source or a "
245 "deblended child from a parent with 'deblend_nChild' > 1")
246 self.fromBlendKeyfromBlendKey = self.schemaschema.addField(
247 "detect_fromBlend", type="Flag",
248 doc="This source is deblended from a parent with more than one child."
249 )
250 self.isIsolatedKeyisIsolatedKey = self.schemaschema.addField(
251 "detect_isIsolated", type="Flag",
252 doc="This source is not a part of a blend."
253 )
254 if "deblend_scarletFlux" in schema.getNames():
255 self.isDeblendedModelKeyisDeblendedModelKey = self.schemaschema.addField(
256 "detect_isDeblendedModelSource", type="Flag",
257 doc=primaryDoc + " and is a deblended child")
258 else:
259 self.isDeblendedModelKeyisDeblendedModelKey = None
260
261 def run(self, sources, skyMap=None, tractInfo=None, patchInfo=None):
262 """Set isPrimary and related flags on sources.
263
264 For coadded imaging, the `isPrimary` flag returns True when an object
265 has no children, is in the inner region of a coadd patch, is in the
266 inner region of a coadd trach, and is not detected in a pseudo-filter
267 (e.g., a sky_object).
268 For single frame imaging, the isPrimary flag returns True when a
269 source has no children and is not a sky source.
270
271 Parameters
272 ----------
274 A sourceTable. Reads in centroid fields and an nChild field.
275 Writes is-patch-inner, is-tract-inner, and is-primary flags.
276 skyMap : `lsst.skymap.BaseSkyMap`
277 Sky tessellation object
278 tractInfo : `lsst.skymap.TractInfo`
279 Tract object
280 patchInfo : `lsst.skymap.PatchInfo`
281 Patch object
282 """
283 # Mark whether sources are contained within the inner regions of the
284 # given tract/patch and are not "pseudo" (e.g. sky) sources.
285 if not self.isSingleFrameisSingleFrame:
286 isPatchInner = getPatchInner(sources, patchInfo)
287 isTractInner = getTractInner(sources, tractInfo, skyMap)
288 isPseudo = getPseudoSources(sources, self.config.pseudoFilterList, self.schemaschema, self.log)
289 isPrimary = isTractInner & isPatchInner & ~isPseudo
290
291 sources[self.isPatchInnerKeyisPatchInnerKey] = isPatchInner
292 sources[self.isTractInnerKeyisTractInnerKey] = isTractInner
293 else:
294 # Mark all of the sky sources in SingleFrame images
295 # (if they were added)
296 if "sky_source" in sources.schema:
297 isSky = sources["sky_source"]
298 else:
299 isSky = np.zeros(len(sources), dtype=bool)
300 isPrimary = ~isSky
301
302 if self.includeDeblendincludeDeblend:
303 result = getDeblendPrimaryFlags(sources)
304 fromBlend, isIsolated, isDeblendedSource, isDeblendedModelSource = result
305 sources[self.fromBlendKeyfromBlendKey] = fromBlend
306 sources[self.isIsolatedKeyisIsolatedKey] = isIsolated
307 sources[self.isDeblendedSourceKeyisDeblendedSourceKey] = isDeblendedSource
308 if self.isDeblendedModelKeyisDeblendedModelKey is not None:
309 sources[self.isDeblendedModelKeyisDeblendedModelKey] = isDeblendedModelSource
310 isPrimary = isPrimary & isDeblendedSource
311
312 sources[self.isPrimaryKeyisPrimaryKey] = isPrimary
Defines the fields and offsets for a table.
Definition: Schema.h:51
A floating-point coordinate rectangle geometry.
Definition: Box.h:413
def run(self, sources, skyMap=None, tractInfo=None, patchInfo=None)
def __init__(self, schema, isSingleFrame=False, **kwargs)
def getPatchInner(sources, patchInfo)
def getTractInner(sources, tractInfo, skyMap)
def getPseudoSources(sources, pseudoFilterList, schema, log)