8
8
from lsst .pipe .base import ArgumentParser , TaskRunner
9
9
from lsst .pipe .tasks .multiBand import (DetectCoaddSourcesTask ,
10
10
MergeDetectionsTask ,
11
+ DeblendCoaddSourcesTask ,
11
12
MeasureMergedCoaddSourcesTask ,
12
13
MergeMeasurementsTask ,)
13
14
from lsst .ctrl .pool .parallel import BatchPoolTask
@@ -76,8 +77,9 @@ class MultiBandDriverConfig(Config):
76
77
doc = "Detect sources on coadd" )
77
78
mergeCoaddDetections = ConfigurableField (
78
79
target = MergeDetectionsTask , doc = "Merge detections" )
80
+ deblendCoaddSources = ConfigurableField (target = DeblendCoaddSourcesTask , doc = "Deblend merged detections" )
79
81
measureCoaddSources = ConfigurableField (target = MeasureMergedCoaddSourcesTask ,
80
- doc = "Measure merged detections" )
82
+ doc = "Measure merged and (optionally) deblended detections" )
81
83
mergeCoaddMeasurements = ConfigurableField (
82
84
target = MergeMeasurementsTask , doc = "Merge measurements" )
83
85
forcedPhotCoadd = ConfigurableField (target = ForcedPhotCoaddTask ,
@@ -96,7 +98,7 @@ def setDefaults(self):
96
98
self .forcedPhotCoadd .references .retarget (MultiBandReferencesTask )
97
99
98
100
def validate (self ):
99
- for subtask in ("mergeCoaddDetections" , "measureCoaddSources" ,
101
+ for subtask in ("mergeCoaddDetections" , "deblendCoaddSources" , " measureCoaddSources" ,
100
102
"mergeCoaddMeasurements" , "forcedPhotCoadd" ):
101
103
coaddName = getattr (self , subtask ).coaddName
102
104
if coaddName != self .coaddName :
@@ -160,7 +162,29 @@ def __init__(self, butler=None, schema=None, refObjLoader=None, reuse=tuple(), *
160
162
self .reuse = tuple (reuse )
161
163
self .makeSubtask ("detectCoaddSources" )
162
164
self .makeSubtask ("mergeCoaddDetections" , schema = schema )
163
- self .makeSubtask ("measureCoaddSources" , schema = afwTable .Schema (self .mergeCoaddDetections .schema ),
165
+ if self .config .measureCoaddSources .inputCatalog .startswith ("deblended" ):
166
+ # Ensure that the output from deblendCoaddSources matches the input to measureCoaddSources
167
+ self .measurementInput = self .config .measureCoaddSources .inputCatalog
168
+ self .deblenderOutput = []
169
+ if self .config .deblendCoaddSources .simultaneous :
170
+ if self .config .deblendCoaddSources .multiBandDeblend .conserveFlux :
171
+ self .deblenderOutput .append ("deblendedFlux" )
172
+ if self .config .deblendCoaddSources .multiBandDeblend .saveTemplates :
173
+ self .deblenderOutput .append ("deblendedModel" )
174
+ else :
175
+ self .deblenderOutput .append ("deblendedFlux" )
176
+ if self .measurementInput not in self .deblenderOutput :
177
+ err = "Measurement input '{0}' is not in the list of deblender output catalogs '{1}'"
178
+ raise ValueError (err .format (self .measurementInput , self .deblenderOutput ))
179
+
180
+ self .makeSubtask ("deblendCoaddSources" ,
181
+ schema = afwTable .Schema (self .mergeCoaddDetections .schema ),
182
+ peakSchema = afwTable .Schema (self .mergeCoaddDetections .merged .getPeakSchema ()),
183
+ butler = butler )
184
+ measureInputSchema = afwTable .Schema (self .deblendCoaddSources .schema )
185
+ else :
186
+ measureInputSchema = afwTable .Schema (self .mergeCoaddDetections .schema )
187
+ self .makeSubtask ("measureCoaddSources" , schema = measureInputSchema ,
164
188
peakSchema = afwTable .Schema (
165
189
self .mergeCoaddDetections .merged .getPeakSchema ()),
166
190
refObjLoader = refObjLoader , butler = butler )
@@ -270,30 +294,30 @@ def runDataRef(self, patchRefList):
270
294
271
295
pool .map (self .runMergeDetections , patches .values ())
272
296
273
- # Measure merged detections, and test for reprocessing
297
+ # Deblend merged detections, and test for reprocessing
274
298
#
275
299
# The reprocessing allows us to have multiple attempts at deblending large footprints. Large
276
300
# footprints can suck up a lot of memory in the deblender, which means that when we process on a
277
301
# cluster, we want to refuse to deblend them (they're flagged "deblend.parent-too-big"). But since
278
302
# they may have astronomically interesting data, we want the ability to go back and reprocess them
279
303
# with a more permissive configuration when we have more memory or processing time.
280
304
#
281
- # self.runMeasureMerged will return whether there are any footprints in that image that required
305
+ # self.runDeblendMerged will return whether there are any footprints in that image that required
282
306
# reprocessing. We need to convert that list of booleans into a dict mapping the patchId (x,y) to
283
307
# a boolean. That tells us whether the merge measurement and forced photometry need to be re-run on
284
308
# a particular patch.
285
309
#
286
310
# This determination of which patches need to be reprocessed exists only in memory (the measurements
287
311
# have been written, clobbering the old ones), so if there was an exception we would lose this
288
- # information, leaving things in an inconsistent state (measurements new, but merged measurements and
312
+ # information, leaving things in an inconsistent state (measurements, merged measurements and
289
313
# forced photometry old). To attempt to preserve this status, we touch a file (dataset named
290
- # "deepCoadd_multibandReprocessing") --- if this file exists, we need to re-run the merge and
291
- # forced photometry.
314
+ # "deepCoadd_multibandReprocessing") --- if this file exists, we need to re-run the measurements,
315
+ # merge and forced photometry.
292
316
#
293
317
# This is, hopefully, a temporary workaround until we can improve the
294
318
# deblender.
295
319
try :
296
- reprocessed = pool .map (self .runMeasureMerged , dataIdList )
320
+ reprocessed = pool .map (self .runDeblendMerged , patches . values () )
297
321
finally :
298
322
if self .config .reprocessing :
299
323
patchReprocessing = {}
@@ -317,10 +341,12 @@ def runDataRef(self, patchRefList):
317
341
patchReprocessing [patchId ] = True
318
342
319
343
# Only process patches that have been identified as needing it
344
+ pool .map (self .runMeasurements , [dataId1 for dataId1 in dataIdList if not self .config .reprocessing or
345
+ patchReprocessing [dataId1 ["patch" ]]])
320
346
pool .map (self .runMergeMeasurements , [idList for patchId , idList in patches .items () if
321
347
not self .config .reprocessing or patchReprocessing [patchId ]])
322
348
pool .map (self .runForcedPhot , [dataId1 for dataId1 in dataIdList if not self .config .reprocessing or
323
- patchReprocessing [dataId ["patch" ]]])
349
+ patchReprocessing [dataId1 ["patch" ]]])
324
350
325
351
# Remove persisted reprocessing determination
326
352
if self .config .reprocessing :
@@ -368,45 +394,75 @@ def runMergeDetections(self, cache, dataIdList):
368
394
return
369
395
self .mergeCoaddDetections .runDataRef (dataRefList )
370
396
371
- def runMeasureMerged (self , cache , dataId ):
372
- """! Run measurement on a patch for a single filter
397
+ def runDeblendMerged (self , cache , dataIdList ):
398
+ """Run the deblender on a list of dataId's
373
399
374
400
Only slave nodes execute this method.
375
401
376
- @param cache: Pool cache, with butler
377
- @param dataId: Data identifier for patch
378
- @return whether the patch requires reprocessing.
402
+ Parameters
403
+ ----------
404
+ cache: Pool cache
405
+ Pool cache with butler.
406
+ dataIdList: list
407
+ Data identifier for patch in each band.
408
+
409
+ Returns
410
+ -------
411
+ result: bool
412
+ whether the patch requires reprocessing.
379
413
"""
380
- with self .logOperation ("measurement on %s" % (dataId ,)):
381
- dataRef = getDataRef (cache .butler , dataId ,
382
- self . config . coaddName + "Coadd_calexp" )
414
+ with self .logOperation ("deblending %s" % (dataIdList ,)):
415
+ dataRefList = [ getDataRef (cache .butler , dataId , self . config . coaddName + "Coadd_calexp" ) for
416
+ dataId in dataIdList ]
383
417
reprocessing = False # Does this patch require reprocessing?
384
- if ("measureCoaddSources " in self .reuse and
385
- dataRef .datasetExists (self .config .coaddName + "Coadd_meas" , write = True )):
418
+ if ("deblendCoaddSources " in self .reuse and
419
+ dataRef .datasetExists (self .config .coaddName + self . measurementInput , write = True )):
386
420
if not self .config .reprocessing :
387
- self .log .info ("Skipping measureCoaddSources for %s; output already exists" % dataId )
421
+ self .log .info ("Skipping deblendCoaddSources for %s; output already exists" % dataIdList )
388
422
return False
389
423
390
- catalog = dataRef .get (self .config .coaddName + "Coadd_meas" )
424
+ catalog = dataRefList [ 0 ] .get (self .config .coaddName + self . measurementInput )
391
425
bigFlag = catalog ["deblend.parent-too-big" ]
392
426
numOldBig = bigFlag .sum ()
393
427
if numOldBig == 0 :
394
428
self .log .info ("No large footprints in %s" %
395
429
(dataRef .dataId ,))
396
430
return False
397
- numNewBig = sum ((self .measureCoaddSources . deblend .isLargeFootprint (src .getFootprint ()) for
431
+ numNewBig = sum ((self .deblendCoaddSources .isLargeFootprint (src .getFootprint ()) for
398
432
src in catalog [bigFlag ]))
399
433
if numNewBig == numOldBig :
400
434
self .log .info ("All %d formerly large footprints continue to be large in %s" %
401
- (numOldBig , dataRef .dataId ,))
435
+ (numOldBig , dataRefList [ 0 ] .dataId ,))
402
436
return False
403
437
self .log .info ("Found %d large footprints to be reprocessed in %s" %
404
- (numOldBig - numNewBig , dataRef .dataId ))
438
+ (numOldBig - numNewBig , [ dataRef .dataId for dataRef in dataRefList ] ))
405
439
reprocessing = True
406
440
407
- self .measureCoaddSources .runDataRef (dataRef )
441
+ self .deblendCoaddSources .runDataRef (dataRefList )
408
442
return reprocessing
409
443
444
+ def runMeasurements (self , cache , dataId ):
445
+ """Run measurement on a patch for a single filter
446
+
447
+ Only slave nodes execute this method.
448
+
449
+ Parameters
450
+ ----------
451
+ cache: Pool cache
452
+ Pool cache, with butler
453
+ dataId: dataRef
454
+ Data identifier for patch
455
+ """
456
+ with self .logOperation ("measurements on %s" % (dataId ,)):
457
+ dataRef = getDataRef (cache .butler , dataId ,
458
+ self .config .coaddName + "Coadd_calexp" )
459
+ if ("measureCoaddSources" in self .reuse and
460
+ not self .config .reprocessing and
461
+ dataRef .datasetExists (self .config .coaddName + "Coadd_meas" , write = True )):
462
+ self .log .info ("Skipping measuretCoaddSources for %s; output already exists" % dataId )
463
+ return
464
+ self .measureCoaddSources .runDataRef (dataRef )
465
+
410
466
def runMergeMeasurements (self , cache , dataIdList ):
411
467
"""!Run measurement merging on a patch
412
468
0 commit comments