24 import astropy.units
as u
35 from lsst.pipe.tasks.colorterms
import ColortermLibrary
36 from lsst.verify
import Job, Measurement
41 from .dataIds
import PerTractCcdDataIdContainer
46 __all__ = [
"JointcalConfig",
"JointcalRunner",
"JointcalTask"]
48 Photometry = collections.namedtuple(
'Photometry', (
'fit',
'model'))
49 Astrometry = collections.namedtuple(
'Astrometry', (
'fit',
'model',
'sky_to_tan_projection'))
54 meas = Measurement(job.metrics[name], value)
55 job.measurements.insert(meas)
59 """Subclass of TaskRunner for jointcalTask 61 jointcalTask.runDataRef() takes a number of arguments, one of which is a list of dataRefs 62 extracted from the command line (whereas most CmdLineTasks' runDataRef methods take 63 single dataRef, are are called repeatedly). This class transforms the processed 64 arguments generated by the ArgumentParser into the arguments expected by 65 Jointcal.runDataRef(). 67 See pipeBase.TaskRunner for more information. 73 Return a list of tuples per tract, each containing (dataRefs, kwargs). 75 Jointcal operates on lists of dataRefs simultaneously. 77 kwargs[
'profile_jointcal'] = parsedCmd.profile_jointcal
78 kwargs[
'butler'] = parsedCmd.butler
82 for ref
in parsedCmd.id.refList:
83 refListDict.setdefault(ref.dataId[
"tract"], []).append(ref)
85 result = [(refListDict[tract], kwargs)
for tract
in sorted(refListDict.keys())]
93 Arguments for Task.runDataRef() 98 if self.doReturnResults is False: 100 - ``exitStatus``: 0 if the task completed successfully, 1 otherwise. 102 if self.doReturnResults is True: 104 - ``result``: the result of calling jointcal.runDataRef() 105 - ``exitStatus``: 0 if the task completed successfully, 1 otherwise. 110 dataRefList, kwargs = args
111 butler = kwargs.pop(
'butler')
112 task = self.TaskClass(config=self.config, log=self.log, butler=butler)
115 result = task.runDataRef(dataRefList, **kwargs)
116 exitStatus = result.exitStatus
117 job_path = butler.get(
'verify_job_filename')
118 result.job.write(job_path[0])
119 except Exception
as e:
124 eName = type(e).__name__
125 tract = dataRefList[0].dataId[
'tract']
126 task.log.fatal(
"Failed processing tract %s, %s: %s", tract, eName, e)
128 if self.doReturnResults:
129 return pipeBase.Struct(result=result, exitStatus=exitStatus)
131 return pipeBase.Struct(exitStatus=exitStatus)
135 """Config for JointcalTask""" 137 doAstrometry = pexConfig.Field(
138 doc=
"Fit astrometry and write the fitted result.",
142 doPhotometry = pexConfig.Field(
143 doc=
"Fit photometry and write the fitted result.",
147 coaddName = pexConfig.Field(
148 doc=
"Type of coadd, typically deep or goodSeeing",
152 positionErrorPedestal = pexConfig.Field(
153 doc=
"Systematic term to apply to the measured position error (pixels)",
157 photometryErrorPedestal = pexConfig.Field(
158 doc=
"Systematic term to apply to the measured error on flux or magnitude as a " 159 "fraction of source flux or magnitude delta (e.g. 0.05 is 5% of flux or +50 millimag).",
164 matchCut = pexConfig.Field(
165 doc=
"Matching radius between fitted and reference stars (arcseconds)",
169 minMeasurements = pexConfig.Field(
170 doc=
"Minimum number of associated measured stars for a fitted star to be included in the fit",
174 minMeasuredStarsPerCcd = pexConfig.Field(
175 doc=
"Minimum number of measuredStars per ccdImage before printing warnings",
179 minRefStarsPerCcd = pexConfig.Field(
180 doc=
"Minimum number of measuredStars per ccdImage before printing warnings",
184 allowLineSearch = pexConfig.Field(
185 doc=
"Allow a line search during minimization, if it is reasonable for the model" 186 " (models with a significant non-linear component, e.g. constrainedPhotometry).",
190 astrometrySimpleOrder = pexConfig.Field(
191 doc=
"Polynomial order for fitting the simple astrometry model.",
195 astrometryChipOrder = pexConfig.Field(
196 doc=
"Order of the per-chip transform for the constrained astrometry model.",
200 astrometryVisitOrder = pexConfig.Field(
201 doc=
"Order of the per-visit transform for the constrained astrometry model.",
205 useInputWcs = pexConfig.Field(
206 doc=
"Use the input calexp WCSs to initialize a SimpleAstrometryModel.",
210 astrometryModel = pexConfig.ChoiceField(
211 doc=
"Type of model to fit to astrometry",
213 default=
"constrained",
214 allowed={
"simple":
"One polynomial per ccd",
215 "constrained":
"One polynomial per ccd, and one polynomial per visit"}
217 photometryModel = pexConfig.ChoiceField(
218 doc=
"Type of model to fit to photometry",
220 default=
"constrainedMagnitude",
221 allowed={
"simpleFlux":
"One constant zeropoint per ccd and visit, fitting in flux space.",
222 "constrainedFlux":
"Constrained zeropoint per ccd, and one polynomial per visit," 223 " fitting in flux space.",
224 "simpleMagnitude":
"One constant zeropoint per ccd and visit," 225 " fitting in magnitude space.",
226 "constrainedMagnitude":
"Constrained zeropoint per ccd, and one polynomial per visit," 227 " fitting in magnitude space.",
230 applyColorTerms = pexConfig.Field(
231 doc=
"Apply photometric color terms to reference stars?" 232 "Requires that colorterms be set to a ColortermLibrary",
236 colorterms = pexConfig.ConfigField(
237 doc=
"Library of photometric reference catalog name to color term dict.",
238 dtype=ColortermLibrary,
240 photometryVisitOrder = pexConfig.Field(
241 doc=
"Order of the per-visit polynomial transform for the constrained photometry model.",
245 photometryDoRankUpdate = pexConfig.Field(
246 doc=
"Do the rank update step during minimization. " 247 "Skipping this can help deal with models that are too non-linear.",
251 astrometryDoRankUpdate = pexConfig.Field(
252 doc=
"Do the rank update step during minimization (should not change the astrometry fit). " 253 "Skipping this can help deal with models that are too non-linear.",
257 outlierRejectSigma = pexConfig.Field(
258 doc=
"How many sigma to reject outliers at during minimization.",
262 maxPhotometrySteps = pexConfig.Field(
263 doc=
"Maximum number of minimize iterations to take when fitting photometry.",
267 maxAstrometrySteps = pexConfig.Field(
268 doc=
"Maximum number of minimize iterations to take when fitting photometry.",
272 astrometryRefObjLoader = pexConfig.ConfigurableField(
273 target=LoadIndexedReferenceObjectsTask,
274 doc=
"Reference object loader for astrometric fit",
276 photometryRefObjLoader = pexConfig.ConfigurableField(
277 target=LoadIndexedReferenceObjectsTask,
278 doc=
"Reference object loader for photometric fit",
280 sourceSelector = sourceSelectorRegistry.makeField(
281 doc=
"How to select sources for cross-matching",
284 astrometryReferenceSelector = pexConfig.ConfigurableField(
285 target=ReferenceSourceSelectorTask,
286 doc=
"How to down-select the loaded astrometry reference catalog.",
288 photometryReferenceSelector = pexConfig.ConfigurableField(
289 target=ReferenceSourceSelectorTask,
290 doc=
"How to down-select the loaded photometry reference catalog.",
292 writeInitMatrix = pexConfig.Field(
294 doc=
"Write the pre/post-initialization Hessian and gradient to text files, for debugging." 295 "The output files will be of the form 'astrometry_preinit-mat.txt', in the current directory." 296 "Note that these files are the dense versions of the matrix, and so may be very large.",
299 writeChi2ContributionFiles = pexConfig.Field(
301 doc=
"Write initial/final fit files containing the contributions to chi2.",
304 sourceFluxType = pexConfig.Field(
306 doc=
"Source flux field to use in source selection and to get fluxes from the catalog.",
313 msg =
"applyColorTerms=True requires the `colorterms` field be set to a ColortermLibrary." 314 raise pexConfig.FieldValidationError(JointcalConfig.colorterms, self, msg)
318 sourceSelector.setDefaults()
320 sourceSelector.badFlags.extend([
"slot_Shape_flag"])
326 """Jointly astrometrically and photometrically calibrate a group of images.""" 328 ConfigClass = JointcalConfig
329 RunnerClass = JointcalRunner
330 _DefaultName =
"jointcal" 332 def __init__(self, butler=None, profile_jointcal=False, **kwargs):
334 Instantiate a JointcalTask. 338 butler : `lsst.daf.persistence.Butler` 339 The butler is passed to the refObjLoader constructor in case it is 340 needed. Ignored if the refObjLoader argument provides a loader directly. 341 Used to initialize the astrometry and photometry refObjLoaders. 342 profile_jointcal : `bool` 343 Set to True to profile different stages of this jointcal run. 345 pipeBase.CmdLineTask.__init__(self, **kwargs)
347 self.makeSubtask(
"sourceSelector")
348 if self.config.doAstrometry:
349 self.makeSubtask(
'astrometryRefObjLoader', butler=butler)
350 self.makeSubtask(
"astrometryReferenceSelector")
353 if self.config.doPhotometry:
354 self.makeSubtask(
'photometryRefObjLoader', butler=butler)
355 self.makeSubtask(
"photometryReferenceSelector")
360 self.
job = Job.load_metrics_package(subset=
'jointcal')
364 def _getConfigName(self):
367 def _getMetadataName(self):
371 def _makeArgumentParser(cls):
372 """Create an argument parser""" 374 parser.add_argument(
"--profile_jointcal", default=
False, action=
"store_true",
375 help=
"Profile steps of jointcal separately.")
376 parser.add_id_argument(
"--id",
"calexp", help=
"data ID, e.g. --id visit=6789 ccd=0..9",
377 ContainerClass=PerTractCcdDataIdContainer)
380 def _build_ccdImage(self, dataRef, associations, jointcalControl):
382 Extract the necessary things from this dataRef to add a new ccdImage. 386 dataRef : `lsst.daf.persistence.ButlerDataRef` 387 DataRef to extract info from. 388 associations : `lsst.jointcal.Associations` 389 Object to add the info to, to construct a new CcdImage 390 jointcalControl : `jointcal.JointcalControl` 391 Control object for associations management 397 The TAN WCS of this image, read from the calexp 398 (`lsst.afw.geom.SkyWcs`). 400 A key to identify this dataRef by its visit and ccd ids 403 This calexp's filter (`str`). 405 if "visit" in dataRef.dataId.keys():
406 visit = dataRef.dataId[
"visit"]
408 visit = dataRef.getButler().queryMetadata(
"calexp", (
"visit"), dataRef.dataId)[0]
410 src = dataRef.get(
"src", flags=lsst.afw.table.SOURCE_IO_NO_FOOTPRINTS, immediate=
True)
412 visitInfo = dataRef.get(
'calexp_visitInfo')
413 detector = dataRef.get(
'calexp_detector')
414 ccdId = detector.getId()
415 calib = dataRef.get(
'calexp_calib')
416 tanWcs = dataRef.get(
'calexp_wcs')
417 bbox = dataRef.get(
'calexp_bbox')
418 filt = dataRef.get(
'calexp_filter')
419 filterName = filt.getName()
420 fluxMag0 = calib.getFluxMag0()
422 referenceFlux = 1e23 * 10**(48.6 / -2.5) * 1e9
423 photoCalib = afwImage.PhotoCalib(referenceFlux/fluxMag0[0],
424 referenceFlux*fluxMag0[1]/fluxMag0[0]**2, bbox)
426 goodSrc = self.sourceSelector.run(src)
428 if len(goodSrc.sourceCat) == 0:
429 self.log.warn(
"No sources selected in visit %s ccd %s", visit, ccdId)
431 self.log.info(
"%d sources selected in visit %d ccd %d", len(goodSrc.sourceCat), visit, ccdId)
432 associations.createCcdImage(goodSrc.sourceCat,
443 Result = collections.namedtuple(
'Result_from_build_CcdImage', (
'wcs',
'key',
'filter'))
444 Key = collections.namedtuple(
'Key', (
'visit',
'ccd'))
445 return Result(tanWcs, Key(visit, ccdId), filterName)
450 Jointly calibrate the astrometry and photometry across a set of images. 454 dataRefs : `list` of `lsst.daf.persistence.ButlerDataRef` 455 List of data references to the exposures to be fit. 456 profile_jointcal : `bool` 457 Profile the individual steps of jointcal. 461 result : `lsst.pipe.base.Struct` 462 Struct of metadata from the fit, containing: 465 The provided data references that were fit (with updated WCSs) 467 The original WCS from each dataRef 469 Dictionary of internally-computed metrics for testing/validation. 471 if len(dataRefs) == 0:
472 raise ValueError(
'Need a non-empty list of data references!')
476 sourceFluxField =
"slot_%sFlux" % (self.config.sourceFluxType,)
480 visit_ccd_to_dataRef = {}
483 load_cat_prof_file =
'jointcal_build_ccdImage.prof' if profile_jointcal
else '' 484 with pipeBase.cmdLineTask.profile(load_cat_prof_file):
487 camera = dataRefs[0].get(
'camera', immediate=
True)
491 oldWcsList.append(result.wcs)
492 visit_ccd_to_dataRef[result.key] = ref
493 filters.append(result.filter)
494 filters = collections.Counter(filters)
496 associations.computeCommonTangentPoint()
501 bbox = associations.getRaDecBBox()
502 bboxCenter = bbox.getCenter()
503 center = afwGeom.SpherePoint(bboxCenter[0], bboxCenter[1], afwGeom.degrees)
504 bboxMax = bbox.getMax()
505 corner = afwGeom.SpherePoint(bboxMax[0], bboxMax[1], afwGeom.degrees)
506 radius = center.separation(corner).asRadians()
511 raise RuntimeError(
"astrometry_net_data is not setup")
514 defaultFilter = filters.most_common(1)[0][0]
515 self.log.debug(
"Using %s band for reference flux", defaultFilter)
518 tract = dataRefs[0].dataId[
'tract']
520 if self.config.doAstrometry:
524 referenceSelector=self.astrometryReferenceSelector,
526 profile_jointcal=profile_jointcal,
532 if self.config.doPhotometry:
536 referenceSelector=self.photometryReferenceSelector,
538 profile_jointcal=profile_jointcal,
541 reject_bad_fluxes=
True)
546 return pipeBase.Struct(dataRefs=dataRefs,
547 oldWcsList=oldWcsList,
551 defaultFilter=defaultFilter,
552 exitStatus=exitStatus)
554 def _do_load_refcat_and_fit(self, associations, defaultFilter, center, radius,
555 name="", refObjLoader=None, referenceSelector=None,
556 filters=[], fit_function=None,
557 tract=None, profile_jointcal=False, match_cut=3.0,
558 reject_bad_fluxes=False):
559 """Load reference catalog, perform the fit, and return the result. 563 associations : `lsst.jointcal.Associations` 564 The star/reference star associations to fit. 565 defaultFilter : `str` 566 filter to load from reference catalog. 567 center : `lsst.afw.geom.SpherePoint` 568 ICRS center of field to load from reference catalog. 569 radius : `lsst.afw.geom.Angle` 570 On-sky radius to load from reference catalog. 572 Name of thing being fit: "Astrometry" or "Photometry". 573 refObjLoader : `lsst.meas.algorithms.LoadReferenceObjectsTask` 574 Reference object loader to load from for fit. 575 filters : `list` of `str`, optional 576 List of filters to load from the reference catalog. 577 fit_function : callable 578 Function to call to perform fit (takes associations object). 580 Name of tract currently being fit. 581 profile_jointcal : `bool`, optional 582 Separately profile the fitting step. 583 match_cut : `float`, optional 584 Radius in arcseconds to find cross-catalog matches to during 585 associations.associateCatalogs. 586 reject_bad_fluxes : `bool`, optional 587 Reject refCat sources with NaN/inf flux or NaN/0 fluxErr. 591 result : `Photometry` or `Astrometry` 592 Result of `fit_function()` 594 self.log.info(
"====== Now processing %s...", name)
597 associations.associateCatalogs(match_cut)
599 associations.fittedStarListSize())
601 applyColorterms =
False if name ==
"Astrometry" else self.config.applyColorTerms
602 if name ==
"Astrometry":
603 referenceSelector = self.config.astrometryReferenceSelector
604 elif name ==
"Photometry":
605 referenceSelector = self.config.photometryReferenceSelector
607 center, radius, defaultFilter,
608 applyColorterms=applyColorterms)
610 associations.collectRefStars(refCat, self.config.matchCut*afwGeom.arcseconds,
611 fluxField, reject_bad_fluxes)
613 associations.refStarListSize())
615 associations.prepareFittedStars(self.config.minMeasurements)
619 associations.nFittedStarsWithAssociatedRefStar())
621 associations.fittedStarListSize())
623 associations.nCcdImagesValidForFit())
625 load_cat_prof_file =
'jointcal_fit_%s.prof'%name
if profile_jointcal
else '' 626 dataName =
"{}_{}".format(tract, defaultFilter)
627 with pipeBase.cmdLineTask.profile(load_cat_prof_file):
628 result = fit_function(associations, dataName)
631 if self.config.writeChi2ContributionFiles:
632 baseName =
"{}_final_chi2-{}.csv".format(name, dataName)
633 result.fit.saveChi2Contributions(baseName)
637 def _load_reference_catalog(self, refObjLoader, referenceSelector, center, radius, filterName,
638 applyColorterms=False):
639 """Load the necessary reference catalog sources, convert fluxes to 640 correct units, and apply color term corrections if requested. 644 refObjLoader : `lsst.meas.algorithms.LoadReferenceObjectsTask` 645 The reference catalog loader to use to get the data. 646 referenceSelector : `lsst.meas.algorithms.ReferenceSourceSelectorTask` 647 Source selector to apply to loaded reference catalog. 648 center : `lsst.geom.SpherePoint` 649 The center around which to load sources. 650 radius : `lsst.geom.Angle` 651 The radius around ``center`` to load sources in. 653 The name of the camera filter to load fluxes for. 654 applyColorterms : `bool` 655 Apply colorterm corrections to the refcat for ``filterName``? 659 refCat : `lsst.afw.table.SimpleCatalog` 660 The loaded reference catalog. 662 The name of the reference catalog flux field appropriate for ``filterName``. 664 skyCircle = refObjLoader.loadSkyCircle(center,
665 afwGeom.Angle(radius, afwGeom.radians),
668 selected = referenceSelector.run(skyCircle.refCat)
670 if not selected.sourceCat.isContiguous():
671 refCat = selected.sourceCat.copy(deep=
True)
673 refCat = selected.sourceCat
677 refCatName = refObjLoader.ref_dataset_name
678 except AttributeError:
680 raise RuntimeError(
"Cannot perform colorterm corrections with a.net refcats.")
681 self.log.info(
"Applying color terms for filterName=%r reference catalog=%s",
682 filterName, refCatName)
683 colorterm = self.config.colorterms.getColorterm(
684 filterName=filterName, photoCatName=refCatName, doRaise=
True)
686 refMag, refMagErr = colorterm.getCorrectedMagnitudes(refCat, filterName)
687 refCat[skyCircle.fluxField] = u.Magnitude(refMag, u.ABmag).to_value(u.nJy)
689 refCat[skyCircle.fluxField+
'Err'] = fluxErrFromABMagErr(refMagErr, refMag) * 1e9
692 refCat[skyCircle.fluxField] *= 1e9
694 refCat[skyCircle.fluxField+
'Err'] *= 1e9
699 return refCat, skyCircle.fluxField
701 def _check_star_lists(self, associations, name):
703 if associations.nCcdImagesValidForFit() == 0:
704 raise RuntimeError(
'No images in the ccdImageList!')
705 if associations.fittedStarListSize() == 0:
706 raise RuntimeError(
'No stars in the {} fittedStarList!'.format(name))
707 if associations.refStarListSize() == 0:
708 raise RuntimeError(
'No stars in the {} reference star list!'.format(name))
710 def _logChi2AndValidate(self, associations, fit, model, chi2Label="Model"):
711 """Compute chi2, log it, validate the model, and return chi2.""" 712 chi2 = fit.computeChi2()
713 self.log.info(
"%s %s", chi2Label, chi2)
715 if not np.isfinite(chi2.chi2):
716 raise FloatingPointError(
'%s chi2 is invalid: %s', chi2Label, chi2)
717 if not model.validate(associations.getCcdImageList()):
718 raise ValueError(
"Model is not valid: check log messages for warnings.")
721 def _fit_photometry(self, associations, dataName=None):
723 Fit the photometric data. 727 associations : `lsst.jointcal.Associations` 728 The star/reference star associations to fit. 730 Name of the data being processed (e.g. "1234_HSC-Y"), for 731 identifying debugging files. 735 fit_result : `namedtuple` 736 fit : `lsst.jointcal.PhotometryFit` 737 The photometric fitter used to perform the fit. 738 model : `lsst.jointcal.PhotometryModel` 739 The photometric model that was fit. 741 self.log.info(
"=== Starting photometric fitting...")
744 if self.config.photometryModel ==
"constrainedFlux":
747 visitOrder=self.config.photometryVisitOrder,
748 errorPedestal=self.config.photometryErrorPedestal)
750 doLineSearch = self.config.allowLineSearch
751 elif self.config.photometryModel ==
"constrainedMagnitude":
754 visitOrder=self.config.photometryVisitOrder,
755 errorPedestal=self.config.photometryErrorPedestal)
757 doLineSearch = self.config.allowLineSearch
758 elif self.config.photometryModel ==
"simpleFlux":
760 errorPedestal=self.config.photometryErrorPedestal)
762 elif self.config.photometryModel ==
"simpleMagnitude":
764 errorPedestal=self.config.photometryErrorPedestal)
772 if self.config.writeChi2ContributionFiles:
773 baseName =
"photometry_initial_chi2-{}.csv".format(dataName)
774 fit.saveChi2Contributions(baseName)
778 dumpMatrixFile =
"photometry_preinit" if self.config.writeInitMatrix
else "" 779 if self.config.photometryModel.startswith(
"constrained"):
782 fit.minimize(
"ModelVisit", dumpMatrixFile=dumpMatrixFile)
786 fit.minimize(
"Model", doLineSearch=doLineSearch, dumpMatrixFile=dumpMatrixFile)
789 fit.minimize(
"Fluxes")
792 fit.minimize(
"Model Fluxes", doLineSearch=doLineSearch)
795 model.freezeErrorTransform()
796 self.log.debug(
"Photometry error scales are frozen.")
800 self.config.maxPhotometrySteps,
803 doRankUpdate=self.config.photometryDoRankUpdate,
804 doLineSearch=doLineSearch,
811 def _fit_astrometry(self, associations, dataName=None):
813 Fit the astrometric data. 817 associations : `lsst.jointcal.Associations` 818 The star/reference star associations to fit. 820 Name of the data being processed (e.g. "1234_HSC-Y"), for 821 identifying debugging files. 825 fit_result : `namedtuple` 826 fit : `lsst.jointcal.AstrometryFit` 827 The astrometric fitter used to perform the fit. 828 model : `lsst.jointcal.AstrometryModel` 829 The astrometric model that was fit. 830 sky_to_tan_projection : `lsst.jointcal.ProjectionHandler` 831 The model for the sky to tangent plane projection that was used in the fit. 834 self.log.info(
"=== Starting astrometric fitting...")
836 associations.deprojectFittedStars()
843 if self.config.astrometryModel ==
"constrained":
845 sky_to_tan_projection,
846 chipOrder=self.config.astrometryChipOrder,
847 visitOrder=self.config.astrometryVisitOrder)
848 elif self.config.astrometryModel ==
"simple":
850 sky_to_tan_projection,
851 self.config.useInputWcs,
853 order=self.config.astrometrySimpleOrder)
860 if self.config.writeChi2ContributionFiles:
861 baseName =
"astrometry_initial_chi2-{}.csv".format(dataName)
862 fit.saveChi2Contributions(baseName)
864 dumpMatrixFile =
"astrometry_preinit" if self.config.writeInitMatrix
else "" 867 if self.config.astrometryModel ==
"constrained":
868 fit.minimize(
"DistortionsVisit", dumpMatrixFile=dumpMatrixFile)
872 fit.minimize(
"Distortions", dumpMatrixFile=dumpMatrixFile)
875 fit.minimize(
"Positions")
878 fit.minimize(
"Distortions Positions")
883 self.config.maxAstrometrySteps,
885 "Distortions Positions",
886 doRankUpdate=self.config.astrometryDoRankUpdate,
892 return Astrometry(fit, model, sky_to_tan_projection)
894 def _check_stars(self, associations):
895 """Count measured and reference stars per ccd and warn/log them.""" 896 for ccdImage
in associations.getCcdImageList():
897 nMeasuredStars, nRefStars = ccdImage.countStars()
898 self.log.debug(
"ccdImage %s has %s measured and %s reference stars",
899 ccdImage.getName(), nMeasuredStars, nRefStars)
900 if nMeasuredStars < self.config.minMeasuredStarsPerCcd:
901 self.log.warn(
"ccdImage %s has only %s measuredStars (desired %s)",
902 ccdImage.getName(), nMeasuredStars, self.config.minMeasuredStarsPerCcd)
903 if nRefStars < self.config.minRefStarsPerCcd:
904 self.log.warn(
"ccdImage %s has only %s RefStars (desired %s)",
905 ccdImage.getName(), nRefStars, self.config.minRefStarsPerCcd)
907 def _iterate_fit(self, associations, fitter, max_steps, name, whatToFit,
911 """Run fitter.minimize up to max_steps times, returning the final chi2. 915 associations : `lsst.jointcal.Associations` 916 The star/reference star associations to fit. 917 fitter : `lsst.jointcal.FitterBase` 918 The fitter to use for minimization. 920 Maximum number of steps to run outlier rejection before declaring 922 name : {'photometry' or 'astrometry'} 923 What type of data are we fitting (for logs and debugging files). 925 Passed to ``fitter.minimize()`` to define the parameters to fit. 926 dataName : `str`, optional 927 Descriptive name for this dataset (e.g. tract and filter), 929 doRankUpdate : `bool`, optional 930 Do an Eigen rank update during minimization, or recompute the full 932 doLineSearch : `bool`, optional 933 Do a line search for the optimum step during minimization? 937 chi2: `lsst.jointcal.Chi2Statistic` 938 The final chi2 after the fit converges, or is forced to end. 943 Raised if the fitter fails with a non-finite value. 945 Raised if the fitter fails for some other reason; 946 log messages will provide further details. 948 dumpMatrixFile =
"%s_postinit" % name
if self.config.writeInitMatrix
else "" 949 for i
in range(max_steps):
950 result = fitter.minimize(whatToFit,
951 self.config.outlierRejectSigma,
952 doRankUpdate=doRankUpdate,
953 doLineSearch=doLineSearch,
954 dumpMatrixFile=dumpMatrixFile)
958 if result == MinimizeResult.Converged:
960 self.log.debug(
"fit has converged - no more outliers - redo minimization " 961 "one more time in case we have lost accuracy in rank update.")
963 result = fitter.minimize(whatToFit, self.config.outlierRejectSigma)
967 if chi2.chi2/chi2.ndof >= 4.0:
968 self.log.error(
"Potentially bad fit: High chi-squared/ndof.")
971 elif result == MinimizeResult.Chi2Increased:
972 self.log.warn(
"still some outliers but chi2 increases - retry")
973 elif result == MinimizeResult.NonFinite:
974 filename =
"{}_failure-nonfinite_chi2-{}.csv".format(name, dataName)
976 fitter.saveChi2Contributions(filename)
977 msg =
"Nonfinite value in chi2 minimization, cannot complete fit. Dumped star tables to: {}" 978 raise FloatingPointError(msg.format(filename))
979 elif result == MinimizeResult.Failed:
980 raise RuntimeError(
"Chi2 minimization failure, cannot complete fit.")
982 raise RuntimeError(
"Unxepected return code from minimize().")
984 self.log.error(
"%s failed to converge after %d steps"%(name, max_steps))
988 def _write_astrometry_results(self, associations, model, visit_ccd_to_dataRef):
990 Write the fitted astrometric results to a new 'jointcal_wcs' dataRef. 994 associations : `lsst.jointcal.Associations` 995 The star/reference star associations to fit. 996 model : `lsst.jointcal.AstrometryModel` 997 The astrometric model that was fit. 998 visit_ccd_to_dataRef : `dict` of Key: `lsst.daf.persistence.ButlerDataRef` 999 Dict of ccdImage identifiers to dataRefs that were fit. 1002 ccdImageList = associations.getCcdImageList()
1003 for ccdImage
in ccdImageList:
1005 ccd = ccdImage.ccdId
1006 visit = ccdImage.visit
1007 dataRef = visit_ccd_to_dataRef[(visit, ccd)]
1008 self.log.info(
"Updating WCS for visit: %d, ccd: %d", visit, ccd)
1009 skyWcs = model.makeSkyWcs(ccdImage)
1011 dataRef.put(skyWcs,
'jointcal_wcs')
1012 except pexExceptions.Exception
as e:
1013 self.log.fatal(
'Failed to write updated Wcs: %s', str(e))
1016 def _write_photometry_results(self, associations, model, visit_ccd_to_dataRef):
1018 Write the fitted photometric results to a new 'jointcal_photoCalib' dataRef. 1022 associations : `lsst.jointcal.Associations` 1023 The star/reference star associations to fit. 1024 model : `lsst.jointcal.PhotometryModel` 1025 The photoometric model that was fit. 1026 visit_ccd_to_dataRef : `dict` of Key: `lsst.daf.persistence.ButlerDataRef` 1027 Dict of ccdImage identifiers to dataRefs that were fit. 1030 ccdImageList = associations.getCcdImageList()
1031 for ccdImage
in ccdImageList:
1033 ccd = ccdImage.ccdId
1034 visit = ccdImage.visit
1035 dataRef = visit_ccd_to_dataRef[(visit, ccd)]
1036 self.log.info(
"Updating PhotoCalib for visit: %d, ccd: %d", visit, ccd)
1037 photoCalib = model.toPhotoCalib(ccdImage)
1039 dataRef.put(photoCalib,
'jointcal_photoCalib')
1040 except pexExceptions.Exception
as e:
1041 self.log.fatal(
'Failed to write updated PhotoCalib: %s', str(e))
def runDataRef(self, dataRefs, profile_jointcal=False)
def _load_reference_catalog(self, refObjLoader, referenceSelector, center, radius, filterName, applyColorterms=False)
def _build_ccdImage(self, dataRef, associations, jointcalControl)
def _fit_photometry(self, associations, dataName=None)
def getTargetList(parsedCmd, kwargs)
def _fit_astrometry(self, associations, dataName=None)
def _check_star_lists(self, associations, name)
The class that implements the relations between MeasuredStar and FittedStar.
A projection handler in which all CCDs from the same visit have the same tangent point.
std::string getPackageDir(std::string const &packageName)
def _logChi2AndValidate(self, associations, fit, model, chi2Label="Model")
this is the model used to fit independent CCDs, meaning that there is no instrument model...
def _iterate_fit(self, associations, fitter, max_steps, name, whatToFit, dataName="", doRankUpdate=True, doLineSearch=False)
def _write_photometry_results(self, associations, model, visit_ccd_to_dataRef)
def _check_stars(self, associations)
Class that handles the photometric least squares problem.
Class that handles the astrometric least squares problem.
def add_measurement(job, name, value)
def _do_load_refcat_and_fit(self, associations, defaultFilter, center, radius, name="", refObjLoader=None, referenceSelector=None, filters=[], fit_function=None, tract=None, profile_jointcal=False, match_cut=3.0, reject_bad_fluxes=False)
This is the model used to fit mappings as the combination of a transformation depending on the chip n...
def _write_astrometry_results(self, associations, model, visit_ccd_to_dataRef)
def __init__(self, butler=None, profile_jointcal=False, kwargs)