1 from __future__
import absolute_import, division
2 from __future__
import print_function
3 from builtins
import zip
4 from builtins
import input
5 from builtins
import range
37 from lsst.afw.image import abMagFromFlux, abMagErrFromFluxErr, fluxFromABMag, Calib
41 from .colorterms
import ColortermLibrary
43 __all__ = [
"PhotoCalTask",
"PhotoCalConfig"]
47 """!Return True if the given source has all good flags set and none of the bad flags set.
49 \param[in] source SourceRecord object to process.
50 \param[in] sourceKeys Struct of source catalog keys, as returned by PhotCalTask.getSourceKeys()
52 for k
in sourceKeys.goodFlags:
55 if source.getPsfFluxFlag():
57 for k
in sourceKeys.badFlags:
64 """Config for PhotoCal"""
65 magLimit = pexConf.Field(
68 doc=
"Don't use objects fainter than this magnitude",
70 doWriteOutput = pexConf.Field(
73 doc=
"Write a field name astrom_usedByPhotoCal to the schema",
75 fluxField = pexConf.Field(
77 default=
"slot_CalibFlux_flux",
78 doc=(
"Name of the source flux field to use. The associated flag field\n"
79 "('<name>_flags') will be implicitly included in badFlags."),
81 applyColorTerms = pexConf.Field(
84 doc=(
"Apply photometric color terms to reference stars? One of:\n"
85 "None: apply if colorterms and photoCatName are not None;\n"
86 " fail if color term data is not available for the specified ref catalog and filter.\n"
87 "True: always apply colorterms; fail if color term data is not available for the\n"
88 " specified reference catalog and filter.\n"
89 "False: do not apply."),
92 goodFlags = pexConf.ListField(
95 doc=
"List of source flag fields that must be set for a source to be used.",
97 badFlags = pexConf.ListField(
99 default=[
"base_PixelFlags_flag_edge",
"base_PixelFlags_flag_interpolated",
100 "base_PixelFlags_flag_saturated"],
101 doc=
"List of source flag fields that will cause a source to be rejected when they are set.",
103 sigmaMax = pexConf.Field(
106 doc=
"maximum sigma to use when clipping",
109 nSigma = pexConf.Field(
112 doc=
"clip at nSigma",
114 useMedian = pexConf.Field(
117 doc=
"use median instead of mean to compute zeropoint",
119 nIter = pexConf.Field(
122 doc=
"number of iterations",
124 colorterms = pexConf.ConfigField(
125 dtype=ColortermLibrary,
126 doc=
"Library of photometric reference catalog name: color term dict",
128 photoCatName = pexConf.Field(
131 doc=(
"Name of photometric reference catalog; used to select a color term dict in colorterms."
132 " see also applyColorTerms"),
134 magErrFloor = pexConf.RangeField(
137 doc=
"Additional magnitude uncertainty to be added in quadrature with measurement errors.",
140 doSelectUnresolved = pexConf.Field(
143 doc=(
"Use the extendedness parameter to select objects to use in photometric calibration?\n"
144 "This applies only to the sources detected on the exposure, not the reference catalog"),
148 pexConf.Config.validate(self)
150 raise RuntimeError(
"applyColorTerms=True requires photoCatName is non-None")
152 raise RuntimeError(
"applyColorTerms=True requires colorterms be provided")
164 \anchor PhotoCalTask_
166 \brief Calculate the zero point of an exposure given a lsst.afw.table.ReferenceMatchVector.
168 \section pipe_tasks_photocal_Contents Contents
170 - \ref pipe_tasks_photocal_Purpose
171 - \ref pipe_tasks_photocal_Initialize
172 - \ref pipe_tasks_photocal_IO
173 - \ref pipe_tasks_photocal_Config
174 - \ref pipe_tasks_photocal_Debug
175 - \ref pipe_tasks_photocal_Example
177 \section pipe_tasks_photocal_Purpose Description
179 \copybrief PhotoCalTask
181 Calculate an Exposure's zero-point given a set of flux measurements of stars matched to an input catalogue.
182 The type of flux to use is specified by PhotoCalConfig.fluxField.
184 The algorithm clips outliers iteratively, with parameters set in the configuration.
186 \note This task can adds fields to the schema, so any code calling this task must ensure that
187 these columns are indeed present in the input match list; see \ref pipe_tasks_photocal_Example
189 \section pipe_tasks_photocal_Initialize Task initialisation
191 \copydoc \_\_init\_\_
193 \section pipe_tasks_photocal_IO Inputs/Outputs to the run method
197 \section pipe_tasks_photocal_Config Configuration parameters
199 See \ref PhotoCalConfig
201 \section pipe_tasks_photocal_Debug Debug variables
203 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a
204 flag \c -d to import \b debug.py from your \c PYTHONPATH; see \ref baseDebug for more about \b debug.py files.
206 The available variables in PhotoCalTask are:
209 <DD> If True enable other debug outputs
210 <DT> \c displaySources
211 <DD> If True, display the exposure on ds9's frame 1 and overlay the source catalogue:
216 <DD> Matched objects deemed unsuitable for photometric calibration.
217 Additional information is:
218 - a cyan o for galaxies
219 - a magenta o for variables
221 <DD> Objects that failed the flux cut
223 <DD> Objects used in the photometric calibration
226 <DD> Make a scatter plot of flux v. reference magnitude as a function of reference magnitude.
227 - good objects in blue
228 - rejected objects in red
229 (if \c scatterPlot is 2 or more, prompt to continue after each iteration)
232 \section pipe_tasks_photocal_Example A complete example of using PhotoCalTask
234 This code is in \link examples/photoCalTask.py\endlink, and can be run as \em e.g.
236 examples/photoCalTask.py
238 \dontinclude photoCalTask.py
240 Import the tasks (there are some other standard imports; read the file for details)
241 \skipline from lsst.pipe.tasks.astrometry
242 \skipline measPhotocal
244 We need to create both our tasks before processing any data as the task constructors
245 can add extra columns to the schema which we get from the input catalogue, \c scrCat:
249 \skip AstrometryTask.ConfigClass
251 (that \c filterMap line is because our test code doesn't use a filter that the reference catalogue recognises,
252 so we tell it to use the \c r band)
258 If the schema has indeed changed we need to add the new columns to the source table
259 (yes; this should be easier!)
263 We're now ready to process the data (we could loop over multiple exposures/catalogues using the same
268 We can then unpack and use the results:
273 To investigate the \ref pipe_tasks_photocal_Debug, put something like
277 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively
278 if name.endswith(".PhotoCal"):
283 lsstDebug.Info = DebugInfo
285 into your debug.py file and run photoCalTask.py with the \c --debug flag.
287 ConfigClass = PhotoCalConfig
288 _DefaultName =
"photoCal"
290 def __init__(self, refObjLoader, schema=None, **kwds):
291 """!Create the photometric calibration task. See PhotoCalTask.init for documentation
293 RefMatchTask.__init__(self, refObjLoader, schema=schema, **kwds)
296 if self.config.doWriteOutput:
297 self.
outputField = schema.addField(
"photocal_photometricStandard", type=
"Flag",
298 doc=
"set if source was used in photometric calibration")
303 """!Return a struct containing the source catalog keys for fields used by PhotoCalTask.
305 Returned fields include:
308 - goodFlags: a list of keys for field names in self.config.goodFlags
309 - badFlags: a list of keys for field names in self.config.badFlags
310 - starGal: key for star/galaxy classification
312 goodFlags = [schema.find(name).key
for name
in self.config.goodFlags]
313 flux = schema.find(self.config.fluxField).key
314 fluxErr = schema.find(self.config.fluxField +
"Sigma").key
315 badFlags = [schema.find(name).key
for name
in self.config.badFlags]
317 starGal = schema.find(
"base_ClassificationExtendedness_value").key
320 return pipeBase.Struct(flux=flux, fluxErr=fluxErr, goodFlags=goodFlags, badFlags=badFlags,
324 """!Return whether the provided source is unresolved or not
326 This particular implementation is designed to work with the
327 base_ClassificationExtendedness_value=0.0 or 1.0 scheme. Because
328 of the diversity of star/galaxy classification outputs (binary
329 decision vs probabilities; signs), it's difficult to make this
330 configurable without using code. This method should therefore
331 be overridden to use the appropriate classification output.
333 \param[in] source Source to test
334 \param[in] starGalKey Struct of schema keys for source
335 \return boolean value for starGalKey (True indicates Unresolved)
337 return source.get(starGalKey) < 0.5
if starGalKey
is not None else True
341 """!Select reference/source matches according the criteria specified in the config.
343 \param[in] matches ReferenceMatchVector (not modified)
344 \param[in] sourceKeys Struct of source catalog keys, as returned by getSourceKeys()
345 \param[in] filterName name of camera filter; used to obtain the reference flux field
346 \param[in] frame ds9 frame number to use for debugging display
347 if frame is non-None, display information about trimmed objects on that ds9 frame:
349 - Unsuitable objects: blue + (and a cyan o if a galaxy)
350 - Failed flux cut: magenta *
352 \return a \link lsst.afw.table.ReferenceMatchVector\endlink that contains only the selected matches.
353 If a schema was passed during task construction, a flag field will be set on sources
354 in the selected matches.
356 \throws ValueError There are no valid matches.
358 self.log.debug(
"Number of input matches: %d", len(matches))
360 if self.config.doSelectUnresolved:
362 matches = [m
for m
in matches
if self.
isUnresolved(m.second, sourceKeys.starGal)]
363 self.log.debug(
"Number of matches after culling resolved sources: %d", len(matches))
365 if len(matches) == 0:
366 raise ValueError(
"No input matches")
369 afterFlagCutInd = [i
for i, m
in enumerate(matches)
if checkSourceFlags(m.second, sourceKeys)]
370 afterFlagCut = [matches[i]
for i
in afterFlagCutInd]
371 self.log.debug(
"Number of matches after source flag cuts: %d", len(afterFlagCut))
373 if len(afterFlagCut) != len(matches):
374 if frame
is not None:
375 with ds9.Buffering():
376 for i, m
in enumerate(matches):
377 if i
not in afterFlagCutInd:
378 x, y = m.second.getCentroid()
379 ds9.dot(
"x", x, y, size=4, frame=frame, ctype=ds9.RED)
381 matches = afterFlagCut
383 if len(matches) == 0:
384 raise ValueError(
"All matches eliminated by source flags")
386 refSchema = matches[0].first.schema
388 photometricKey = refSchema.find(
"photometric").key
390 resolvedKey = refSchema.find(
"resolved").key
395 variableKey = refSchema.find(
"variable").key
399 self.log.warn(
"No 'photometric' flag key found in reference schema.")
400 photometricKey =
None
402 if photometricKey
is not None:
403 afterRefCutInd = [i
for i, m
in enumerate(matches)
if m.first.get(photometricKey)]
404 afterRefCut = [matches[i]
for i
in afterRefCutInd]
406 if len(afterRefCut) != len(matches):
407 if frame
is not None:
408 with ds9.Buffering():
409 for i, m
in enumerate(matches):
410 if i
not in afterRefCutInd:
411 x, y = m.second.getCentroid()
412 ds9.dot(
"+", x, y, size=4, frame=frame, ctype=ds9.BLUE)
414 if resolvedKey
and m.first.get(resolvedKey):
415 ds9.dot(
"o", x, y, size=6, frame=frame, ctype=ds9.CYAN)
416 if variableKey
and m.first.get(variableKey):
417 ds9.dot(
"o", x, y, size=6, frame=frame, ctype=ds9.MAGENTA)
419 matches = afterRefCut
421 self.log.debug(
"Number of matches after reference catalog cuts: %d", len(matches))
422 if len(matches) == 0:
423 raise RuntimeError(
"No sources remain in match list after reference catalog cuts.")
425 fluxKey = refSchema.find(fluxName).key
426 if self.config.magLimit
is not None:
429 afterMagCutInd = [i
for i, m
in enumerate(matches)
if (m.first.get(fluxKey) > fluxLimit
and
430 m.second.getPsfFlux() > 0.0)]
432 afterMagCutInd = [i
for i, m
in enumerate(matches)
if m.second.getPsfFlux() > 0.0]
434 afterMagCut = [matches[i]
for i
in afterMagCutInd]
436 if len(afterMagCut) != len(matches):
437 if frame
is not None:
438 with ds9.Buffering():
439 for i, m
in enumerate(matches):
440 if i
not in afterMagCutInd:
441 x, y = m.second.getCentroid()
442 ds9.dot(
"*", x, y, size=4, frame=frame, ctype=ds9.MAGENTA)
444 matches = afterMagCut
446 self.log.debug(
"Number of matches after magnitude limit cuts: %d", len(matches))
448 if len(matches) == 0:
449 raise RuntimeError(
"No sources remaining in match list after magnitude limit cuts.")
451 if frame
is not None:
452 with ds9.Buffering():
454 x, y = m.second.getCentroid()
455 ds9.dot(
"o", x, y, size=4, frame=frame, ctype=ds9.GREEN)
466 """!Extract magnitude and magnitude error arrays from the given matches.
468 \param[in] matches Reference/source matches, a \link lsst::afw::table::ReferenceMatchVector\endlink
469 \param[in] filterName Name of filter being calibrated
470 \param[in] sourceKeys Struct of source catalog keys, as returned by getSourceKeys()
472 \return Struct containing srcMag, refMag, srcMagErr, refMagErr, and magErr numpy arrays
473 where magErr is an error in the magnitude; the error in srcMag - refMag
474 If nonzero, config.magErrFloor will be added to magErr *only* (not srcMagErr or refMagErr), as
475 magErr is what is later used to determine the zero point.
476 Struct also contains refFluxFieldList: a list of field names of the reference catalog used for fluxes
478 \note These magnitude arrays are the \em inputs to the photometric calibration, some may have been
479 discarded by clipping while estimating the calibration (https://jira.lsstcorp.org/browse/DM-813)
481 srcFluxArr = np.array([m.second.get(sourceKeys.flux)
for m
in matches])
482 srcFluxErrArr = np.array([m.second.get(sourceKeys.fluxErr)
for m
in matches])
483 if not np.all(np.isfinite(srcFluxErrArr)):
485 self.log.warn(
"Source catalog does not have flux uncertainties; using sqrt(flux).")
486 srcFluxErrArr = np.sqrt(srcFluxArr)
489 JanskysPerABFlux = 3631.0
490 srcFluxArr = srcFluxArr * JanskysPerABFlux
491 srcFluxErrArr = srcFluxErrArr * JanskysPerABFlux
494 raise RuntimeError(
"No reference stars are available")
495 refSchema = matches[0].first.schema
497 applyColorTerms = self.config.applyColorTerms
498 applyCTReason =
"config.applyColorTerms is %s" % (self.config.applyColorTerms,)
499 if self.config.applyColorTerms
is None:
501 ctDataAvail = len(self.config.colorterms.data) > 0
502 photoCatSpecified = self.config.photoCatName
is not None
503 applyCTReason +=
" and data %s available" % (
"is" if ctDataAvail
else "is not")
504 applyCTReason +=
" and photoRefCat %s None" % (
"is not" if photoCatSpecified
else "is")
505 applyColorTerms = ctDataAvail
and photoCatSpecified
508 self.log.info(
"Applying color terms for filterName=%r, config.photoCatName=%s because %s",
509 filterName, self.config.photoCatName, applyCTReason)
510 ct = self.config.colorterms.getColorterm(
511 filterName=filterName, photoCatName=self.config.photoCatName, doRaise=
True)
513 self.log.info(
"Not applying color terms because %s", applyCTReason)
517 fluxFieldList = [
getRefFluxField(refSchema, filt)
for filt
in (ct.primary, ct.secondary)]
518 missingFluxFieldList = []
519 for fluxField
in fluxFieldList:
521 refSchema.find(fluxField).key
523 missingFluxFieldList.append(fluxField)
525 if missingFluxFieldList:
526 self.log.warn(
"Source catalog does not have fluxes for %s; ignoring color terms",
527 " ".join(missingFluxFieldList))
534 refFluxErrArrList = []
535 for fluxField
in fluxFieldList:
536 fluxKey = refSchema.find(fluxField).key
537 refFluxArr = np.array([m.first.get(fluxKey)
for m
in matches])
539 fluxErrKey = refSchema.find(fluxField +
"Sigma").key
540 refFluxErrArr = np.array([m.first.get(fluxErrKey)
for m
in matches])
543 self.log.warn(
"Reference catalog does not have flux uncertainties for %s; using sqrt(flux).",
545 refFluxErrArr = np.sqrt(refFluxArr)
547 refFluxArrList.append(refFluxArr)
548 refFluxErrArrList.append(refFluxErrArr)
551 refMagArr1 = np.array([
abMagFromFlux(rf1)
for rf1
in refFluxArrList[0]])
552 refMagArr2 = np.array([
abMagFromFlux(rf2)
for rf2
in refFluxArrList[1]])
554 refMagArr = ct.transformMags(refMagArr1, refMagArr2)
555 refFluxErrArr = ct.propagateFluxErrors(refFluxErrArrList[0], refFluxErrArrList[1])
557 refMagArr = np.array([
abMagFromFlux(rf)
for rf
in refFluxArrList[0]])
559 srcMagArr = np.array([
abMagFromFlux(sf)
for sf
in srcFluxArr])
563 magErrArr = np.array([
abMagErrFromFluxErr(fe, sf)
for fe, sf
in zip(srcFluxErrArr, srcFluxArr)])
564 if self.config.magErrFloor != 0.0:
565 magErrArr = (magErrArr**2 + self.config.magErrFloor**2)**0.5
567 srcMagErrArr = np.array([
abMagErrFromFluxErr(sfe, sf)
for sfe, sf
in zip(srcFluxErrArr, srcFluxArr)])
568 refMagErrArr = np.array([
abMagErrFromFluxErr(rfe, rf)
for rfe, rf
in zip(refFluxErrArr, refFluxArr)])
570 return pipeBase.Struct(
574 srcMagErr=srcMagErrArr,
575 refMagErr=refMagErrArr,
576 refFluxFieldList=fluxFieldList,
580 def run(self, exposure, sourceCat):
581 """!Do photometric calibration - select matches to use and (possibly iteratively) compute
584 \param[in] exposure Exposure upon which the sources in the matches were detected.
585 \param[in] sourceCat A catalog of sources to use in the calibration
586 (\em i.e. a list of lsst.afw.table.Match with
587 \c first being of type lsst.afw.table.SimpleRecord and \c second type lsst.afw.table.SourceRecord ---
588 the reference object and matched object respectively).
589 (will not be modified except to set the outputField if requested.).
592 - calib ------- \link lsst::afw::image::Calib\endlink object containing the zero point
593 - arrays ------ Magnitude arrays returned be PhotoCalTask.extractMagArrays
594 - matches ----- Final ReferenceMatchVector, as returned by PhotoCalTask.selectMatches.
595 - zp ---------- Photometric zero point (mag)
596 - sigma ------- Standard deviation of fit of photometric zero point (mag)
597 - ngood ------- Number of sources used to fit photometric zero point
599 The exposure is only used to provide the name of the filter being calibrated (it may also be
600 used to generate debugging plots).
602 The reference objects:
603 - Must include a field \c photometric; True for objects which should be considered as
604 photometric standards
605 - Must include a field \c flux; the flux used to impose a magnitude limit and also to calibrate
606 the data to (unless a color term is specified, in which case ColorTerm.primary is used;
607 See https://jira.lsstcorp.org/browse/DM-933)
608 - May include a field \c stargal; if present, True means that the object is a star
609 - May include a field \c var; if present, True means that the object is variable
611 The measured sources:
612 - Must include PhotoCalConfig.fluxField; the flux measurement to be used for calibration
614 \throws RuntimeError with the following strings:
617 <DT> `sources' schema does not contain the calibration object flag "XXX"`
618 <DD> The constructor added fields to the schema that aren't in the Sources
619 <DT> No input matches
620 <DD> The input match vector is empty
621 <DT> All matches eliminated by source flags
622 <DD> The flags specified by \c badFlags in the config eliminated all candidate objects
623 <DT> No sources remain in match list after reference catalog cuts
624 <DD> The reference catalogue has a column "photometric", but no matched objects have it set
625 <DT> No sources remaining in match list after magnitude limit cuts
626 <DD> All surviving matches are either too faint in the catalogue or have negative or \c NaN flux
627 <DT> No reference stars are available
628 <DD> No matches survive all the checks
634 displaySources = display
and lsstDebug.Info(__name__).displaySources
638 from matplotlib
import pyplot
642 self.
fig = pyplot.figure()
646 ds9.mtv(exposure, frame=frame, title=
"photocal")
650 res = self.loadAndMatch(exposure, sourceCat)
651 matches = res.matches
652 filterName = exposure.getFilter().getName()
654 matches = self.
selectMatches(matches=matches, sourceKeys=sourceKeys, filterName=filterName,
656 arrays = self.
extractMagArrays(matches=matches, filterName=filterName, sourceKeys=sourceKeys)
662 matches[0].second.getSchema().find(self.
outputField)
664 raise RuntimeError(
"sources' schema does not contain the used-in-calibration flag \"%s\"" %
673 r = self.
getZeroPoint(arrays.srcMag, arrays.refMag, arrays.magErr, zp0=zp)
675 self.log.info(
"Magnitude zero point: %f +/- %f from %d stars", r.zp, r.sigma, r.ngood)
677 flux0 = 10**(0.4*r.zp)
678 flux0err = 0.4*math.log(10)*flux0*r.sigma
680 calib.setFluxMag0(flux0, flux0err)
682 return pipeBase.Struct(
692 """!Flux calibration code, returning (ZeroPoint, Distribution Width, Number of stars)
694 We perform nIter iterations of a simple sigma-clipping algorithm with a couple of twists:
695 1. We use the median/interquartile range to estimate the position to clip around, and the
697 2. We never allow sigma to go _above_ a critical value sigmaMax --- if we do, a sufficiently
698 large estimate will prevent the clipping from ever taking effect.
699 3. Rather than start with the median we start with a crude mode. This means that a set of magnitude
700 residuals with a tight core and asymmetrical outliers will start in the core. We use the width of
701 this core to set our maximum sigma (see 2.)
704 - zp ---------- Photometric zero point (mag)
705 - sigma ------- Standard deviation of fit of zero point (mag)
706 - ngood ------- Number of sources used to fit zero point
708 sigmaMax = self.config.sigmaMax
712 indArr = np.argsort(dmag)
715 if srcErr
is not None:
716 dmagErr = srcErr[indArr]
718 dmagErr = np.ones(len(dmag))
721 ind_noNan = np.array([i
for i
in range(len(dmag))
722 if (
not np.isnan(dmag[i])
and not np.isnan(dmagErr[i]))])
723 dmag = dmag[ind_noNan]
724 dmagErr = dmagErr[ind_noNan]
726 IQ_TO_STDEV = 0.741301109252802
731 for i
in range(self.config.nIter):
742 hist, edges = np.histogram(dmag, nhist, new=
True)
744 hist, edges = np.histogram(dmag, nhist)
745 imode = np.arange(nhist)[np.where(hist == hist.max())]
747 if imode[-1] - imode[0] + 1 == len(imode):
751 center = 0.5*(edges[imode[0]] + edges[imode[-1] + 1])
753 peak = sum(hist[imode])/len(imode)
757 while j >= 0
and hist[j] > 0.5*peak:
760 q1 = dmag[sum(hist[range(j)])]
763 while j < nhist
and hist[j] > 0.5*peak:
765 j = min(j, nhist - 1)
766 j = min(sum(hist[range(j)]), npt - 1)
770 q1 = dmag[int(0.25*npt)]
771 q3 = dmag[int(0.75*npt)]
778 self.log.debug(
"Photo calibration histogram: center = %.2f, sig = %.2f", center, sig)
782 sigmaMax = dmag[-1] - dmag[0]
784 center = np.median(dmag)
785 q1 = dmag[int(0.25*npt)]
786 q3 = dmag[int(0.75*npt)]
791 if self.config.useMedian:
792 center = np.median(gdmag)
794 gdmagErr = dmagErr[good]
795 center = np.average(gdmag, weights=gdmagErr)
797 q3 = gdmag[min(int(0.75*npt + 0.5), npt - 1)]
798 q1 = gdmag[min(int(0.25*npt + 0.5), npt - 1)]
800 sig = IQ_TO_STDEV*(q3 - q1)
802 good = abs(dmag - center) < self.config.nSigma*min(sig, sigmaMax)
809 axes = self.fig.add_axes((0.1, 0.1, 0.85, 0.80))
811 axes.plot(ref[good], dmag[good] - center,
"b+")
812 axes.errorbar(ref[good], dmag[good] - center, yerr=dmagErr[good],
813 linestyle=
'', color=
'b')
815 bad = np.logical_not(good)
816 if len(ref[bad]) > 0:
817 axes.plot(ref[bad], dmag[bad] - center,
"r+")
818 axes.errorbar(ref[bad], dmag[bad] - center, yerr=dmagErr[bad],
819 linestyle=
'', color=
'r')
821 axes.plot((-100, 100), (0, 0), "g-")
823 axes.plot((-100, 100), x*0.05*np.ones(2),
"g--")
825 axes.set_ylim(-1.1, 1.1)
826 axes.set_xlim(24, 13)
827 axes.set_xlabel(
"Reference")
828 axes.set_ylabel(
"Reference - Instrumental")
834 while i == 0
or reply !=
"c":
836 reply =
input(
"Next iteration? [ynhpc] ")
841 print(
"Options: c[ontinue] h[elp] n[o] p[db] y[es]", file=sys.stderr)
844 if reply
in (
"",
"c",
"n",
"p",
"y"):
847 print(
"Unrecognised response: %s" % reply, file=sys.stderr)
854 except Exception
as e:
855 print(
"Error plotting in PhotoCal.getZeroPoint: %s" % e, file=sys.stderr)
862 msg =
"PhotoCal.getZeroPoint: no good stars remain"
865 center = np.average(dmag, weights=dmagErr)
866 msg +=
" on first iteration; using average of all calibration stars"
870 return pipeBase.Struct(
874 elif ngood == old_ngood:
880 dmagErr = dmagErr[good]
883 dmagErr = dmagErr[good]
884 zp, weightSum = np.average(dmag, weights=1/dmagErr**2, returned=
True)
885 sigma = np.sqrt(1.0/weightSum)
886 return pipeBase.Struct(
double abMagErrFromFluxErr(double fluxErr, double flux)
Compute AB magnitude error from flux and flux error in Janskys.
std::vector< ReferenceMatch > ReferenceMatchVector
def checkSourceFlags
Return True if the given source has all good flags set and none of the bad flags set.
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations...
double abMagFromFlux(double flux)
Compute AB magnitude from flux in Janskys.
Describe an exposure's calibration.
double fluxFromABMag(double mag)
Compute flux in Janskys from AB magnitude.
def isUnresolved
Return whether the provided source is unresolved or not.
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.