30 from lsst.meas.base import BasePlugin, SingleFrameMeasurementTask, MeasureApCorrTask
32 from .photoCal
import PhotoCalTask
33 from .repair
import RepairTask
34 from .measurePsf
import MeasurePsfTask
37 """!Describes the initial PSF used for detection and measurement before we do PSF determination."""
39 model = pexConfig.ChoiceField(
41 doc =
"PSF model type",
42 default =
"SingleGaussian",
44 "SingleGaussian":
"Single Gaussian model",
45 "DoubleGaussian":
"Double Gaussian model",
48 pixelScale = pexConfig.Field(
50 doc =
"Pixel size (arcsec). Only needed if no Wcs is provided",
53 fwhm = pexConfig.Field(
55 doc =
"FWHM of PSF model (arcsec)",
58 size = pexConfig.Field(
60 doc =
"Size of PSF model (pixels)",
65 initialPsf = pexConfig.ConfigField(dtype=InitialPsfConfig, doc=InitialPsfConfig.__doc__)
66 doBackground = pexConfig.Field(
68 doc =
"Subtract background (after computing it, if not supplied)?",
71 doPsf = pexConfig.Field(
73 doc =
"Perform PSF fitting?",
76 doMeasureApCorr = pexConfig.Field(
78 doc =
"Compute aperture corrections?",
81 doAstrometry = pexConfig.Field(
83 doc =
"Compute astrometric solution?",
86 doPhotoCal = pexConfig.Field(
88 doc =
"Compute photometric zeropoint?",
91 background = pexConfig.ConfigField(
92 dtype = measAlg.estimateBackground.ConfigClass,
93 doc =
"Background estimation configuration"
95 repair = pexConfig.ConfigurableField(
97 doc =
"Interpolate over defects and cosmic rays",
99 detection = pexConfig.ConfigurableField(
100 target = measAlg.SourceDetectionTask,
101 doc =
"Initial (high-threshold) detection phase for calibration",
103 initialMeasurement = pexConfig.ConfigurableField(
104 target = SingleFrameMeasurementTask,
105 doc =
"Initial measurements used to feed PSF determination and aperture correction determination",
107 measurePsf = pexConfig.ConfigurableField(
108 target = MeasurePsfTask,
111 measurement = pexConfig.ConfigurableField(
112 target = SingleFrameMeasurementTask,
113 doc =
"Post-PSF-determination measurements used to feed other calibrations",
115 measureApCorr = pexConfig.ConfigurableField(
116 target = MeasureApCorrTask,
117 doc =
"subtask to measure aperture corrections"
119 astrometry = pexConfig.ConfigurableField(
120 target = AstrometryTask,
121 doc =
"fit WCS of exposure",
123 photocal = pexConfig.ConfigurableField(
124 target = PhotoCalTask,
125 doc =
"peform photometric calibration",
129 pexConfig.Config.validate(self)
131 raise ValueError(
"Cannot do photometric calibration without doing astrometric matching")
132 if self.
doMeasureApCorr and self.measureApCorr.inputFilterFlag ==
"calib_psfUsed" and not self.
doPsf:
133 raise ValueError(
"Cannot measure aperture correction with inputFilterFlag=calib_psfUsed"
134 " unless doPsf is also True")
135 if self.measurement.doApplyApCorr.startswith(
"yes")
and not self.
doMeasureApCorr:
136 raise ValueError(
"Cannot set measurement.doApplyApCorr to 'yes...'"
137 " unless doMeasureApCorr is True")
140 self.detection.includeThresholdMultiplier = 10.0
141 self.initialMeasurement.algorithms.names -= [
"base_ClassificationExtendedness"]
142 self.initialMeasurement.doApplyApCorr =
"no"
143 self.measurement.doApplyApCorr =
"yes"
144 initflags = [x
for x
in self.measurePsf.starSelector[
"catalog"].badStarPixelFlags]
145 self.measurePsf.starSelector[
"catalog"].badStarPixelFlags.extend(initflags)
146 self.background.binSize = 1024
157 \anchor CalibrateTask_
159 \brief Calibrate an exposure: measure PSF, subtract background, measure astrometry and photometry
161 \section pipe_tasks_calibrate_Contents Contents
163 - \ref pipe_tasks_calibrate_Purpose
164 - \ref pipe_tasks_calibrate_Initialize
165 - \ref pipe_tasks_calibrate_IO
166 - \ref pipe_tasks_calibrate_Config
167 - \ref pipe_tasks_calibrate_Metadata
168 - \ref pipe_tasks_calibrate_Debug
169 - \ref pipe_tasks_calibrate_Example
171 \section pipe_tasks_calibrate_Purpose Description
173 \copybrief CalibrateTask
175 Calculate an Exposure's zero-point given a set of flux measurements of stars matched to an input catalogue.
176 The type of flux to use is specified by CalibrateConfig.fluxField.
178 The algorithm clips outliers iteratively, with parameters set in the configuration.
180 \note This task can adds fields to the schema, so any code calling this task must ensure that
181 these columns are indeed present in the input match list; see \ref pipe_tasks_calibrate_Example
183 \section pipe_tasks_calibrate_Initialize Task initialisation
185 \copydoc \_\_init\_\_
187 CalibrateTask delegates most of its work to a set of sub-Tasks:
189 <DT> repair \ref RepairTask_ "RepairTask"
190 <DD> Interpolate over defects such as bad columns and cosmic rays. This task is called twice; once
191 before the %measurePsf step and again after the PSF has been measured.
192 <DT> detection \ref SourceDetectionTask_ "SourceDetectionTask"
193 <DD> Initial (high-threshold) detection phase for calibration
194 <DT> initialMeasurement \ref SingleFrameMeasurementTask_ "SingleFrameMeasurementTask"
195 <DD> Make the initial measurements used to feed PSF determination and aperture correction determination
196 <DT> astrometry \ref AstrometryTask_ "AstrometryTask"
197 <DD> Solve the astrometry. May be disabled by setting CalibrateTaskConfig.doAstrometry to be False.
198 This task is called twice; once before the %measurePsf step and again after the PSF has been measured.
199 <DT> %measurePsf \ref MeasurePsfTask_ "MeasurePsfTask"
200 <DD> Estimate the PSF. May be disabled by setting CalibrateTaskConfig.doPsf to be False. If requested
201 the astrometry is solved before this is called, so if you disable the astrometry the %measurePsf
202 task won't have access to objects positions.
203 <DT> measurement \ref SingleFrameMeasurementTask_ "SingleFrameMeasurementTask"
204 <DD> Post-PSF-determination measurements used to feed other calibrations
205 <DT> photocal \ref PhotoCalTask_ "PhotoCalTask"
206 <DD> Solve for the photometric zeropoint.
207 May be disabled by setting CalibrateTaskConfig.doPhotoCal to be False.
208 \em N.b. Requires that \c astrometry was successfully run.
211 You can replace any of these subtasks if you wish, see \ref calibrate_MyAstrometryTask.
212 \note These task APIs are not well controlled, so replacing a task is a matter of matching
213 a poorly specified interface. We will be working on this over the first year of construction.
215 \section pipe_tasks_calibrate_IO Invoking the Task
219 \section pipe_tasks_calibrate_Config Configuration parameters
221 See \ref CalibrateConfig
223 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
225 \section pipe_tasks_calibrate_Metadata Quantities set in Metadata
231 <DT> MAGZERO <DD> Measured zeropoint (DN per second)
234 <DT> Exposure metadata
237 <DT> MAGZERO_RMS <DD> MAGZERO's RMS == return.sigma
238 <DT> MAGZERO_NOBJ <DD> Number of stars used == return.ngood
239 <DT> COLORTERM1 <DD> ?? (always 0.0)
240 <DT> COLORTERM2 <DD> ?? (always 0.0)
241 <DT> COLORTERM3 <DD> ?? (always 0.0)
245 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
247 \section pipe_tasks_calibrate_Debug Debug variables
249 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a
250 flag \c -d to import \b debug.py from your \c PYTHONPATH; see \ref baseDebug for more about \b debug.py files.
252 The calibrate task has a debug dictionary with keys which correspond to stages of the CalibrationTask
256 <DD> Fixed defects and masked cosmic rays with a guessed PSF. Action: show the exposure.
258 <DD> Subtracted background (no sources masked). Action: show the exposure
260 <DD> Fixed defects and removed cosmic rays with an estimated PSF. Action: show the exposure
262 <DD> Subtracted background (calibration sources masked). Action: show the exposure
264 <DD> Just before astro/photo calibration. Action: show the exposure, and
265 - sources as smallish green o
266 - matches (if exposure has a Wcs).
267 - catalog position as a largish yellow +
268 - source position as a largish red x
270 The values are the \c ds9 frame to display in (if >= 1); if <= 0, nothing's displayed.
271 There's an example \ref pipe_tasks_calibrate_Debug_example "here".
273 Some subtasks may also have their own debug information; see individual Task documentation.
275 \section pipe_tasks_calibrate_Example A complete example of using CalibrateTask
277 This code is in \link calibrateTask.py\endlink in the examples directory, and can be run as \em e.g.
279 examples/calibrateTask.py --ds9
281 \dontinclude calibrateTask.py
283 Import the task (there are some other standard imports; read the file if you're curious)
284 \skipline CalibrateTask
286 Create the detection task
287 \skip CalibrateTask.ConfigClass
289 Note that we're using a custom AstrometryTask (because we don't have a valid astrometric catalogue handy);
290 see \ref calibrate_MyAstrometryTask.
292 We're now ready to process the data (we could loop over multiple exposures/catalogues using the same
293 task objects) and unpack the results
297 We then might plot the results (\em e.g. if you set \c --ds9 on the command line)
301 \subsection calibrate_MyAstrometryTask Using a Custom Astrometry Task
303 The first thing to do is define my own task:
304 \dontinclude calibrateTask.py
305 \skip MyAstrometryTask
306 \skip MyAstrometryTask
309 Then we need our own \c run method. First unpack the filtername and wcs
312 Then build a "reference catalog" by shamelessly copying the catalog of detected sources
314 \until get("photometric")
315 (you need to set "flux" as well as \c filterName due to a bug in the photometric calibration code;
316 <A HREF=https://jira.lsstcorp.org/browse/DM-933>DM-933</A>).
318 Then "match" by zipping up the two catalogs,
321 and finally return the desired results.
326 \anchor pipe_tasks_calibrate_Debug_example
327 To investigate the \ref pipe_tasks_calibrate_Debug, put something like
331 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively
332 if name == "lsst.pipe.tasks.calibrate":
340 lsstDebug.Info = DebugInfo
342 into your debug.py file and run calibrateTask.py with the \c --debug flag.
344 ConfigClass = CalibrateConfig
345 _DefaultName =
"calibrate"
349 Create the calibration task
351 \param **kwargs keyword arguments to be passed to lsst.pipe.base.task.Task.__init__
353 pipeBase.Task.__init__(self, **kwargs)
359 self.
schema1 = afwTable.SourceTable.makeMinimalSchema()
361 self.makeSubtask(
"repair")
362 self.makeSubtask(
"detection", schema=self.
schema1)
363 beginInitial = self.schema1.getFieldCount()
364 self.makeSubtask(
"initialMeasurement", schema=self.
schema1, algMetadata=self.
algMetadata)
365 endInitial = self.schema1.getFieldCount()
368 self.makeSubtask(
"measurePsf", schema=self.
schema1)
369 self.makeSubtask(
"astrometry", schema=self.
schema1)
377 field = item.getField()
378 name = field.getName()
379 if count > beginInitial
and count <= endInitial:
380 name =
"initial" + separator + name
381 self.schemaMapper.addMapping(item.key, name)
384 schema = self.schemaMapper.editOutputSchema()
385 self.makeSubtask(
"measurement", schema=schema, algMetadata=self.
algMetadata)
386 self.makeSubtask(
"measureApCorr", schema=schema)
387 self.makeSubtask(
"photocal", schema=schema)
390 self.
schema = self.schemaMapper.getOutputSchema()
394 Return a sequence of schema keys that represent fields that should be propagated from
395 icSrc to src by ProcessCcdTask.
397 if self.config.doPsf:
398 return (self.measurePsf.candidateKey, self.measurePsf.usedKey)
403 def run(self, exposure, defects=None, idFactory=None):
404 """!Run the calibration task on an exposure
406 \param[in,out] exposure Exposure to calibrate; measured PSF will be installed there as well
407 \param[in] defects List of defects on exposure
408 \param[in] idFactory afw.table.IdFactory to use for source catalog.
409 \return a pipeBase.Struct with fields:
410 - exposure: Repaired exposure
411 - backgrounds: A list of background models applied in the calibration phase
412 - psf: Point spread function
413 - sources: Sources used in calibration
414 - matches: A list of reference object/source matches (an lsst.afw.table.ReferenceMatchVector)
415 - matchMeta: Metadata about the field (an lsst.daf.base.PropertyList)
416 - photocal: Output of photocal subtask
418 It is moderately important to provide a decent initial guess for the seeing if you want to
419 deal with cosmic rays. If there's a PSF in the exposure it'll be used; failing that the
420 CalibrateConfig.initialPsf is consulted (although the pixel scale will be taken from the
423 If the exposure contains an lsst.afw.image.Calib object with the exposure time set, MAGZERO
424 will be set in the task metadata.
426 assert exposure
is not None,
"No exposure provided"
428 if not exposure.hasPsf():
430 if idFactory
is None:
431 idFactory = afwTable.IdFactory.makeSimple()
432 backgrounds = afwMath.BackgroundList()
434 self.repair.run(exposure, defects=defects, keepCRs=keepCRs)
435 self.display(
'repair', exposure=exposure)
437 if self.config.doBackground:
438 with self.timer(
"background"):
439 bg, exposure = measAlg.estimateBackground(exposure, self.config.background, subtract=
True)
440 backgrounds.append(bg)
441 self.display(
'background', exposure=exposure)
444 table1 = afwTable.SourceTable.make(self.
schema1, idFactory)
446 detRet = self.detection.makeSourceCatalog(table1, exposure)
447 sources1 = detRet.sources
448 if detRet.fpSets.background:
449 backgrounds.append(detRet.fpSets.background)
453 self.initialMeasurement.run(exposure, sources1, allowApCorr=
False)
455 if self.config.doPsf:
456 if self.config.doAstrometry:
457 astromRet = self.astrometry.run(exposure, sources1)
458 matches = astromRet.matches
463 psfRet = self.measurePsf.run(exposure, sources1, matches=matches)
465 elif exposure.hasPsf():
466 psf = exposure.getPsf()
472 if self.config.doPsf:
473 self.repair.run(exposure, defects=defects, keepCRs=
None)
474 self.display(
'PSF_repair', exposure=exposure)
476 if self.config.doBackground:
480 with self.timer(
"background"):
482 bg, exposure = measAlg.estimateBackground(
483 exposure, self.config.background, subtract=
True,
484 statsKeys=(
'BGMEAN2',
'BGVAR2'))
485 self.log.info(
"Fit and subtracted background")
486 backgrounds.append(bg)
488 self.display(
'PSF_background', exposure=exposure)
492 table2 = afwTable.SourceTable.make(self.
schema, idFactory)
499 if self.config.doMeasureApCorr:
502 self.measurement.run(exposure, sources, endOrder=BasePlugin.APCORR_ORDER)
503 apCorrMap = self.measureApCorr.run(bbox=exposure.getBBox(), catalog=sources).apCorrMap
504 exposure.getInfo().setApCorrMap(apCorrMap)
505 self.measurement.run(exposure, sources, beginOrder=BasePlugin.APCORR_ORDER)
507 self.measurement.run(exposure, sources)
509 if self.config.doAstrometry:
510 astromRet = self.astrometry.run(exposure, sources)
511 matches = astromRet.matches
512 matchMeta = astromRet.matchMeta
514 matches, matchMeta =
None,
None
516 if self.config.doPhotoCal:
517 assert(matches
is not None)
519 photocalRet = self.photocal.run(exposure, matches)
521 self.log.warn(
"Failed to determine photometric zero-point: %s" % e)
523 self.metadata.set(
'MAGZERO', float(
"NaN"))
526 self.log.info(
"Photometric zero-point: %f" % photocalRet.calib.getMagnitude(1.0))
527 exposure.getCalib().setFluxMag0(photocalRet.calib.getFluxMag0())
528 metadata = exposure.getMetadata()
531 magZero = photocalRet.zp - 2.5 * math.log10(exposure.getCalib().getExptime() )
532 metadata.set(
'MAGZERO', magZero)
534 self.log.warn(
"Could not set normalized MAGZERO in header: no exposure time")
535 metadata.set(
'MAGZERO_RMS', photocalRet.sigma)
536 metadata.set(
'MAGZERO_NOBJ', photocalRet.ngood)
537 metadata.set(
'COLORTERM1', 0.0)
538 metadata.set(
'COLORTERM2', 0.0)
539 metadata.set(
'COLORTERM3', 0.0)
542 self.display(
'calibrate', exposure=exposure, sources=sources, matches=matches)
544 return pipeBase.Struct(
546 backgrounds = backgrounds,
550 matchMeta = matchMeta,
551 photocal = photocalRet,
555 """!Initialise the calibration procedure by setting the PSF to a configuration-defined guess.
557 \param[in,out] exposure Exposure to process; fake PSF will be installed here.
558 \throws AssertionError If exposure or exposure.getWcs() are None
560 assert exposure,
"No exposure provided"
562 wcs = exposure.getWcs()
564 pixelScale = wcs.pixelScale().asArcseconds()
566 pixelScale = self.config.initialPsf.pixelScale
568 cls = getattr(measAlg, self.config.initialPsf.model +
"Psf")
570 fwhm = self.config.initialPsf.fwhm/pixelScale
571 size = self.config.initialPsf.size
572 self.log.info(
"installInitialPsf fwhm=%s pixels; size=%s pixels" % (fwhm, size))
573 psf = cls(size, size, fwhm/(2*math.sqrt(2*math.log(2))))
def __init__
Create the calibration task.
def getCalibKeys
Return a sequence of schema keys that represent fields that should be propagated from icSrc to src by...
Class for storing ordered metadata with comments.
A mapping between the keys of two Schemas, used to copy data between them.
Describes the initial PSF used for detection and measurement before we do PSF determination.
def installInitialPsf
Initialise the calibration procedure by setting the PSF to a configuration-defined guess...
Custom catalog class for record/table subclasses that are guaranteed to have an ID, and should generally be sorted by that ID.
def run
Run the calibration task on an exposure.
Calibrate an exposure: measure PSF, subtract background, measure astrometry and photometry.