1 from __future__
import absolute_import, division
24 from itertools
import izip
33 from lsst.afw.image import abMagFromFlux, abMagErrFromFluxErr, fluxFromABMag, Calib
36 from .colorterms
import ColortermLibrary
38 __all__ = [
"PhotoCalTask",
"PhotoCalConfig"]
41 """!Return True if the given source has all good flags set and none of the bad flags set.
43 \param[in] source SourceRecord object to process.
44 \param[in] sourceKeys Struct of source catalog keys, as returned by PhotCalTask.getSourceKeys()
46 for k
in sourceKeys.goodFlags:
47 if not source.get(k):
return False
48 if source.getPsfFluxFlag():
return False
49 for k
in sourceKeys.badFlags:
50 if source.get(k):
return False
55 magLimit = pexConf.Field(dtype=float, doc=
"Don't use objects fainter than this magnitude", default=22.0)
56 doWriteOutput = pexConf.Field(
57 doc=
"Write a field name astrom_usedByPhotoCal to the schema",
61 fluxField = pexConf.Field(
62 doc=
"Name of the source flux field to use. The associated flag field\n"\
63 "('<name>.flags') will be implicitly included in badFlags.\n",
65 default=
"slot_CalibFlux_flux",
67 applyColorTerms = pexConf.Field(
68 doc=
"Apply photometric color terms to reference stars? One of: " + \
69 "None: apply if colorterms and photoCatName are not None; " + \
70 "fail if color term data is not available for the specified ref catalog and filter. " + \
71 "True: always apply colorterms; fail if color term data is not available for the " + \
72 "specified reference catalog and filter. " + \
73 "False: do not apply.",
78 goodFlags = pexConf.ListField(
79 doc=
"List of source flag fields that must be set for a source to be used.",
83 badFlags = pexConf.ListField(
84 doc=
"List of source flag fields that will cause a source to be rejected when they are set.",
86 default=[
"base_PixelFlags_flag_edge",
"base_PixelFlags_flag_interpolated",
87 "base_PixelFlags_flag_saturated"],
89 sigmaMax = pexConf.Field(
90 doc=
"maximum sigma to use when clipping",
95 nSigma = pexConf.Field(
100 useMedian = pexConf.Field(
101 doc=
"use median instead of mean to compute zeropoint",
105 nIter = pexConf.Field(
106 doc=
"number of iterations",
110 colorterms = pexConf.ConfigField(
111 doc=
"Library of photometric reference catalog name: color term dict",
112 dtype=ColortermLibrary,
114 photoCatName = pexConf.Field(
115 doc=
"Name of photometric reference catalog; used to select a color term dict in colorterms." + \
116 " see also applyColorTerms",
120 magErrFloor = pexConf.RangeField(
121 doc=
"Additional magnitude uncertainty to be added in quadrature with measurement errors.",
128 pexConf.Config.validate(self)
130 raise RuntimeError(
"applyColorTerms=True requires photoCatName is non-None")
132 raise RuntimeError(
"applyColorTerms=True requires colorterms be provided")
144 \anchor PhotoCalTask_
146 \brief Calculate the zero point of an exposure given a lsst.afw.table.ReferenceMatchVector.
148 \section pipe_tasks_photocal_Contents Contents
150 - \ref pipe_tasks_photocal_Purpose
151 - \ref pipe_tasks_photocal_Initialize
152 - \ref pipe_tasks_photocal_IO
153 - \ref pipe_tasks_photocal_Config
154 - \ref pipe_tasks_photocal_Debug
155 - \ref pipe_tasks_photocal_Example
157 \section pipe_tasks_photocal_Purpose Description
159 \copybrief PhotoCalTask
161 Calculate an Exposure's zero-point given a set of flux measurements of stars matched to an input catalogue.
162 The type of flux to use is specified by PhotoCalConfig.fluxField.
164 The algorithm clips outliers iteratively, with parameters set in the configuration.
166 \note This task can adds fields to the schema, so any code calling this task must ensure that
167 these columns are indeed present in the input match list; see \ref pipe_tasks_photocal_Example
169 \section pipe_tasks_photocal_Initialize Task initialisation
171 \copydoc \_\_init\_\_
173 \section pipe_tasks_photocal_IO Inputs/Outputs to the run method
177 \section pipe_tasks_photocal_Config Configuration parameters
179 See \ref PhotoCalConfig
181 \section pipe_tasks_photocal_Debug Debug variables
183 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a
184 flag \c -d to import \b debug.py from your \c PYTHONPATH; see \ref baseDebug for more about \b debug.py files.
186 The available variables in PhotoCalTask are:
189 <DD> If True enable other debug outputs
190 <DT> \c displaySources
191 <DD> If True, display the exposure on ds9's frame 1 and overlay the source catalogue:
196 <DD> Matched objects deemed unsuitable for photometric calibration.
197 Additional information is:
198 - a cyan o for galaxies
199 - a magenta o for variables
201 <DD> Objects that failed the flux cut
203 <DD> Objects used in the photometric calibration
206 <DD> Make a scatter plot of flux v. reference magnitude as a function of reference magnitude.
207 - good objects in blue
208 - rejected objects in red
209 (if \c scatterPlot is 2 or more, prompt to continue after each iteration)
212 \section pipe_tasks_photocal_Example A complete example of using PhotoCalTask
214 This code is in \link examples/photoCalTask.py\endlink, and can be run as \em e.g.
216 examples/photoCalTask.py
218 \dontinclude photoCalTask.py
220 Import the tasks (there are some other standard imports; read the file for details)
221 \skipline from lsst.pipe.tasks.astrometry
222 \skipline measPhotocal
224 We need to create both our tasks before processing any data as the task constructors
225 can add extra columns to the schema which we get from the input catalogue, \c scrCat:
229 \skip AstrometryTask.ConfigClass
231 (that \c filterMap line is because our test code doesn't use a filter that the reference catalogue recognises,
232 so we tell it to use the \c r band)
238 If the schema has indeed changed we need to add the new columns to the source table
239 (yes; this should be easier!)
243 We're now ready to process the data (we could loop over multiple exposures/catalogues using the same
248 We can then unpack and use the results:
253 To investigate the \ref pipe_tasks_photocal_Debug, put something like
257 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively
258 if name.endswith(".PhotoCal"):
263 lsstDebug.Info = DebugInfo
265 into your debug.py file and run photoCalTask.py with the \c --debug flag.
267 ConfigClass = PhotoCalConfig
268 _DefaultName =
"photoCal"
271 """!Create the photometric calibration task. See PhotoCalTask.init for documentation
273 pipeBase.Task.__init__(self, **kwds)
276 if self.config.doWriteOutput:
277 self.
outputField = schema.addField(
"photocal_photometricStandard", type=
"Flag",
278 doc=
"set if source was used in photometric calibration")
283 """!Return a struct containing the source catalog keys for fields used by PhotoCalTask.
285 Returned fields include:
288 - goodFlags: a list of keys for field names in self.config.goodFlags
289 - badFlags: a list of keys for field names in self.config.badFlags
291 goodFlags = [schema.find(name).key
for name
in self.config.goodFlags]
292 flux = schema.find(self.config.fluxField).key
293 fluxErr = schema.find(self.config.fluxField +
"Sigma").key
294 badFlags = [schema.find(name).key
for name
in self.config.badFlags]
296 return pipeBase.Struct(flux=flux, fluxErr=fluxErr, goodFlags=goodFlags, badFlags=badFlags)
300 """!Select reference/source matches according the criteria specified in the config.
302 \param[in] matches ReferenceMatchVector (not modified)
303 \param[in] sourceKeys Struct of source catalog keys, as returned by getSourceKeys()
304 \param[in] filterName name of camera filter; used to obtain the reference flux field
305 \param[in] frame ds9 frame number to use for debugging display
306 if frame is non-None, display information about trimmed objects on that ds9 frame:
308 - Unsuitable objects: blue + (and a cyan o if a galaxy)
309 - Failed flux cut: magenta *
311 \return a \link lsst.afw.table.ReferenceMatchVector\endlink that contains only the selected matches.
312 If a schema was passed during task construction, a flag field will be set on sources
313 in the selected matches.
315 \throws ValueError There are no valid matches.
318 self.log.logdebug(
"Number of input matches: %d" % (len(matches)))
319 if len(matches) == 0:
320 raise ValueError(
"No input matches")
323 afterFlagCutInd = [i
for i, m
in enumerate(matches)
if checkSourceFlags(m.second, sourceKeys)]
324 afterFlagCut = [matches[i]
for i
in afterFlagCutInd]
325 self.log.logdebug(
"Number of matches after source flag cuts: %d" % (len(afterFlagCut)))
327 if len(afterFlagCut) != len(matches):
328 if frame
is not None:
329 with ds9.Buffering():
330 for i, m
in enumerate(matches):
331 if i
not in afterFlagCutInd:
332 x, y = m.second.getCentroid()
333 ds9.dot(
"x", x, y, size=4, frame=frame, ctype=ds9.RED)
335 matches = afterFlagCut
337 if len(matches) == 0:
338 raise ValueError(
"All matches eliminated by source flags")
340 refSchema = matches[0].first.schema
342 photometricKey = refSchema.find(
"photometric").key
344 resolvedKey = refSchema.find(
"resolved").key
349 variableKey = refSchema.find(
"variable").key
353 self.log.warn(
"No 'photometric' flag key found in reference schema.")
354 photometricKey =
None
356 if photometricKey
is not None:
357 afterRefCutInd = [i
for i, m
in enumerate(matches)
if m.first.get(photometricKey)]
358 afterRefCut = [matches[i]
for i
in afterRefCutInd]
360 if len(afterRefCut) != len(matches):
361 if frame
is not None:
362 with ds9.Buffering():
363 for i, m
in enumerate(matches):
364 if i
not in afterRefCutInd:
365 x, y = m.second.getCentroid()
366 ds9.dot(
"+", x, y, size=4, frame=frame, ctype=ds9.BLUE)
368 if resolvedKey
and m.first.get(resolvedKey):
369 ds9.dot(
"o", x, y, size=6, frame=frame, ctype=ds9.CYAN)
370 if variableKey
and m.first.get(variableKey):
371 ds9.dot(
"o", x, y, size=6, frame=frame, ctype=ds9.MAGENTA)
373 matches = afterRefCut
375 self.log.logdebug(
"Number of matches after reference catalog cuts: %d" % (len(matches)))
376 if len(matches) == 0:
377 raise RuntimeError(
"No sources remain in match list after reference catalog cuts.")
379 fluxKey = refSchema.find(fluxName).key
380 if self.config.magLimit
is not None:
383 afterMagCutInd = [i
for i, m
in enumerate(matches)
if (m.first.get(fluxKey) > fluxLimit
384 and m.second.getPsfFlux() > 0.0)]
386 afterMagCutInd = [i
for i, m
in enumerate(matches)
if m.second.getPsfFlux() > 0.0]
388 afterMagCut = [matches[i]
for i
in afterMagCutInd]
390 if len(afterMagCut) != len(matches):
391 if frame
is not None:
392 with ds9.Buffering():
393 for i, m
in enumerate(matches):
394 if i
not in afterMagCutInd:
395 x, y = m.second.getCentroid()
396 ds9.dot(
"*", x, y, size=4, frame=frame, ctype=ds9.MAGENTA)
398 matches = afterMagCut
400 self.log.logdebug(
"Number of matches after magnitude limit cuts: %d" % (len(matches)))
402 if len(matches) == 0:
403 raise RuntimeError(
"No sources remaining in match list after magnitude limit cuts.")
405 if frame
is not None:
406 with ds9.Buffering():
408 x, y = m.second.getCentroid()
409 ds9.dot(
"o", x, y, size=4, frame=frame, ctype=ds9.GREEN)
420 """!Extract magnitude and magnitude error arrays from the given matches.
422 \param[in] matches Reference/source matches, a \link lsst::afw::table::ReferenceMatchVector\endlink
423 \param[in] filterName Name of filter being calibrated
424 \param[in] sourceKeys Struct of source catalog keys, as returned by getSourceKeys()
426 \return Struct containing srcMag, refMag, srcMagErr, refMagErr, and magErr numpy arrays
427 where magErr is an error in the magnitude; the error in srcMag - refMag
428 If nonzero, config.magErrFloor will be added to magErr *only* (not srcMagErr or refMagErr), as
429 magErr is what is later used to determine the zero point.
430 Struct also contains refFluxFieldList: a list of field names of the reference catalog used for fluxes
432 \note These magnitude arrays are the \em inputs to the photometric calibration, some may have been
433 discarded by clipping while estimating the calibration (https://jira.lsstcorp.org/browse/DM-813)
435 srcFluxArr = np.array([m.second.get(sourceKeys.flux)
for m
in matches])
436 srcFluxErrArr = np.array([m.second.get(sourceKeys.fluxErr)
for m
in matches])
437 if not np.all(np.isfinite(srcFluxErrArr)):
439 self.log.warn(
"Source catalog does not have flux uncertainties; using sqrt(flux).")
440 srcFluxErrArr = np.sqrt(srcFluxArr)
443 JanskysPerABFlux = 3631.0
444 srcFluxArr = srcFluxArr * JanskysPerABFlux
445 srcFluxErrArr = srcFluxErrArr * JanskysPerABFlux
448 raise RuntimeError(
"No reference stars are available")
449 refSchema = matches[0].first.schema
451 applyColorTerms = self.config.applyColorTerms
452 applyCTReason =
"config.applyColorTerms is %s" % (self.config.applyColorTerms,)
453 if self.config.applyColorTerms
is None:
455 ctDataAvail = len(self.config.colorterms.data) > 0
456 photoCatSpecified = self.config.photoCatName
is not None
457 applyCTReason +=
" and data %s available" % (
"is" if ctDataAvail
else "is not")
458 applyCTReason +=
" and photoRefCat %s None" % (
"is not" if photoCatSpecified
else "is")
459 applyColorTerms = ctDataAvail
and photoCatSpecified
462 self.log.info(
"Applying color terms for filterName=%r, config.photoCatName=%s because %s" %
463 (filterName, self.config.photoCatName, applyCTReason))
464 ct = self.config.colorterms.getColorterm(
465 filterName=filterName, photoCatName=self.config.photoCatName, doRaise=
True)
467 self.log.info(
"Not applying color terms because %s" % (applyCTReason,))
471 fluxFieldList = [
getRefFluxField(refSchema, filt)
for filt
in (ct.primary, ct.secondary)]
472 missingFluxFieldList = []
473 for fluxField
in fluxFieldList:
475 refSchema.find(fluxField).key
477 missingFluxFieldList.append(fluxField)
479 if missingFluxFieldList:
480 self.log.warn(
"Source catalog does not have fluxes for %s; ignoring color terms" %
481 " ".join(missingFluxFieldList))
488 refFluxErrArrList = []
489 for fluxField
in fluxFieldList:
490 fluxKey = refSchema.find(fluxField).key
491 refFluxArr = np.array([m.first.get(fluxKey)
for m
in matches])
493 fluxErrKey = refSchema.find(fluxField +
"Sigma").key
494 refFluxErrArr = np.array([m.first.get(fluxErrKey)
for m
in matches])
497 self.log.warn(
"Reference catalog does not have flux uncertainties for %s; using sqrt(flux)."
499 refFluxErrArr = np.sqrt(refFluxArr)
501 refFluxArrList.append(refFluxArr)
502 refFluxErrArrList.append(refFluxErrArr)
505 refMagArr1 = np.array([
abMagFromFlux(rf1)
for rf1
in refFluxArrList[0]])
506 refMagArr2 = np.array([
abMagFromFlux(rf2)
for rf2
in refFluxArrList[1]])
508 refMagArr = ct.transformMags(refMagArr1, refMagArr2)
509 refFluxErrArr = ct.propagateFluxErrors(refFluxErrArrList[0], refFluxErrArrList[1])
511 refMagArr = np.array([
abMagFromFlux(rf)
for rf
in refFluxArrList[0]])
513 srcMagArr = np.array([
abMagFromFlux(sf)
for sf
in srcFluxArr])
517 magErrArr = np.array([
abMagErrFromFluxErr(fe, sf)
for fe, sf
in izip(srcFluxErrArr, srcFluxArr)])
518 if self.config.magErrFloor != 0.0:
519 magErrArr = (magErrArr**2 + self.config.magErrFloor**2)**0.5
521 srcMagErrArr = np.array([
abMagErrFromFluxErr(sfe, sf)
for sfe, sf
in izip(srcFluxErrArr, srcFluxArr)])
522 refMagErrArr = np.array([
abMagErrFromFluxErr(rfe, rf)
for rfe, rf
in izip(refFluxErrArr, refFluxArr)])
524 return pipeBase.Struct(
528 srcMagErr = srcMagErrArr,
529 refMagErr = refMagErrArr,
530 refFluxFieldList = fluxFieldList,
534 def run(self, exposure, matches):
535 """!Do photometric calibration - select matches to use and (possibly iteratively) compute
538 \param[in] exposure Exposure upon which the sources in the matches were detected.
539 \param[in] matches Input lsst.afw.table.ReferenceMatchVector
540 (\em i.e. a list of lsst.afw.table.Match with
541 \c first being of type lsst.afw.table.SimpleRecord and \c second type lsst.afw.table.SourceRecord ---
542 the reference object and matched object respectively).
543 (will not be modified except to set the outputField if requested.).
546 - calib ------- \link lsst::afw::image::Calib\endlink object containing the zero point
547 - arrays ------ Magnitude arrays returned be PhotoCalTask.extractMagArrays
548 - matches ----- Final ReferenceMatchVector, as returned by PhotoCalTask.selectMatches.
550 The exposure is only used to provide the name of the filter being calibrated (it may also be
551 used to generate debugging plots).
553 The reference objects:
554 - Must include a field \c photometric; True for objects which should be considered as
555 photometric standards
556 - Must include a field \c flux; the flux used to impose a magnitude limit and also to calibrate
557 the data to (unless a color term is specified, in which case ColorTerm.primary is used;
558 See https://jira.lsstcorp.org/browse/DM-933)
559 - May include a field \c stargal; if present, True means that the object is a star
560 - May include a field \c var; if present, True means that the object is variable
562 The measured sources:
563 - Must include PhotoCalConfig.fluxField; the flux measurement to be used for calibration
565 \throws RuntimeError with the following strings:
568 <DT> `sources' schema does not contain the calibration object flag "XXX"`
569 <DD> The constructor added fields to the schema that aren't in the Sources
570 <DT> No input matches
571 <DD> The input match vector is empty
572 <DT> All matches eliminated by source flags
573 <DD> The flags specified by \c badFlags in the config eliminated all candidate objects
574 <DT> No sources remain in match list after reference catalog cuts
575 <DD> The reference catalogue has a column "photometric", but no matched objects have it set
576 <DT> No sources remaining in match list after magnitude limit cuts
577 <DD> All surviving matches are either too faint in the catalogue or have negative or \c NaN flux
578 <DT> No reference stars are available
579 <DD> No matches survive all the checks
585 displaySources = display
and lsstDebug.Info(__name__).displaySources
589 from matplotlib
import pyplot
593 self.
fig = pyplot.figure()
597 ds9.mtv(exposure, frame=frame, title=
"photocal")
601 filterName = exposure.getFilter().getName()
603 matches = self.
selectMatches(matches=matches, sourceKeys=sourceKeys, filterName=filterName,
605 arrays = self.
extractMagArrays(matches=matches, filterName=filterName, sourceKeys=sourceKeys)
611 matches[0].second.getSchema().find(self.
outputField)
613 raise RuntimeError(
"sources' schema does not contain the used-in-calibration flag \"%s\"" %
614 self.config.outputField)
622 r = self.
getZeroPoint(arrays.srcMag, arrays.refMag, arrays.magErr, zp0=zp)
624 self.log.info(
"Magnitude zero point: %f +/- %f from %d stars" % (r.zp, r.sigma, r.ngood))
626 flux0 = 10**(0.4*r.zp)
627 flux0err = 0.4*math.log(10)*flux0*r.sigma
629 calib.setFluxMag0(flux0, flux0err)
631 return pipeBase.Struct(
641 """!Flux calibration code, returning (ZeroPoint, Distribution Width, Number of stars)
643 We perform nIter iterations of a simple sigma-clipping algorithm with a couple of twists:
644 1. We use the median/interquartile range to estimate the position to clip around, and the
646 2. We never allow sigma to go _above_ a critical value sigmaMax --- if we do, a sufficiently
647 large estimate will prevent the clipping from ever taking effect.
648 3. Rather than start with the median we start with a crude mode. This means that a set of magnitude
649 residuals with a tight core and asymmetrical outliers will start in the core. We use the width of
650 this core to set our maximum sigma (see 2.)
652 sigmaMax = self.config.sigmaMax
656 indArr = np.argsort(dmag)
659 if srcErr
is not None:
660 dmagErr = srcErr[indArr]
662 dmagErr = np.ones(len(dmag))
665 ind_noNan = np.array([i
for i
in range(len(dmag))
666 if (
not np.isnan(dmag[i])
and not np.isnan(dmagErr[i])) ])
667 dmag = dmag[ind_noNan]
668 dmagErr = dmagErr[ind_noNan]
670 IQ_TO_STDEV = 0.741301109252802;
675 for i
in range(self.config.nIter):
686 hist, edges = np.histogram(dmag, nhist, new=
True)
688 hist, edges = np.histogram(dmag, nhist)
689 imode = np.arange(nhist)[np.where(hist == hist.max())]
691 if imode[-1] - imode[0] + 1 == len(imode):
695 center = 0.5*(edges[imode[0]] + edges[imode[-1] + 1])
697 peak =
sum(hist[imode])/len(imode)
701 while j >= 0
and hist[j] > 0.5*peak:
704 q1 = dmag[
sum(hist[range(j)])]
707 while j < nhist
and hist[j] > 0.5*peak:
709 j = min(j, nhist - 1)
710 j = min(
sum(hist[range(j)]), npt - 1)
714 q1 = dmag[int(0.25*npt)]
715 q3 = dmag[int(0.75*npt)]
722 self.log.logdebug(
"Photo calibration histogram: center = %.2f, sig = %.2f"
727 sigmaMax = dmag[-1] - dmag[0]
729 center = np.median(dmag)
730 q1 = dmag[int(0.25*npt)]
731 q3 = dmag[int(0.75*npt)]
736 if self.config.useMedian:
737 center = np.median(gdmag)
739 gdmagErr = dmagErr[good]
740 center = np.average(gdmag, weights=gdmagErr)
742 q3 = gdmag[min(int(0.75*npt + 0.5), npt - 1)]
743 q1 = gdmag[min(int(0.25*npt + 0.5), npt - 1)]
745 sig = IQ_TO_STDEV*(q3 - q1)
747 good = abs(dmag - center) < self.config.nSigma*min(sig, sigmaMax)
754 axes = self.fig.add_axes((0.1, 0.1, 0.85, 0.80));
756 axes.plot(ref[good], dmag[good] - center,
"b+")
757 axes.errorbar(ref[good], dmag[good] - center, yerr=dmagErr[good],
758 linestyle=
'', color=
'b')
760 bad = np.logical_not(good)
761 if len(ref[bad]) > 0:
762 axes.plot(ref[bad], dmag[bad] - center,
"r+")
763 axes.errorbar(ref[bad], dmag[bad] - center, yerr=dmagErr[bad],
764 linestyle=
'', color=
'r')
766 axes.plot((-100, 100), (0, 0), "g-")
768 axes.plot((-100, 100), x*0.05*np.ones(2),
"g--")
770 axes.set_ylim(-1.1, 1.1)
771 axes.set_xlim(24, 13)
772 axes.set_xlabel(
"Reference")
773 axes.set_ylabel(
"Reference - Instrumental")
779 while i == 0
or reply !=
"c":
781 reply = raw_input(
"Next iteration? [ynhpc] ")
786 print >> sys.stderr,
"Options: c[ontinue] h[elp] n[o] p[db] y[es]"
789 if reply
in (
"",
"c",
"n",
"p",
"y"):
792 print >> sys.stderr,
"Unrecognised response: %s" % reply
797 import pdb; pdb.set_trace()
799 print >> sys.stderr,
"Error plotting in PhotoCal.getZeroPoint: %s" % e
806 msg =
"PhotoCal.getZeroPoint: no good stars remain"
809 center = np.average(dmag, weights=dmagErr)
810 msg +=
" on first iteration; using average of all calibration stars"
812 self.log.log(self.log.WARN, msg)
814 return pipeBase.Struct(
819 elif ngood == old_ngood:
825 dmagErr = dmagErr[good]
828 dmagErr = dmagErr[good]
829 zp, weightSum = np.average(dmag, weights=1/dmagErr**2, returned=
True)
830 sigma = np.sqrt(1.0/weightSum)
831 return pipeBase.Struct(
double abMagErrFromFluxErr(double fluxErr, double flux)
Compute AB magnitude error from flux and flux error in Janskys.
Calculate the zero point of an exposure given a lsst.afw.table.ReferenceMatchVector.
std::vector< ReferenceMatch > ReferenceMatchVector
def checkSourceFlags
Return True if the given source has all good flags set and none of the bad flags set.
double abMagFromFlux(double flux)
Compute AB magnitude from flux in Janskys.
boost::enable_if< typename ExpressionTraits< Scalar >::IsScalar, Scalar >::type sum(Scalar const &scalar)
double fluxFromABMag(double mag)
Compute flux in Janskys from AB magnitude.
def run
Do photometric calibration - select matches to use and (possibly iteratively) compute the zero point...
def extractMagArrays
Extract magnitude and magnitude error arrays from the given matches.
def getRefFluxField
Get name of flux field in schema.
def __init__
Create the photometric calibration task.
def getZeroPoint
Flux calibration code, returning (ZeroPoint, Distribution Width, Number of stars) ...
def getSourceKeys
Return a struct containing the source catalog keys for fields used by PhotoCalTask.
def selectMatches
Select reference/source matches according the criteria specified in the config.