32 __all__ = [
"BaseSelectImagesTask",
"BaseExposureInfo",
"WcsSelectImagesTask",
"PsfWcsSelectImagesTask",
33 "DatabaseSelectImagesConfig",
"BestSeeingWcsSelectImagesTask",
"BestSeeingSelectVisitsTask",
34 "BestSeeingQuantileSelectVisitsTask"]
38 """Base configuration for subclasses of BaseSelectImagesTask that use a database"""
39 host = pexConfig.Field(
40 doc=
"Database server host name",
43 port = pexConfig.Field(
44 doc=
"Database server port",
47 database = pexConfig.Field(
48 doc=
"Name of database",
51 maxExposures = pexConfig.Field(
52 doc=
"maximum exposures to select; intended for debugging; ignored if None",
59 """Data about a selected exposure
63 """Create exposure information that can be used to generate data references
65 The object has the following fields:
66 - dataId: data ID of exposure (a dict)
67 - coordList: ICRS coordinates of the corners of the exposure (list of lsst.geom.SpherePoint)
68 plus any others items that are desired
70 super(BaseExposureInfo, self).
__init__(dataId=dataId, coordList=coordList)
74 """Base task for selecting images suitable for coaddition
76 ConfigClass = pexConfig.Config
77 _DefaultName =
"selectImages"
80 def run(self, coordList):
81 """Select images suitable for coaddition in a particular region
83 @param[in] coordList: list of coordinates defining region of interest; if None then select all images
84 subclasses may add additional keyword arguments, as required
86 @return a pipeBase Struct containing:
87 - exposureInfoList: a list of exposure information objects (subclasses of BaseExposureInfo),
88 which have at least the following fields:
89 - dataId: data ID dictionary
90 - coordList: ICRS coordinates of the corners of the exposure (list of lsst.geom.SpherePoint)
92 raise NotImplementedError()
94 def _runArgDictFromDataId(self, dataId):
95 """Extract keyword arguments for run (other than coordList) from a data ID
97 @return keyword arguments for run (other than coordList), as a dict
99 raise NotImplementedError()
101 def runDataRef(self, dataRef, coordList, makeDataRefList=True, selectDataList=[]):
102 """Run based on a data reference
104 This delegates to run() and _runArgDictFromDataId() to do the actual
105 selection. In the event that the selectDataList is non-empty, this will
106 be used to further restrict the selection, providing the user with
107 additional control over the selection.
109 @param[in] dataRef: data reference; must contain any extra keys needed by the subclass
110 @param[in] coordList: list of coordinates defining region of interest; if None, search the whole sky
111 @param[in] makeDataRefList: if True, return dataRefList
112 @param[in] selectDataList: List of SelectStruct with dataRefs to consider for selection
113 @return a pipeBase Struct containing:
114 - exposureInfoList: a list of objects derived from ExposureInfo
115 - dataRefList: a list of data references (None if makeDataRefList False)
118 exposureInfoList = self.
runrun(coordList, **runArgDict).exposureInfoList
120 if len(selectDataList) > 0
and len(exposureInfoList) > 0:
122 ccdKeys, ccdValues = _extractKeyValue(exposureInfoList)
123 inKeys, inValues = _extractKeyValue([s.dataRef
for s
in selectDataList], keys=ccdKeys)
124 inValues =
set(inValues)
125 newExposureInfoList = []
126 for info, ccdVal
in zip(exposureInfoList, ccdValues):
127 if ccdVal
in inValues:
128 newExposureInfoList.append(info)
130 self.log.
info(
"De-selecting exposure %s: not in selectDataList", info.dataId)
131 exposureInfoList = newExposureInfoList
134 butler = dataRef.butlerSubset.butler
135 dataRefList = [butler.dataRef(datasetType=
"calexp",
136 dataId=expInfo.dataId,
137 )
for expInfo
in exposureInfoList]
141 return pipeBase.Struct(
142 dataRefList=dataRefList,
143 exposureInfoList=exposureInfoList,
147 def _extractKeyValue(dataList, keys=None):
148 """Extract the keys and values from a list of dataIds
150 The input dataList is a list of objects that have 'dataId' members.
151 This allows it to be used for both a list of data references and a
154 assert len(dataList) > 0
156 keys = sorted(dataList[0].dataId.keys())
159 for data
in dataList:
160 thisKeys =
set(data.dataId.keys())
161 if thisKeys != keySet:
162 raise RuntimeError(
"DataId keys inconsistent: %s vs %s" % (keySet, thisKeys))
163 values.append(tuple(data.dataId[k]
for k
in keys))
168 """A container for data to be passed to the WcsSelectImagesTask"""
171 super(SelectStruct, self).
__init__(dataRef=dataRef, wcs=wcs, bbox=bbox)
175 """Select images using their Wcs
177 We use the "convexHull" method of lsst.sphgeom.ConvexPolygon to define
178 polygons on the celestial sphere, and test the polygon of the
179 patch for overlap with the polygon of the image.
181 We use "convexHull" instead of generating a ConvexPolygon
182 directly because the standard for the inputs to ConvexPolygon
183 are pretty high and we don't want to be responsible for reaching them.
186 def runDataRef(self, dataRef, coordList, makeDataRefList=True, selectDataList=[]):
187 """Select images in the selectDataList that overlap the patch
189 This method is the old entry point for the Gen2 commandline tasks and drivers
190 Will be deprecated in v22.
192 @param dataRef: Data reference for coadd/tempExp (with tract, patch)
193 @param coordList: List of ICRS coordinates (lsst.geom.SpherePoint) specifying boundary of patch
194 @param makeDataRefList: Construct a list of data references?
195 @param selectDataList: List of SelectStruct, to consider for selection
198 exposureInfoList = []
200 patchVertices = [coord.getVector()
for coord
in coordList]
203 for data
in selectDataList:
204 dataRef = data.dataRef
208 imageCorners = self.
getValidImageCornersgetValidImageCorners(imageWcs, imageBox, patchPoly, dataId=
None)
210 dataRefList.append(dataRef)
213 return pipeBase.Struct(
214 dataRefList=dataRefList
if makeDataRefList
else None,
215 exposureInfoList=exposureInfoList,
218 def run(self, wcsList, bboxList, coordList, dataIds=None, **kwargs):
219 """Return indices of provided lists that meet the selection criteria
223 wcsList : `list` of `lsst.afw.geom.SkyWcs`
224 specifying the WCS's of the input ccds to be selected
225 bboxList : `list` of `lsst.geom.Box2I`
226 specifying the bounding boxes of the input ccds to be selected
227 coordList : `list` of `lsst.geom.SpherePoint`
228 ICRS coordinates specifying boundary of the patch.
232 result: `list` of `int`
233 of indices of selected ccds
236 dataIds = [
None] * len(wcsList)
237 patchVertices = [coord.getVector()
for coord
in coordList]
240 for i, (imageWcs, imageBox, dataId)
in enumerate(zip(wcsList, bboxList, dataIds)):
241 imageCorners = self.
getValidImageCornersgetValidImageCorners(imageWcs, imageBox, patchPoly, dataId)
247 "Return corners or None if bad"
249 imageCorners = [imageWcs.pixelToSky(pix)
for pix
in geom.Box2D(imageBox).getCorners()]
252 self.log.
debug(
"WCS error in testing calexp %s (%s): deselecting", dataId, e)
256 if imagePoly
is None:
257 self.log.
debug(
"Unable to create polygon from image %s: deselecting", dataId)
260 if patchPoly.intersects(imagePoly):
262 self.log.
info(
"Selecting calexp %s", dataId)
267 "Return median absolute deviation scaled to normally distributed data"
268 return 1.4826*np.median(np.abs(array - np.median(array)))
272 dimensions=(
"tract",
"patch",
"skymap",
"instrument",
"visit"),
273 defaultTemplates={
"coaddName":
"deep"}):
277 class PsfWcsSelectImagesConfig(pipeBase.PipelineTaskConfig,
278 pipelineConnections=PsfWcsSelectImagesConnections):
279 maxEllipResidual = pexConfig.Field(
280 doc=
"Maximum median ellipticity residual",
285 maxSizeScatter = pexConfig.Field(
286 doc=
"Maximum scatter in the size residuals",
290 maxScaledSizeScatter = pexConfig.Field(
291 doc=
"Maximum scatter in the size residuals, scaled by the median size",
296 starSelection = pexConfig.Field(
297 doc=
"select star with this field",
299 default=
'calib_psf_used'
301 starShape = pexConfig.Field(
302 doc=
"name of star shape",
304 default=
'base_SdssShape'
306 psfShape = pexConfig.Field(
307 doc=
"name of psf shape",
309 default=
'base_SdssShape_psf'
314 """Select images using their Wcs and cuts on the PSF properties
316 The PSF quality criteria are based on the size and ellipticity residuals from the
317 adaptive second moments of the star and the PSF.
320 - the median of the ellipticty residuals
321 - the robust scatter of the size residuals (using the median absolute deviation)
322 - the robust scatter of the size residuals scaled by the square of
326 ConfigClass = PsfWcsSelectImagesConfig
327 _DefaultName =
"PsfWcsSelectImages"
329 def runDataRef(self, dataRef, coordList, makeDataRefList=True, selectDataList=[]):
330 """Select images in the selectDataList that overlap the patch and satisfy PSF quality critera.
332 This method is the old entry point for the Gen2 commandline tasks and drivers
333 Will be deprecated in v22.
335 @param dataRef: Data reference for coadd/tempExp (with tract, patch)
336 @param coordList: List of ICRS coordinates (lsst.geom.SpherePoint) specifying boundary of patch
337 @param makeDataRefList: Construct a list of data references?
338 @param selectDataList: List of SelectStruct, to consider for selection
340 result = super(PsfWcsSelectImagesTask, self).runDataRef(dataRef, coordList, makeDataRefList,
344 exposureInfoList = []
345 for dataRef, exposureInfo
in zip(result.dataRefList, result.exposureInfoList):
346 butler = dataRef.butlerSubset.butler
347 srcCatalog = butler.get(
'src', dataRef.dataId)
348 valid = self.isValid(srcCatalog, dataRef.dataId)
352 dataRefList.append(dataRef)
353 exposureInfoList.append(exposureInfo)
355 return pipeBase.Struct(
356 dataRefList=dataRefList,
357 exposureInfoList=exposureInfoList,
360 def run(self, wcsList, bboxList, coordList, srcList, dataIds=None, **kwargs):
361 """Return indices of provided lists that meet the selection criteria
365 wcsList : `list` of `lsst.afw.geom.SkyWcs`
366 specifying the WCS's of the input ccds to be selected
367 bboxList : `list` of `lsst.geom.Box2I`
368 specifying the bounding boxes of the input ccds to be selected
369 coordList : `list` of `lsst.geom.SpherePoint`
370 ICRS coordinates specifying boundary of the patch.
371 srcList : `list` of `lsst.afw.table.SourceCatalog`
372 containing the PSF shape information for the input ccds to be selected
376 goodPsf: `list` of `int`
377 of indices of selected ccds
379 goodWcs = super(PsfWcsSelectImagesTask, self).
run(wcsList=wcsList, bboxList=bboxList,
380 coordList=coordList, dataIds=dataIds)
384 dataIds = [
None] * len(srcList)
385 for i, (srcCatalog, dataId)
in enumerate(zip(srcList, dataIds)):
388 if self.isValid(srcCatalog, dataId):
393 def isValid(self, srcCatalog, dataId=None):
394 """Should this ccd be selected based on its PSF shape information
398 srcCatalog : `lsst.afw.table.SourceCatalog`
399 dataId : `dict` of dataId keys, optional.
400 Used only for logging. Defaults to None.
407 mask = srcCatalog[self.config.starSelection]
409 starXX = srcCatalog[self.config.starShape+
'_xx'][mask]
410 starYY = srcCatalog[self.config.starShape+
'_yy'][mask]
411 starXY = srcCatalog[self.config.starShape+
'_xy'][mask]
412 psfXX = srcCatalog[self.config.psfShape+
'_xx'][mask]
413 psfYY = srcCatalog[self.config.psfShape+
'_yy'][mask]
414 psfXY = srcCatalog[self.config.psfShape+
'_xy'][mask]
416 starSize = np.power(starXX*starYY - starXY**2, 0.25)
417 starE1 = (starXX - starYY)/(starXX + starYY)
418 starE2 = 2*starXY/(starXX + starYY)
419 medianSize = np.median(starSize)
421 psfSize = np.power(psfXX*psfYY - psfXY**2, 0.25)
422 psfE1 = (psfXX - psfYY)/(psfXX + psfYY)
423 psfE2 = 2*psfXY/(psfXX + psfYY)
425 medianE1 = np.abs(np.median(starE1 - psfE1))
426 medianE2 = np.abs(np.median(starE2 - psfE2))
427 medianE = np.sqrt(medianE1**2 + medianE2**2)
429 scatterSize =
sigmaMad(starSize - psfSize)
430 scaledScatterSize = scatterSize/medianSize**2
433 if self.config.maxEllipResidual
and medianE > self.config.maxEllipResidual:
434 self.log.
info(
"Removing visit %s because median e residual too large: %f vs %f",
435 dataId, medianE, self.config.maxEllipResidual)
437 elif self.config.maxSizeScatter
and scatterSize > self.config.maxSizeScatter:
438 self.log.
info(
"Removing visit %s because size scatter is too large: %f vs %f",
439 dataId, scatterSize, self.config.maxSizeScatter)
441 elif self.config.maxScaledSizeScatter
and scaledScatterSize > self.config.maxScaledSizeScatter:
442 self.log.
info(
"Removing visit %s because scaled size scatter is too large: %f vs %f",
443 dataId, scaledScatterSize, self.config.maxScaledSizeScatter)
449 class BestSeeingWcsSelectImageConfig(WcsSelectImagesTask.ConfigClass):
450 """Base configuration for BestSeeingSelectImagesTask.
452 nImagesMax = pexConfig.RangeField(
454 doc=
"Maximum number of images to select",
457 maxPsfFwhm = pexConfig.Field(
459 doc=
"Maximum PSF FWHM (in arcseconds) to select",
462 minPsfFwhm = pexConfig.Field(
464 doc=
"Minimum PSF FWHM (in arcseconds) to select",
470 """Select up to a maximum number of the best-seeing images using their Wcs.
472 ConfigClass = BestSeeingWcsSelectImageConfig
474 def runDataRef(self, dataRef, coordList, makeDataRefList=True,
475 selectDataList=None):
476 """Select the best-seeing images in the selectDataList that overlap the patch.
478 This method is the old entry point for the Gen2 commandline tasks and drivers
479 Will be deprecated in v22.
483 dataRef : `lsst.daf.persistence.ButlerDataRef`
484 Data reference for coadd/tempExp (with tract, patch)
485 coordList : `list` of `lsst.geom.SpherePoint`
486 List of ICRS sky coordinates specifying boundary of patch
487 makeDataRefList : `boolean`, optional
488 Construct a list of data references?
489 selectDataList : `list` of `SelectStruct`
490 List of SelectStruct, to consider for selection
494 result : `lsst.pipe.base.Struct`
495 Result struct with components:
496 - ``exposureList``: the selected exposures
497 (`list` of `lsst.pipe.tasks.selectImages.BaseExposureInfo`).
498 - ``dataRefList``: the optional data references corresponding to
499 each element of ``exposureList``
500 (`list` of `lsst.daf.persistence.ButlerDataRef`, or `None`).
504 exposureInfoList = []
506 if selectDataList
is None:
509 result = super().runDataRef(dataRef, coordList, makeDataRefList=
True, selectDataList=selectDataList)
511 for dataRef, exposureInfo
in zip(result.dataRefList, result.exposureInfoList):
512 cal = dataRef.get(
"calexp", immediate=
True)
515 pixToArcseconds = cal.getWcs().getPixelScale().asArcseconds()
516 psfSize = cal.getPsf().computeShape().getDeterminantRadius()*pixToArcseconds
517 sizeFwhm = psfSize * np.sqrt(8.*np.log(2.))
518 if self.config.maxPsfFwhm
and sizeFwhm > self.config.maxPsfFwhm:
520 if self.config.minPsfFwhm
and sizeFwhm < self.config.minPsfFwhm:
522 psfSizes.append(sizeFwhm)
523 dataRefList.append(dataRef)
524 exposureInfoList.append(exposureInfo)
526 if len(psfSizes) > self.config.nImagesMax:
527 sortedIndices = np.argsort(psfSizes)[:self.config.nImagesMax]
528 filteredDataRefList = [dataRefList[i]
for i
in sortedIndices]
529 filteredExposureInfoList = [exposureInfoList[i]
for i
in sortedIndices]
530 self.log.
info(
"%d images selected with FWHM range of %f--%f arcseconds",
531 len(sortedIndices), psfSizes[sortedIndices[0]], psfSizes[sortedIndices[-1]])
534 if len(psfSizes) == 0:
535 self.log.
warning(
"0 images selected.")
537 self.log.
debug(
"%d images selected with FWHM range of %d--%d arcseconds",
538 len(psfSizes), psfSizes[0], psfSizes[-1])
539 filteredDataRefList = dataRefList
540 filteredExposureInfoList = exposureInfoList
542 return pipeBase.Struct(
543 dataRefList=filteredDataRefList
if makeDataRefList
else None,
544 exposureInfoList=filteredExposureInfoList,
548 class BestSeeingSelectVisitsConnections(pipeBase.PipelineTaskConnections,
549 dimensions=(
"tract",
"patch",
"skymap",
"band",
"instrument"),
550 defaultTemplates={
"coaddName":
"goodSeeing"}):
551 skyMap = pipeBase.connectionTypes.Input(
552 doc=
"Input definition of geometry/bbox and projection/wcs for coadded exposures",
553 name=BaseSkyMap.SKYMAP_DATASET_TYPE_NAME,
554 storageClass=
"SkyMap",
555 dimensions=(
"skymap",),
557 visitSummaries = pipeBase.connectionTypes.Input(
558 doc=
"Per-visit consolidated exposure metadata from ConsolidateVisitSummaryTask",
560 storageClass=
"ExposureCatalog",
561 dimensions=(
"instrument",
"visit",),
565 goodVisits = pipeBase.connectionTypes.Output(
566 doc=
"Selected visits to be coadded.",
567 name=
"{coaddName}Visits",
568 storageClass=
"StructuredDataDict",
569 dimensions=(
"instrument",
"tract",
"patch",
"skymap",
"band"),
573 class BestSeeingSelectVisitsConfig(pipeBase.PipelineTaskConfig,
574 pipelineConnections=BestSeeingSelectVisitsConnections):
575 nVisitsMax = pexConfig.RangeField(
577 doc=
"Maximum number of visits to select",
581 maxPsfFwhm = pexConfig.Field(
583 doc=
"Maximum PSF FWHM (in arcseconds) to select",
587 minPsfFwhm = pexConfig.Field(
589 doc=
"Minimum PSF FWHM (in arcseconds) to select",
593 doConfirmOverlap = pexConfig.Field(
595 doc=
"Do remove visits that do not actually overlap the patch?",
598 minMJD = pexConfig.Field(
600 doc=
"Minimum visit MJD to select",
604 maxMJD = pexConfig.Field(
606 doc=
"Maximum visit MJD to select",
612 class BestSeeingSelectVisitsTask(pipeBase.PipelineTask):
613 """Select up to a maximum number of the best-seeing visits
615 Don't exceed the FWHM range specified by configs min(max)PsfFwhm.
616 This Task is a port of the Gen2 image-selector used in the AP pipeline:
617 BestSeeingSelectImagesTask. This Task selects full visits based on the
618 average PSF of the entire visit.
620 ConfigClass = BestSeeingSelectVisitsConfig
621 _DefaultName =
'bestSeeingSelectVisits'
623 def runQuantum(self, butlerQC, inputRefs, outputRefs):
624 inputs = butlerQC.get(inputRefs)
625 quantumDataId = butlerQC.quantum.dataId
626 outputs = self.run(**inputs, dataId=quantumDataId)
627 butlerQC.put(outputs, outputRefs)
629 def run(self, visitSummaries, skyMap, dataId):
634 visitSummary : `list`
635 List of `lsst.pipe.base.connections.DeferredDatasetRef` of
636 visitSummary tables of type `lsst.afw.table.ExposureCatalog`
637 skyMap : `lsst.skyMap.SkyMap`
638 SkyMap for checking visits overlap patch
639 dataId : `dict` of dataId keys
640 For retrieving patch info for checking visits overlap patch
644 result : `lsst.pipe.base.Struct`
645 Result struct with components:
647 - `goodVisits`: `dict` with selected visit ids as keys,
648 so that it can be be saved as a StructuredDataDict.
649 StructuredDataList's are currently limited.
652 if self.config.doConfirmOverlap:
653 patchPolygon = self.makePatchPolygon(skyMap, dataId)
655 inputVisits = [visitSummary.ref.dataId[
'visit']
for visitSummary
in visitSummaries]
658 for visit, visitSummary
in zip(inputVisits, visitSummaries):
660 visitSummary = visitSummary.get()
663 mjd = visitSummary[0].getVisitInfo().getDate().get(system=DateTime.MJD)
665 pixToArcseconds = [vs.getWcs().getPixelScale(vs.getBBox().getCenter()).asArcseconds()
666 for vs
in visitSummary]
668 psfSigmas = np.array([vs[
'psfSigma']
for vs
in visitSummary])
669 fwhm = np.nanmean(psfSigmas * pixToArcseconds) * np.sqrt(8.*np.log(2.))
671 if self.config.maxPsfFwhm
and fwhm > self.config.maxPsfFwhm:
673 if self.config.minPsfFwhm
and fwhm < self.config.minPsfFwhm:
675 if self.config.minMJD
and mjd < self.config.minMJD:
676 self.log.
debug(
'MJD %f earlier than %.2f; rejecting', mjd, self.config.minMJD)
678 if self.config.maxMJD
and mjd > self.config.maxMJD:
679 self.log.
debug(
'MJD %f later than %.2f; rejecting', mjd, self.config.maxMJD)
681 if self.config.doConfirmOverlap
and not self.doesIntersectPolygon(visitSummary, patchPolygon):
684 fwhmSizes.append(fwhm)
687 sortedVisits = [ind
for (_, ind)
in sorted(zip(fwhmSizes, visits))]
688 output = sortedVisits[:self.config.nVisitsMax]
689 self.log.
info(
"%d images selected with FWHM range of %d--%d arcseconds",
690 len(output), fwhmSizes[visits.index(output[0])], fwhmSizes[visits.index(output[-1])])
693 goodVisits = {key:
True for key
in output}
694 return pipeBase.Struct(goodVisits=goodVisits)
696 def makePatchPolygon(self, skyMap, dataId):
697 """Return True if sky polygon overlaps visit
701 skyMap : `lsst.afw.table.ExposureCatalog`
702 Exposure catalog with per-detector geometry
703 dataId : `dict` of dataId keys
704 For retrieving patch info
708 result :` lsst.sphgeom.ConvexPolygon.convexHull`
709 Polygon of patch's outer bbox
711 wcs = skyMap[dataId[
'tract']].getWcs()
712 bbox = skyMap[dataId[
'tract']][dataId[
'patch']].getOuterBBox()
717 def doesIntersectPolygon(self, visitSummary, polygon):
718 """Return True if sky polygon overlaps visit
722 visitSummary : `lsst.afw.table.ExposureCatalog`
723 Exposure catalog with per-detector geometry
724 polygon :` lsst.sphgeom.ConvexPolygon.convexHull`
725 Polygon to check overlap
729 doesIntersect: `bool`
730 Does the visit overlap the polygon
732 doesIntersect =
False
733 for detectorSummary
in visitSummary:
735 zip(detectorSummary[
'raCorners'], detectorSummary[
'decCorners'])]
737 if detectorPolygon.intersects(polygon):
743 class BestSeeingQuantileSelectVisitsConfig(pipeBase.PipelineTaskConfig,
744 pipelineConnections=BestSeeingSelectVisitsConnections):
745 qMin = pexConfig.RangeField(
746 doc=
"Lower bound of quantile range to select. Sorts visits by seeing from narrow to wide, "
747 "and select those in the interquantile range (qMin, qMax). Set qMin to 0 for Best Seeing. "
748 "This config should be changed from zero only for exploratory diffIm testing.",
754 qMax = pexConfig.RangeField(
755 doc=
"Upper bound of quantile range to select. Sorts visits by seeing from narrow to wide, "
756 "and select those in the interquantile range (qMin, qMax). Set qMax to 1 for Worst Seeing.",
762 nVisitsMin = pexConfig.Field(
763 doc=
"At least this number of visits selected and supercedes quantile. For example, if 10 visits "
764 "cover this patch, qMin=0.33, and nVisitsMin=5, the best 5 visits will be selected.",
768 doConfirmOverlap = pexConfig.Field(
770 doc=
"Do remove visits that do not actually overlap the patch?",
773 minMJD = pexConfig.Field(
775 doc=
"Minimum visit MJD to select",
779 maxMJD = pexConfig.Field(
781 doc=
"Maximum visit MJD to select",
787 class BestSeeingQuantileSelectVisitsTask(BestSeeingSelectVisitsTask):
788 """Select a quantile of the best-seeing visits
790 Selects the best (for example, third) full visits based on the average
791 PSF width in the entire visit. It can also be used for difference imaging
792 experiments that require templates with the worst seeing visits.
793 For example, selecting the worst third can be acheived by
794 changing the config parameters qMin to 0.66 and qMax to 1.
796 ConfigClass = BestSeeingQuantileSelectVisitsConfig
797 _DefaultName =
'bestSeeingQuantileSelectVisits'
799 @utils.inheritDoc(BestSeeingSelectVisitsTask)
800 def run(self, visitSummaries, skyMap, dataId):
801 if self.config.doConfirmOverlap:
802 patchPolygon = self.makePatchPolygon(skyMap, dataId)
803 visits = np.array([visitSummary.ref.dataId[
'visit']
for visitSummary
in visitSummaries])
804 radius = np.empty(len(visits))
805 intersects = np.full(len(visits),
True)
806 for i, visitSummary
in enumerate(visitSummaries):
808 visitSummary = visitSummary.get()
810 psfSigma = np.nanmedian([vs[
'psfSigma']
for vs
in visitSummary])
812 if self.config.doConfirmOverlap:
813 intersects[i] = self.doesIntersectPolygon(visitSummary, patchPolygon)
814 if self.config.minMJD
or self.config.maxMJD:
816 mjd = visitSummary[0].getVisitInfo().getDate().get(system=DateTime.MJD)
817 aboveMin = mjd > self.config.minMJD
if self.config.minMJD
else True
818 belowMax = mjd < self.config.maxMJD
if self.config.maxMJD
else True
819 intersects[i] = intersects[i]
and aboveMin
and belowMax
821 sortedVisits = [v
for rad, v
in sorted(zip(radius[intersects], visits[intersects]))]
822 lowerBound =
min(int(np.round(self.config.qMin*len(visits[intersects]))),
823 max(0, len(visits[intersects]) - self.config.nVisitsMin))
824 upperBound =
max(int(np.round(self.config.qMax*len(visits[intersects]))), self.config.nVisitsMin)
827 goodVisits = {int(visit):
True for visit
in sortedVisits[lowerBound:upperBound]}
828 return pipeBase.Struct(goodVisits=goodVisits)
A floating-point coordinate rectangle geometry.
Point in an unspecified spherical coordinate system.
Reports arguments outside the domain of an operation.
Reports errors that are due to events beyond the control of the program.
def __init__(self, dataId, coordList)
def _runArgDictFromDataId(self, dataId)
def runDataRef(self, dataRef, coordList, makeDataRefList=True, selectDataList=[])
def __init__(self, dataRef, wcs, bbox)
def getValidImageCorners(self, imageWcs, imageBox, patchPoly, dataId=None)
def runDataRef(self, dataRef, coordList, makeDataRefList=True, selectDataList=[])
def run(self, wcsList, bboxList, coordList, dataIds=None, **kwargs)
static ConvexPolygon convexHull(std::vector< UnitVector3d > const &points)
convexHull returns the convex hull of the given set of points if it exists and throws an exception ot...
daf::base::PropertyList * list
daf::base::PropertySet * set
def run(self, coaddExposures, bbox, wcs)