24 import astropy.units
as u
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 writeInitMatrix = pexConfig.Field(
286 doc=
"Write the pre/post-initialization Hessian and gradient to text files, for debugging." 287 "The output files will be of the form 'astrometry_preinit-mat.txt', in the current directory." 288 "Note that these files are the dense versions of the matrix, and so may be very large.",
291 writeChi2ContributionFiles = pexConfig.Field(
293 doc=
"Write initial/final fit files containing the contributions to chi2.",
296 sourceFluxType = pexConfig.Field(
298 doc=
"Source flux field to use in source selection and to get fluxes from the catalog.",
305 msg =
"applyColorTerms=True requires the `colorterms` field be set to a ColortermLibrary." 306 raise pexConfig.FieldValidationError(JointcalConfig.colorterms, self, msg)
310 sourceSelector.setDefaults()
312 sourceSelector.badFlags.extend([
"slot_Shape_flag"])
318 """Jointly astrometrically and photometrically calibrate a group of images.""" 320 ConfigClass = JointcalConfig
321 RunnerClass = JointcalRunner
322 _DefaultName =
"jointcal" 324 def __init__(self, butler=None, profile_jointcal=False, **kwargs):
326 Instantiate a JointcalTask. 330 butler : lsst.daf.persistence.Butler 331 The butler is passed to the refObjLoader constructor in case it is 332 needed. Ignored if the refObjLoader argument provides a loader directly. 333 Used to initialize the astrometry and photometry refObjLoaders. 334 profile_jointcal : bool 335 set to True to profile different stages of this jointcal run. 337 pipeBase.CmdLineTask.__init__(self, **kwargs)
339 self.makeSubtask(
"sourceSelector")
340 if self.config.doAstrometry:
341 self.makeSubtask(
'astrometryRefObjLoader', butler=butler)
344 if self.config.doPhotometry:
345 self.makeSubtask(
'photometryRefObjLoader', butler=butler)
350 self.
job = Job.load_metrics_package(subset=
'jointcal')
354 def _getConfigName(self):
357 def _getMetadataName(self):
361 def _makeArgumentParser(cls):
362 """Create an argument parser""" 364 parser.add_argument(
"--profile_jointcal", default=
False, action=
"store_true",
365 help=
"Profile steps of jointcal separately.")
366 parser.add_id_argument(
"--id",
"calexp", help=
"data ID, e.g. --id visit=6789 ccd=0..9",
367 ContainerClass=PerTractCcdDataIdContainer)
370 def _build_ccdImage(self, dataRef, associations, jointcalControl):
372 Extract the necessary things from this dataRef to add a new ccdImage. 376 dataRef : lsst.daf.persistence.ButlerDataRef 377 dataRef to extract info from. 378 associations : lsst.jointcal.Associations 379 object to add the info to, to construct a new CcdImage 380 jointcalControl : jointcal.JointcalControl 381 control object for associations management 386 wcs : lsst.afw.geom.SkyWcs 387 the TAN WCS of this image, read from the calexp 389 a key to identify this dataRef by its visit and ccd ids 393 if "visit" in dataRef.dataId.keys():
394 visit = dataRef.dataId[
"visit"]
396 visit = dataRef.getButler().queryMetadata(
"calexp", (
"visit"), dataRef.dataId)[0]
398 src = dataRef.get(
"src", flags=lsst.afw.table.SOURCE_IO_NO_FOOTPRINTS, immediate=
True)
400 visitInfo = dataRef.get(
'calexp_visitInfo')
401 detector = dataRef.get(
'calexp_detector')
402 ccdId = detector.getId()
403 calib = dataRef.get(
'calexp_calib')
404 tanWcs = dataRef.get(
'calexp_wcs')
405 bbox = dataRef.get(
'calexp_bbox')
406 filt = dataRef.get(
'calexp_filter')
407 filterName = filt.getName()
408 fluxMag0 = calib.getFluxMag0()
410 referenceFlux = 1e23 * 10**(48.6 / -2.5) * 1e9
412 referenceFlux*fluxMag0[1]/fluxMag0[0]**2, bbox)
414 goodSrc = self.sourceSelector.
run(src)
416 if len(goodSrc.sourceCat) == 0:
417 self.log.
warn(
"No sources selected in visit %s ccd %s", visit, ccdId)
419 self.log.
info(
"%d sources selected in visit %d ccd %d", len(goodSrc.sourceCat), visit, ccdId)
420 associations.createCcdImage(goodSrc.sourceCat,
431 Result = collections.namedtuple(
'Result_from_build_CcdImage', (
'wcs',
'key',
'filter'))
432 Key = collections.namedtuple(
'Key', (
'visit',
'ccd'))
433 return Result(tanWcs, Key(visit, ccdId), filterName)
438 Jointly calibrate the astrometry and photometry across a set of images. 442 dataRefs : list of lsst.daf.persistence.ButlerDataRef 443 List of data references to the exposures to be fit. 444 profile_jointcal : bool 445 Profile the individual steps of jointcal. 451 * dataRefs: the provided data references that were fit (with updated WCSs) 452 * oldWcsList: the original WCS from each dataRef 453 * metrics: dictionary of internally-computed metrics for testing/validation. 455 if len(dataRefs) == 0:
456 raise ValueError(
'Need a non-empty list of data references!')
460 sourceFluxField =
"slot_%sFlux" % (self.config.sourceFluxType,)
464 visit_ccd_to_dataRef = {}
467 load_cat_prof_file =
'jointcal_build_ccdImage.prof' if profile_jointcal
else '' 468 with pipeBase.cmdLineTask.profile(load_cat_prof_file):
471 camera = dataRefs[0].get(
'camera', immediate=
True)
475 oldWcsList.append(result.wcs)
476 visit_ccd_to_dataRef[result.key] = ref
477 filters.append(result.filter)
478 filters = collections.Counter(filters)
480 associations.computeCommonTangentPoint()
485 bbox = associations.getRaDecBBox()
486 bboxCenter = bbox.getCenter()
488 bboxMax = bbox.getMax()
490 radius = center.separation(corner).asRadians()
495 raise RuntimeError(
"astrometry_net_data is not setup")
498 defaultFilter = filters.most_common(1)[0][0]
499 self.log.
debug(
"Using %s band for reference flux", defaultFilter)
502 tract = dataRefs[0].dataId[
'tract']
504 if self.config.doAstrometry:
509 profile_jointcal=profile_jointcal,
515 if self.config.doPhotometry:
520 profile_jointcal=profile_jointcal,
523 reject_bad_fluxes=
True)
528 return pipeBase.Struct(dataRefs=dataRefs,
529 oldWcsList=oldWcsList,
533 defaultFilter=defaultFilter,
534 exitStatus=exitStatus)
536 def _do_load_refcat_and_fit(self, associations, defaultFilter, center, radius,
537 name="", refObjLoader=None, filters=[], fit_function=None,
538 tract=None, profile_jointcal=False, match_cut=3.0,
539 reject_bad_fluxes=False):
540 """Load reference catalog, perform the fit, and return the result. 544 associations : lsst.jointcal.Associations 545 The star/reference star associations to fit. 547 filter to load from reference catalog. 548 center : lsst.afw.geom.SpherePoint 549 ICRS center of field to load from reference catalog. 550 radius : lsst.afw.geom.Angle 551 On-sky radius to load from reference catalog. 553 Name of thing being fit: "Astrometry" or "Photometry". 554 refObjLoader : lsst.meas.algorithms.LoadReferenceObjectsTask 555 Reference object loader to load from for fit. 556 filters : list of str, optional 557 List of filters to load from the reference catalog. 558 fit_function : function 559 function to call to perform fit (takes associations object). 561 Name of tract currently being fit. 562 profile_jointcal : bool, optional 563 Separately profile the fitting step. 564 match_cut : float, optional 565 Radius in arcseconds to find cross-catalog matches to during 566 associations.associateCatalogs. 567 reject_bad_fluxes : bool, optional 568 Reject refCat sources with NaN/inf flux or NaN/0 fluxErr. 572 Result of `fit_function()` 574 self.log.
info(
"====== Now processing %s...", name)
577 associations.associateCatalogs(match_cut)
579 associations.fittedStarListSize())
581 applyColorterms =
False if name ==
"Astrometry" else self.config.applyColorTerms
583 applyColorterms=applyColorterms)
585 associations.collectRefStars(refCat, self.config.matchCut*afwGeom.arcseconds,
586 fluxField, reject_bad_fluxes)
588 associations.refStarListSize())
590 associations.prepareFittedStars(self.config.minMeasurements)
594 associations.nFittedStarsWithAssociatedRefStar())
596 associations.fittedStarListSize())
598 associations.nCcdImagesValidForFit())
600 load_cat_prof_file =
'jointcal_fit_%s.prof'%name
if profile_jointcal
else '' 601 dataName =
"{}_{}".
format(tract, defaultFilter)
602 with pipeBase.cmdLineTask.profile(load_cat_prof_file):
603 result = fit_function(associations, dataName)
606 if self.config.writeChi2ContributionFiles:
607 baseName =
"{}_final_chi2-{}.csv".
format(name, dataName)
608 result.fit.saveChi2Contributions(baseName)
612 def _load_reference_catalog(self, refObjLoader, center, radius, filterName,
613 applyColorterms=False):
614 """Load the necessary reference catalog sources, convert fluxes to 615 correct units, and apply color term corrections if requested. 619 refObjLoader : `lsst.meas.algorithms.LoadReferenceObjectsTask` 620 The reference catalog loader to use to get the data. 621 center : `lsst.geom.SpherePoint` 622 The center around which to load sources. 623 radius : `lsst.geom.Angle` 624 The radius around ``center`` to load sources in. 626 The name of the camera filter to load fluxes for. 627 applyColorterms : `bool` 628 Apply colorterm corrections to the refcat for ``filterName``? 632 refCat : `lsst.afw.table.SimpleCatalog` 633 The loaded reference catalog. 635 The name of the reference catalog flux field appropriate for ``filterName``. 637 skyCircle = refObjLoader.loadSkyCircle(center,
642 if not skyCircle.refCat.isContiguous():
643 refCat = skyCircle.refCat.copy(deep=
True)
645 refCat = skyCircle.refCat
649 refCatName = refObjLoader.ref_dataset_name
650 except AttributeError:
652 raise RuntimeError(
"Cannot perform colorterm corrections with a.net refcats.")
653 self.log.
info(
"Applying color terms for filterName=%r reference catalog=%s",
654 filterName, refCatName)
655 colorterm = self.config.colorterms.getColorterm(
656 filterName=filterName, photoCatName=refCatName, doRaise=
True)
658 refMag, refMagErr = colorterm.getCorrectedMagnitudes(refCat, filterName)
659 refCat[skyCircle.fluxField] = u.Magnitude(refMag, u.ABmag).to_value(u.nJy)
664 refCat[skyCircle.fluxField] *= 1e9
666 refCat[skyCircle.fluxField+
'Err'] *= 1e9
671 return refCat, skyCircle.fluxField
673 def _check_star_lists(self, associations, name):
675 if associations.nCcdImagesValidForFit() == 0:
676 raise RuntimeError(
'No images in the ccdImageList!')
677 if associations.fittedStarListSize() == 0:
678 raise RuntimeError(
'No stars in the {} fittedStarList!'.
format(name))
679 if associations.refStarListSize() == 0:
680 raise RuntimeError(
'No stars in the {} reference star list!'.
format(name))
682 def _logChi2AndValidate(self, associations, fit, model, chi2Label="Model"):
683 """Compute chi2, log it, validate the model, and return chi2.""" 684 chi2 = fit.computeChi2()
685 self.log.
info(
"%s %s", chi2Label, chi2)
687 if not np.isfinite(chi2.chi2):
688 raise FloatingPointError(
'%s chi2 is invalid: %s', chi2Label, chi2)
689 if not model.validate(associations.getCcdImageList()):
690 raise ValueError(
"Model is not valid: check log messages for warnings.")
693 def _fit_photometry(self, associations, dataName=None):
695 Fit the photometric data. 699 associations : lsst.jointcal.Associations 700 The star/reference star associations to fit. 702 Name of the data being processed (e.g. "1234_HSC-Y"), for 703 identifying debugging files. 708 fit : lsst.jointcal.PhotometryFit 709 The photometric fitter used to perform the fit. 710 model : lsst.jointcal.PhotometryModel 711 The photometric model that was fit. 713 self.log.
info(
"=== Starting photometric fitting...")
716 if self.config.photometryModel ==
"constrainedFlux":
719 visitOrder=self.config.photometryVisitOrder,
720 errorPedestal=self.config.photometryErrorPedestal)
722 doLineSearch = self.config.allowLineSearch
723 elif self.config.photometryModel ==
"constrainedMagnitude":
726 visitOrder=self.config.photometryVisitOrder,
727 errorPedestal=self.config.photometryErrorPedestal)
729 doLineSearch = self.config.allowLineSearch
730 elif self.config.photometryModel ==
"simpleFlux":
732 errorPedestal=self.config.photometryErrorPedestal)
734 elif self.config.photometryModel ==
"simpleMagnitude":
736 errorPedestal=self.config.photometryErrorPedestal)
744 if self.config.writeChi2ContributionFiles:
745 baseName =
"photometry_initial_chi2-{}.csv".
format(dataName)
746 fit.saveChi2Contributions(baseName)
750 dumpMatrixFile =
"photometry_preinit" if self.config.writeInitMatrix
else "" 751 if self.config.photometryModel.startswith(
"constrained"):
754 fit.minimize(
"ModelVisit", dumpMatrixFile=dumpMatrixFile)
758 fit.minimize(
"Model", doLineSearch=doLineSearch, dumpMatrixFile=dumpMatrixFile)
761 fit.minimize(
"Fluxes")
764 fit.minimize(
"Model Fluxes", doLineSearch=doLineSearch)
767 model.freezeErrorTransform()
768 self.log.
debug(
"Photometry error scales are frozen.")
772 self.config.maxPhotometrySteps,
775 doRankUpdate=self.config.photometryDoRankUpdate,
776 doLineSearch=doLineSearch,
783 def _fit_astrometry(self, associations, dataName=None):
785 Fit the astrometric data. 789 associations : lsst.jointcal.Associations 790 The star/reference star associations to fit. 792 Name of the data being processed (e.g. "1234_HSC-Y"), for 793 identifying debugging files. 798 fit : lsst.jointcal.AstrometryFit 799 The astrometric fitter used to perform the fit. 800 model : lsst.jointcal.AstrometryModel 801 The astrometric model that was fit. 802 sky_to_tan_projection : lsst.jointcal.ProjectionHandler 803 The model for the sky to tangent plane projection that was used in the fit. 806 self.log.
info(
"=== Starting astrometric fitting...")
808 associations.deprojectFittedStars()
815 if self.config.astrometryModel ==
"constrained":
817 sky_to_tan_projection,
818 chipOrder=self.config.astrometryChipOrder,
819 visitOrder=self.config.astrometryVisitOrder)
820 elif self.config.astrometryModel ==
"simple":
822 sky_to_tan_projection,
823 self.config.useInputWcs,
825 order=self.config.astrometrySimpleOrder)
832 if self.config.writeChi2ContributionFiles:
833 baseName =
"astrometry_initial_chi2-{}.csv".
format(dataName)
834 fit.saveChi2Contributions(baseName)
836 dumpMatrixFile =
"astrometry_preinit" if self.config.writeInitMatrix
else "" 839 if self.config.astrometryModel ==
"constrained":
840 fit.minimize(
"DistortionsVisit", dumpMatrixFile=dumpMatrixFile)
844 fit.minimize(
"Distortions", dumpMatrixFile=dumpMatrixFile)
847 fit.minimize(
"Positions")
850 fit.minimize(
"Distortions Positions")
855 self.config.maxAstrometrySteps,
857 "Distortions Positions",
858 doRankUpdate=self.config.astrometryDoRankUpdate,
864 return Astrometry(fit, model, sky_to_tan_projection)
866 def _check_stars(self, associations):
867 """Count measured and reference stars per ccd and warn/log them.""" 868 for ccdImage
in associations.getCcdImageList():
869 nMeasuredStars, nRefStars = ccdImage.countStars()
870 self.log.
debug(
"ccdImage %s has %s measured and %s reference stars",
871 ccdImage.getName(), nMeasuredStars, nRefStars)
872 if nMeasuredStars < self.config.minMeasuredStarsPerCcd:
873 self.log.
warn(
"ccdImage %s has only %s measuredStars (desired %s)",
874 ccdImage.getName(), nMeasuredStars, self.config.minMeasuredStarsPerCcd)
875 if nRefStars < self.config.minRefStarsPerCcd:
876 self.log.
warn(
"ccdImage %s has only %s RefStars (desired %s)",
877 ccdImage.getName(), nRefStars, self.config.minRefStarsPerCcd)
879 def _iterate_fit(self, associations, fitter, max_steps, name, whatToFit,
883 """Run fitter.minimize up to max_steps times, returning the final chi2. 887 associations : `lsst.jointcal.Associations` 888 The star/reference star associations to fit. 889 fitter : `lsst.jointcal.FitterBase` 890 The fitter to use for minimization. 892 Maximum number of steps to run outlier rejection before declaring 894 name : {'photometry' or 'astrometry'} 895 What type of data are we fitting (for logs and debugging files). 897 Passed to ``fitter.minimize()`` to define the parameters to fit. 898 dataName : str, optional 899 Descriptive name for this dataset (e.g. tract and filter), 901 doRankUpdate : bool, optional 902 Do an Eigen rank update during minimization, or recompute the full 904 doLineSearch : bool, optional 905 Do a line search for the optimum step during minimization? 909 chi2: `lsst.jointcal.Chi2Statistic` 910 The final chi2 after the fit converges, or is forced to end. 915 Raised if the fitter fails with a non-finite value. 917 Raised if the fitter fails for some other reason; 918 log messages will provide further details. 920 dumpMatrixFile =
"%s_postinit" % name
if self.config.writeInitMatrix
else "" 921 for i
in range(max_steps):
922 result = fitter.minimize(whatToFit,
923 self.config.outlierRejectSigma,
924 doRankUpdate=doRankUpdate,
925 doLineSearch=doLineSearch,
926 dumpMatrixFile=dumpMatrixFile)
930 if result == MinimizeResult.Converged:
932 self.log.
debug(
"fit has converged - no more outliers - redo minimization " 933 "one more time in case we have lost accuracy in rank update.")
935 result = fitter.minimize(whatToFit, self.config.outlierRejectSigma)
939 if chi2.chi2/chi2.ndof >= 4.0:
940 self.log.
error(
"Potentially bad fit: High chi-squared/ndof.")
943 elif result == MinimizeResult.Chi2Increased:
944 self.log.
warn(
"still some outliers but chi2 increases - retry")
945 elif result == MinimizeResult.NonFinite:
946 filename =
"{}_failure-nonfinite_chi2-{}.csv".
format(name, dataName)
948 fitter.saveChi2Contributions(filename)
949 msg =
"Nonfinite value in chi2 minimization, cannot complete fit. Dumped star tables to: {}" 950 raise FloatingPointError(msg.format(filename))
951 elif result == MinimizeResult.Failed:
952 raise RuntimeError(
"Chi2 minimization failure, cannot complete fit.")
954 raise RuntimeError(
"Unxepected return code from minimize().")
956 self.log.
error(
"%s failed to converge after %d steps"%(name, max_steps))
960 def _write_astrometry_results(self, associations, model, visit_ccd_to_dataRef):
962 Write the fitted astrometric results to a new 'jointcal_wcs' dataRef. 966 associations : lsst.jointcal.Associations 967 The star/reference star associations to fit. 968 model : lsst.jointcal.AstrometryModel 969 The astrometric model that was fit. 970 visit_ccd_to_dataRef : dict of Key: lsst.daf.persistence.ButlerDataRef 971 dict of ccdImage identifiers to dataRefs that were fit 974 ccdImageList = associations.getCcdImageList()
975 for ccdImage
in ccdImageList:
978 visit = ccdImage.visit
979 dataRef = visit_ccd_to_dataRef[(visit, ccd)]
980 self.log.
info(
"Updating WCS for visit: %d, ccd: %d", visit, ccd)
981 skyWcs = model.makeSkyWcs(ccdImage)
983 dataRef.put(skyWcs,
'jointcal_wcs')
985 self.log.
fatal(
'Failed to write updated Wcs: %s',
str(e))
988 def _write_photometry_results(self, associations, model, visit_ccd_to_dataRef):
990 Write the fitted photometric results to a new 'jointcal_photoCalib' dataRef. 994 associations : lsst.jointcal.Associations 995 The star/reference star associations to fit. 996 model : lsst.jointcal.PhotometryModel 997 The photoometric 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 PhotoCalib for visit: %d, ccd: %d", visit, ccd)
1009 photoCalib = model.toPhotoCalib(ccdImage)
1011 dataRef.put(photoCalib,
'jointcal_photoCalib')
1013 self.log.
fatal(
'Failed to write updated PhotoCalib: %s',
str(e))
def runDataRef(self, dataRefs, profile_jointcal=False)
double fluxErrFromABMagErr(double magErr, double mag) noexcept
Compute flux error in Janskys from AB magnitude error and AB magnitude.
def _build_ccdImage(self, dataRef, associations, jointcalControl)
def _fit_photometry(self, associations, dataName=None)
The photometric calibration of an exposure.
def getTargetList(parsedCmd, kwargs)
def _fit_astrometry(self, associations, dataName=None)
Provides consistent interface for LSST exceptions.
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations...
def _check_star_lists(self, associations, name)
def _load_reference_catalog(self, refObjLoader, center, radius, filterName, applyColorterms=False)
The class that implements the relations between MeasuredStar and FittedStar.
A class representing an angle.
A projection handler in which all CCDs from the same visit have the same tangent point.
std::string getPackageDir(std::string const &packageName)
return the root directory of a setup package
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 format(config, name=None, writeSourceLine=True, prefix="", verbose=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.
Point in an unspecified spherical coordinate system.
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, 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)