29import astropy.units
as u
40from lsst.verify
import Job, Measurement
43 LoadIndexedReferenceObjectsConfig)
49__all__ = [
"JointcalConfig",
"JointcalTask"]
51Photometry = collections.namedtuple(
'Photometry', (
'fit',
'model'))
52Astrometry = collections.namedtuple(
'Astrometry', (
'fit',
'model',
'sky_to_tan_projection'))
57 meas = Measurement(job.metrics[name], value)
58 job.measurements.insert(meas)
61def lookupStaticCalibrations(datasetType, registry, quantumDataId, collections):
62 """Lookup function that asserts/hopes that a static calibration dataset
63 exists in a particular collection, since this task can
't provide a single
64 date/time to use to search for one properly.
66 This
is mostly useful
for the ``camera`` dataset,
in cases where the task
's
67 quantum dimensions do *not* include something temporal, like ``exposure``
72 datasetType : `lsst.daf.butler.DatasetType`
73 Type of dataset being searched
for.
74 registry : `lsst.daf.butler.Registry`
75 Data repository registry to search.
76 quantumDataId : `lsst.daf.butler.DataCoordinate`
77 Data ID of the quantum this camera should match.
78 collections : `Iterable` [ `str` ]
79 Collections that should be searched - but this lookup function works
80 by ignoring this
in favor of a more-
or-less hard-coded value.
84 refs : `Iterator` [ `lsst.daf.butler.DatasetRef` ]
85 Iterator over dataset references; should have only one element.
89 This implementation duplicates one
in fgcmcal,
and is at least quite
90 similar to another
in cp_pipe. This duplicate has the most documentation.
91 Fixing this
is DM-29661.
93 instrument = Instrument.fromName(quantumDataId["instrument"], registry)
94 unboundedCollection = instrument.makeUnboundedCalibrationRunName()
95 return registry.queryDatasets(datasetType,
97 collections=[unboundedCollection],
102 """Lookup function that finds all refcats for all visits that overlap a
103 tract, rather than just the refcats that directly overlap the tract.
107 datasetType : `lsst.daf.butler.DatasetType`
108 Type of dataset being searched for.
109 registry : `lsst.daf.butler.Registry`
110 Data repository registry to search.
111 quantumDataId : `lsst.daf.butler.DataCoordinate`
112 Data ID of the quantum; expected to be something we can use
as a
113 constraint to query
for overlapping visits.
114 collections : `Iterable` [ `str` ]
115 Collections to search.
119 refs : `Iterator` [ `lsst.daf.butler.DatasetRef` ]
120 Iterator over refcat references.
129 for visit_data_id
in set(registry.queryDataIds(
"visit", dataId=quantumDataId).expanded()):
131 registry.queryDatasets(
133 collections=collections,
134 dataId=visit_data_id,
142 dimensions=(
"skymap",
"tract",
"instrument",
"physical_filter")):
143 """Middleware input/output connections for jointcal data."""
144 inputCamera = pipeBase.connectionTypes.PrerequisiteInput(
145 doc=
"The camera instrument that took these observations.",
147 storageClass=
"Camera",
148 dimensions=(
"instrument",),
150 lookupFunction=lookupStaticCalibrations,
152 inputSourceTableVisit = pipeBase.connectionTypes.Input(
153 doc=
"Source table in parquet format, per visit",
154 name=
"sourceTable_visit",
155 storageClass=
"DataFrame",
156 dimensions=(
"instrument",
"visit"),
160 inputVisitSummary = pipeBase.connectionTypes.Input(
161 doc=(
"Per-visit consolidated exposure metadata built from calexps. "
162 "These catalogs use detector id for the id and must be sorted for "
163 "fast lookups of a detector."),
165 storageClass=
"ExposureCatalog",
166 dimensions=(
"instrument",
"visit"),
170 astrometryRefCat = pipeBase.connectionTypes.PrerequisiteInput(
171 doc=
"The astrometry reference catalog to match to loaded input catalog sources.",
172 name=
"gaia_dr2_20200414",
173 storageClass=
"SimpleCatalog",
174 dimensions=(
"skypix",),
177 lookupFunction=lookupVisitRefCats,
179 photometryRefCat = pipeBase.connectionTypes.PrerequisiteInput(
180 doc=
"The photometry reference catalog to match to loaded input catalog sources.",
181 name=
"ps1_pv3_3pi_20170110",
182 storageClass=
"SimpleCatalog",
183 dimensions=(
"skypix",),
186 lookupFunction=lookupVisitRefCats,
189 outputWcs = pipeBase.connectionTypes.Output(
190 doc=(
"Per-tract, per-visit world coordinate systems derived from the fitted model."
191 " These catalogs only contain entries for detectors with an output, and use"
192 " the detector id for the catalog id, sorted on id for fast lookups of a detector."),
193 name=
"jointcalSkyWcsCatalog",
194 storageClass=
"ExposureCatalog",
195 dimensions=(
"instrument",
"visit",
"skymap",
"tract"),
198 outputPhotoCalib = pipeBase.connectionTypes.Output(
199 doc=(
"Per-tract, per-visit photometric calibrations derived from the fitted model."
200 " These catalogs only contain entries for detectors with an output, and use"
201 " the detector id for the catalog id, sorted on id for fast lookups of a detector."),
202 name=
"jointcalPhotoCalibCatalog",
203 storageClass=
"ExposureCatalog",
204 dimensions=(
"instrument",
"visit",
"skymap",
"tract"),
212 for name
in (
"astrometry",
"photometry"):
213 vars()[f
"{name}_matched_fittedStars"] = pipeBase.connectionTypes.Output(
214 doc=f
"The number of cross-matched fittedStars for {name}",
215 name=f
"metricvalue_jointcal_{name}_matched_fittedStars",
216 storageClass=
"MetricValue",
217 dimensions=(
"skymap",
"tract",
"instrument",
"physical_filter"),
219 vars()[f
"{name}_collected_refStars"] = pipeBase.connectionTypes.Output(
220 doc=f
"The number of {name} reference stars drawn from the reference catalog, before matching.",
221 name=f
"metricvalue_jointcal_{name}_collected_refStars",
222 storageClass=
"MetricValue",
223 dimensions=(
"skymap",
"tract",
"instrument",
"physical_filter"),
225 vars()[f
"{name}_prepared_refStars"] = pipeBase.connectionTypes.Output(
226 doc=f
"The number of {name} reference stars matched to fittedStars.",
227 name=f
"metricvalue_jointcal_{name}_prepared_refStars",
228 storageClass=
"MetricValue",
229 dimensions=(
"skymap",
"tract",
"instrument",
"physical_filter"),
231 vars()[f
"{name}_prepared_fittedStars"] = pipeBase.connectionTypes.Output(
232 doc=f
"The number of cross-matched fittedStars after cleanup, for {name}.",
233 name=f
"metricvalue_jointcal_{name}_prepared_fittedStars",
234 storageClass=
"MetricValue",
235 dimensions=(
"skymap",
"tract",
"instrument",
"physical_filter"),
237 vars()[f
"{name}_prepared_ccdImages"] = pipeBase.connectionTypes.Output(
238 doc=f
"The number of ccdImages that will be fit for {name}, after cleanup.",
239 name=f
"metricvalue_jointcal_{name}_prepared_ccdImages",
240 storageClass=
"MetricValue",
241 dimensions=(
"skymap",
"tract",
"instrument",
"physical_filter"),
243 vars()[f
"{name}_final_chi2"] = pipeBase.connectionTypes.Output(
244 doc=f
"The final chi2 of the {name} fit.",
245 name=f
"metricvalue_jointcal_{name}_final_chi2",
246 storageClass=
"MetricValue",
247 dimensions=(
"skymap",
"tract",
"instrument",
"physical_filter"),
249 vars()[f
"{name}_final_ndof"] = pipeBase.connectionTypes.Output(
250 doc=f
"The number of degrees of freedom of the fitted {name}.",
251 name=f
"metricvalue_jointcal_{name}_final_ndof",
252 storageClass=
"MetricValue",
253 dimensions=(
"skymap",
"tract",
"instrument",
"physical_filter"),
264 if not config.doAstrometry:
265 self.prerequisiteInputs.
remove(
"astrometryRefCat")
266 self.outputs.
remove(
"outputWcs")
267 for key
in list(self.outputs):
268 if "metricvalue_jointcal_astrometry" in key:
270 if not config.doPhotometry:
271 self.prerequisiteInputs.
remove(
"photometryRefCat")
272 self.outputs.
remove(
"outputPhotoCalib")
273 for key
in list(self.outputs):
274 if "metricvalue_jointcal_photometry" in key:
279 pipelineConnections=JointcalTaskConnections):
280 """Configuration for JointcalTask"""
282 doAstrometry = pexConfig.Field(
283 doc=
"Fit astrometry and write the fitted result.",
287 doPhotometry = pexConfig.Field(
288 doc=
"Fit photometry and write the fitted result.",
292 sourceFluxType = pexConfig.Field(
294 doc=
"Source flux field to use in source selection and to get fluxes from the catalog.",
295 default=
'apFlux_12_0'
297 positionErrorPedestal = pexConfig.Field(
298 doc=
"Systematic term to apply to the measured position error (pixels)",
302 photometryErrorPedestal = pexConfig.Field(
303 doc=
"Systematic term to apply to the measured error on flux or magnitude as a "
304 "fraction of source flux or magnitude delta (e.g. 0.05 is 5% of flux or +50 millimag).",
309 matchCut = pexConfig.Field(
310 doc=
"Matching radius between fitted and reference stars (arcseconds)",
314 minMeasurements = pexConfig.Field(
315 doc=
"Minimum number of associated measured stars for a fitted star to be included in the fit",
319 minMeasuredStarsPerCcd = pexConfig.Field(
320 doc=
"Minimum number of measuredStars per ccdImage before printing warnings",
324 minRefStarsPerCcd = pexConfig.Field(
325 doc=
"Minimum number of measuredStars per ccdImage before printing warnings",
329 allowLineSearch = pexConfig.Field(
330 doc=
"Allow a line search during minimization, if it is reasonable for the model"
331 " (models with a significant non-linear component, e.g. constrainedPhotometry).",
335 astrometrySimpleOrder = pexConfig.Field(
336 doc=
"Polynomial order for fitting the simple astrometry model.",
340 astrometryChipOrder = pexConfig.Field(
341 doc=
"Order of the per-chip transform for the constrained astrometry model.",
345 astrometryVisitOrder = pexConfig.Field(
346 doc=
"Order of the per-visit transform for the constrained astrometry model.",
350 useInputWcs = pexConfig.Field(
351 doc=
"Use the input calexp WCSs to initialize a SimpleAstrometryModel.",
355 astrometryModel = pexConfig.ChoiceField(
356 doc=
"Type of model to fit to astrometry",
358 default=
"constrained",
359 allowed={
"simple":
"One polynomial per ccd",
360 "constrained":
"One polynomial per ccd, and one polynomial per visit"}
362 photometryModel = pexConfig.ChoiceField(
363 doc=
"Type of model to fit to photometry",
365 default=
"constrainedMagnitude",
366 allowed={
"simpleFlux":
"One constant zeropoint per ccd and visit, fitting in flux space.",
367 "constrainedFlux":
"Constrained zeropoint per ccd, and one polynomial per visit,"
368 " fitting in flux space.",
369 "simpleMagnitude":
"One constant zeropoint per ccd and visit,"
370 " fitting in magnitude space.",
371 "constrainedMagnitude":
"Constrained zeropoint per ccd, and one polynomial per visit,"
372 " fitting in magnitude space.",
375 applyColorTerms = pexConfig.Field(
376 doc=
"Apply photometric color terms to reference stars?"
377 "Requires that colorterms be set to a ColortermLibrary",
381 colorterms = pexConfig.ConfigField(
382 doc=
"Library of photometric reference catalog name to color term dict.",
383 dtype=ColortermLibrary,
385 photometryVisitOrder = pexConfig.Field(
386 doc=
"Order of the per-visit polynomial transform for the constrained photometry model.",
390 photometryDoRankUpdate = pexConfig.Field(
391 doc=(
"Do the rank update step during minimization. "
392 "Skipping this can help deal with models that are too non-linear."),
396 astrometryDoRankUpdate = pexConfig.Field(
397 doc=(
"Do the rank update step during minimization (should not change the astrometry fit). "
398 "Skipping this can help deal with models that are too non-linear."),
402 outlierRejectSigma = pexConfig.Field(
403 doc=
"How many sigma to reject outliers at during minimization.",
407 astrometryOutlierRelativeTolerance = pexConfig.Field(
408 doc=(
"Convergence tolerance for outlier rejection threshold when fitting astrometry. Iterations will "
409 "stop when the fractional change in the chi2 cut level is below this value. If tolerance is set "
410 "to zero, iterations will continue until there are no more outliers. We suggest a value of 0.002"
411 "as a balance between a shorter minimization runtime and achieving a final fitted model that is"
412 "close to the solution found when removing all outliers."),
416 maxPhotometrySteps = pexConfig.Field(
417 doc=
"Maximum number of minimize iterations to take when fitting photometry.",
421 maxAstrometrySteps = pexConfig.Field(
422 doc=
"Maximum number of minimize iterations to take when fitting astrometry.",
426 astrometryRefObjLoader = pexConfig.ConfigField(
427 dtype=LoadIndexedReferenceObjectsConfig,
428 doc=
"Reference object loader for astrometric fit",
430 photometryRefObjLoader = pexConfig.ConfigField(
431 dtype=LoadIndexedReferenceObjectsConfig,
432 doc=
"Reference object loader for photometric fit",
434 sourceSelector = sourceSelectorRegistry.makeField(
435 doc=
"How to select sources for cross-matching",
438 astrometryReferenceSelector = pexConfig.ConfigurableField(
439 target=ReferenceSourceSelectorTask,
440 doc=
"How to down-select the loaded astrometry reference catalog.",
442 photometryReferenceSelector = pexConfig.ConfigurableField(
443 target=ReferenceSourceSelectorTask,
444 doc=
"How to down-select the loaded photometry reference catalog.",
446 astrometryReferenceErr = pexConfig.Field(
447 doc=(
"Uncertainty on reference catalog coordinates [mas] to use in place of the `coord_*Err` fields. "
448 "If None, then raise an exception if the reference catalog is missing coordinate errors. "
449 "If specified, overrides any existing `coord_*Err` values."),
456 writeInitMatrix = pexConfig.Field(
458 doc=(
"Write the pre/post-initialization Hessian and gradient to text files, for debugging. "
459 "Output files will be written to `config.debugOutputPath` and will "
460 "be of the form 'astrometry_[pre|post]init-TRACT-FILTER-mat.txt'. "
461 "Note that these files are the dense versions of the matrix, and so may be very large."),
464 writeChi2FilesInitialFinal = pexConfig.Field(
466 doc=(
"Write .csv files containing the contributions to chi2 for the initialization and final fit. "
467 "Output files will be written to `config.debugOutputPath` and will "
468 "be of the form `astrometry_[initial|final]_chi2-TRACT-FILTER."),
471 writeChi2FilesOuterLoop = pexConfig.Field(
473 doc=(
"Write .csv files containing the contributions to chi2 for the outer fit loop. "
474 "Output files will be written to `config.debugOutputPath` and will "
475 "be of the form `astrometry_init-NN_chi2-TRACT-FILTER`."),
478 writeInitialModel = pexConfig.Field(
480 doc=(
"Write the pre-initialization model to text files, for debugging. "
481 "Output files will be written to `config.debugOutputPath` and will be "
482 "of the form `initial_astrometry_model-TRACT_FILTER.txt`."),
485 debugOutputPath = pexConfig.Field(
488 doc=(
"Path to write debug output files to. Used by "
489 "`writeInitialModel`, `writeChi2Files*`, `writeInitMatrix`.")
491 detailedProfile = pexConfig.Field(
494 doc=
"Output separate profiling information for different parts of jointcal, e.g. data read, fitting"
500 msg =
"applyColorTerms=True requires the `colorterms` field be set to a ColortermLibrary."
501 raise pexConfig.FieldValidationError(JointcalConfig.colorterms, self, msg)
503 msg = (
"Only doing astrometry, but Colorterms are not applied for astrometry;"
504 "applyColorTerms=True will be ignored.")
505 logging.getLogger(
"lsst.jointcal").warning(msg)
517 self.
sourceSelector[
"science"].signalToNoise.fluxField = f
"{self.sourceFluxType}_instFlux"
518 self.
sourceSelector[
"science"].signalToNoise.errField = f
"{self.sourceFluxType}_instFluxErr"
521 self.
sourceSelector[
"science"].isolated.parentName =
"parentSourceId"
522 self.
sourceSelector[
"science"].isolated.nChildName =
"deblend_nChild"
526 badFlags = [
"pixelFlags_edge",
527 "pixelFlags_saturated",
528 "pixelFlags_interpolatedCenter",
529 "pixelFlags_interpolated",
530 "pixelFlags_crCenter",
532 "hsmPsfMoments_flag",
533 f
"{self.sourceFluxType}_flag",
537 self.
sourceSelector[
"science"].requireFiniteRaDec.raColName =
"ra"
538 self.
sourceSelector[
"science"].requireFiniteRaDec.decColName =
"decl"
547 """Write model to outfile."""
548 with open(filename,
"w")
as file:
549 file.write(repr(model))
550 log.info(
"Wrote %s to file: %s", model, filename)
553@dataclasses.dataclass
555 """The input data jointcal needs for each detector/visit."""
557 """The visit identifier of this exposure."""
559 """The catalog derived from this exposure."""
561 """The VisitInfo of this exposure."""
563 """The detector of this exposure."""
565 """The photometric calibration of this exposure."""
567 """The WCS of this exposure."""
569 """The bounding box of this exposure."""
571 """The filter of this exposure."""
575 """Astrometricly and photometricly calibrate across multiple visits of the
579 ConfigClass = JointcalConfig
580 _DefaultName = "jointcal"
582 def __init__(self, **kwargs):
583 super().__init__(**kwargs)
584 self.makeSubtask(
"sourceSelector")
585 if self.config.doAstrometry:
586 self.makeSubtask(
"astrometryReferenceSelector")
589 if self.config.doPhotometry:
590 self.makeSubtask(
"photometryReferenceSelector")
595 self.
job = Job.load_metrics_package(subset=
'jointcal')
600 inputs = butlerQC.get(inputRefs)
602 tract = butlerQC.quantum.dataId[
'tract']
603 if self.config.doAstrometry:
605 dataIds=[ref.datasetRef.dataId
606 for ref
in inputRefs.astrometryRefCat],
607 refCats=inputs.pop(
'astrometryRefCat'),
608 config=self.config.astrometryRefObjLoader,
609 name=self.config.connections.astrometryRefCat,
611 if self.config.doPhotometry:
613 dataIds=[ref.datasetRef.dataId
614 for ref
in inputRefs.photometryRefCat],
615 refCats=inputs.pop(
'photometryRefCat'),
616 config=self.config.photometryRefObjLoader,
617 name=self.config.connections.photometryRefCat,
619 outputs = self.
run(**inputs, tract=tract)
621 if self.config.doAstrometry:
622 self.
_put_output(butlerQC, outputs.outputWcs, outputRefs.outputWcs,
623 inputs[
'inputCamera'],
"setWcs")
624 if self.config.doPhotometry:
625 self.
_put_output(butlerQC, outputs.outputPhotoCalib, outputRefs.outputPhotoCalib,
626 inputs[
'inputCamera'],
"setPhotoCalib")
628 def _put_metrics(self, butlerQC, job, outputRefs):
629 """Persist all measured metrics stored in a job.
633 butlerQC : `lsst.pipe.base.ButlerQuantumContext`
634 A butler which is specialized to operate
in the context of a
635 `lsst.daf.butler.Quantum`; This
is the input to `runQuantum`.
636 job : `lsst.verify.job.Job`
637 Measurements of metrics to persist.
638 outputRefs : `list` [`lsst.pipe.base.connectionTypes.OutputQuantizedConnection`]
639 The DatasetRefs to persist the data to.
641 for key
in job.measurements.keys():
642 butlerQC.put(job.measurements[key], getattr(outputRefs, key.fqn.replace(
'jointcal.',
'')))
644 def _put_output(self, butlerQC, outputs, outputRefs, camera, setter):
645 """Persist the output datasets to their appropriate datarefs.
649 butlerQC : `lsst.pipe.base.ButlerQuantumContext`
650 A butler which is specialized to operate
in the context of a
651 `lsst.daf.butler.Quantum`; This
is the input to `runQuantum`.
654 The fitted objects to persist.
655 outputRefs : `list` [`lsst.pipe.base.connectionTypes.OutputQuantizedConnection`]
656 The DatasetRefs to persist the data to.
658 The camera
for this instrument, to get detector ids
from.
660 The method to call on the ExposureCatalog to set each output.
663 schema.addField('visit', type=
'L', doc=
'Visit number')
665 def new_catalog(visit, size):
666 """Return an catalog ready to be filled with appropriate output."""
669 catalog[
'visit'] = visit
671 metadata.add(
"COMMENT",
"Catalog id is detector id, sorted.")
672 metadata.add(
"COMMENT",
"Only detectors with data have entries.")
676 detectors_per_visit = collections.defaultdict(int)
679 detectors_per_visit[key[0]] += 1
681 for ref
in outputRefs:
682 visit = ref.dataId[
'visit']
683 catalog = new_catalog(visit, detectors_per_visit[visit])
686 for detector
in camera:
687 detectorId = detector.getId()
688 key = (ref.dataId[
'visit'], detectorId)
689 if key
not in outputs:
691 self.log.debug(
"No %s output for detector %s in visit %s",
692 setter[3:], detectorId, visit)
695 catalog[i].setId(detectorId)
696 getattr(catalog[i], setter)(outputs[key])
700 butlerQC.put(catalog, ref)
701 self.log.info(
"Wrote %s detectors to %s", i, ref)
703 def run(self, inputSourceTableVisit, inputVisitSummary, inputCamera, tract=None):
709 sourceFluxField =
"flux"
713 oldWcsList, bands = self.
_load_data(inputSourceTableVisit,
719 boundingCircle, center, radius, defaultFilter, epoch = self.
_prep_sky(associations, bands)
721 if self.config.doAstrometry:
725 referenceSelector=self.astrometryReferenceSelector,
729 astrometry_output = self.
_make_output(associations.getCcdImageList(),
733 astrometry_output =
None
735 if self.config.doPhotometry:
739 referenceSelector=self.photometryReferenceSelector,
743 reject_bad_fluxes=
True)
744 photometry_output = self.
_make_output(associations.getCcdImageList(),
748 photometry_output =
None
750 return pipeBase.Struct(outputWcs=astrometry_output,
751 outputPhotoCalib=photometry_output,
756 def _load_data(self, inputSourceTableVisit, inputVisitSummary, associations,
757 jointcalControl, camera):
758 """Read the data that jointcal needs to run.
760 Modifies ``associations`` in-place
with the loaded data.
764 inputSourceTableVisit : `list` [`lsst.daf.butler.DeferredDatasetHandle`]
765 References to visit-level DataFrames to load the catalog data
from.
766 inputVisitSummary : `list` [`lsst.daf.butler.DeferredDatasetHandle`]
767 Visit-level exposure summary catalog
with metadata.
769 Object to add the loaded data to by constructing new CcdImages.
771 Control object
for C++ associations management.
773 Camera object
for detector geometry.
778 The original WCS of the input data, to aid
in writing tests.
779 bands : `list` [`str`]
780 The filter bands of each input dataset.
784 load_cat_profile_file = 'jointcal_load_data.prof' if self.config.detailedProfile
else ''
785 with lsst.utils.timer.profile(load_cat_profile_file):
788 catalogMap = {ref.dataId[
'visit']: i
for i, ref
in enumerate(inputSourceTableVisit)}
789 detectorDict = {detector.getId(): detector
for detector
in camera}
793 for visitSummaryRef
in inputVisitSummary:
794 visitSummary = visitSummaryRef.get()
796 dataRef = inputSourceTableVisit[catalogMap[visitSummaryRef.dataId[
'visit']]]
798 inColumns = dataRef.get(component=
'columns')
802 visitCatalog = dataRef.get(parameters={
'columns': columns})
804 selected = self.sourceSelector.run(visitCatalog)
805 if len(selected.sourceCat) == 0:
806 self.log.warning(
"No sources selected in visit %s. Skipping...",
807 visitSummary[
"visit"][0])
811 detectors = {id: index
for index, id
in enumerate(visitSummary[
'id'])}
812 for id, index
in detectors.items():
817 self.config.sourceFluxType,
823 if result
is not None:
824 oldWcsList.append(result.wcs)
826 filters.append(data.filter)
827 if len(filters) == 0:
828 raise RuntimeError(
"No data to process: did source selector remove all sources?")
829 filters = collections.Counter(filters)
831 return oldWcsList, filters
833 def _make_one_input_data(self, visitRecord, catalog, detectorDict):
834 """Return a data structure for this detector+visit."""
837 visitInfo=visitRecord.getVisitInfo(),
838 detector=detectorDict[visitRecord.getId()],
839 photoCalib=visitRecord.getPhotoCalib(),
840 wcs=visitRecord.getWcs(),
841 bbox=visitRecord.getBBox(),
845 physical=visitRecord[
'physical_filter']))
847 def _build_ccdImage(self, data, associations, jointcalControl):
849 Extract the necessary things from this catalog+metadata to add a new
854 data : `JointcalInputData`
855 The loaded input data.
857 Object to add the info to, to construct a new CcdImage
859 Control object
for associations management
865 The TAN WCS of this image, read
from the calexp
868 A key to identify this dataRef by its visit
and ccd ids
871 if there are no sources
in the loaded catalog.
873 if len(data.catalog) == 0:
874 self.log.warning(
"No sources selected in visit %s ccd %s", data.visit, data.detector.getId())
877 associations.createCcdImage(data.catalog,
881 data.filter.physicalLabel,
885 data.detector.getId(),
888 Result = collections.namedtuple(
'Result_from_build_CcdImage', (
'wcs',
'key'))
889 Key = collections.namedtuple(
'Key', (
'visit',
'ccd'))
890 return Result(data.wcs, Key(data.visit, data.detector.getId()))
892 def _getDebugPath(self, filename):
893 """Constructs a path to filename using the configured debug path.
895 return os.path.join(self.config.debugOutputPath, filename)
897 def _prep_sky(self, associations, filters):
898 """Prepare on-sky and other data that must be computed after data has
901 associations.computeCommonTangentPoint()
903 boundingCircle = associations.computeBoundingCircle()
905 radius = lsst.geom.Angle(boundingCircle.getOpeningAngle().asRadians(), lsst.geom.radians)
907 self.log.info(f"Data has center={center} with radius={radius.asDegrees()} degrees.")
910 defaultFilter = filters.most_common(1)[0][0]
911 self.log.debug(
"Using '%s' filter for reference flux", defaultFilter.physicalLabel)
915 associations.setEpoch(epoch.jyear)
917 return boundingCircle, center, radius, defaultFilter, epoch
919 def _get_refcat_coordinate_error_override(self, refCat, name):
920 """Check whether we should override the refcat coordinate errors, and
921 return the overridden error
if necessary.
926 The reference catalog to check
for a ``coord_raErr`` field.
928 Whether we are doing
"astrometry" or "photometry".
932 refCoordErr : `float`
933 The refcat coordinate error to use,
or NaN
if we are
not overriding
939 Raised
if the refcat does
not contain coordinate errors
and
940 ``config.astrometryReferenceErr``
is not set.
944 if name.lower() ==
"photometry":
945 if 'coord_raErr' not in refCat.schema:
950 if self.config.astrometryReferenceErr
is None and 'coord_raErr' not in refCat.schema:
951 msg = (
"Reference catalog does not contain coordinate errors, "
952 "and config.astrometryReferenceErr not supplied.")
953 raise pexConfig.FieldValidationError(JointcalConfig.astrometryReferenceErr,
957 if self.config.astrometryReferenceErr
is not None and 'coord_raErr' in refCat.schema:
958 self.log.warning(
"Overriding reference catalog coordinate errors with %f/coordinate [mas]",
959 self.config.astrometryReferenceErr)
961 if self.config.astrometryReferenceErr
is None:
964 return self.config.astrometryReferenceErr
966 def _compute_proper_motion_epoch(self, ccdImageList):
967 """Return the proper motion correction epoch of the provided images.
972 The images to compute the appropriate epoch for.
976 epoch : `astropy.time.Time`
977 The date to use
for proper motion corrections.
979 return astropy.time.Time(np.mean([ccdImage.getEpoch()
for ccdImage
in ccdImageList]),
983 def _do_load_refcat_and_fit(self, associations, defaultFilter, center, radius,
984 tract="", match_cut=3.0,
985 reject_bad_fluxes=False, *,
986 name="", refObjLoader=None, referenceSelector=None,
987 fit_function=None, epoch=None):
988 """Load reference catalog, perform the fit, and return the result.
993 The star/reference star associations to fit.
995 filter to load from reference catalog.
997 ICRS center of field to load
from reference catalog.
999 On-sky radius to load
from reference catalog.
1001 Name of thing being fit:
"astrometry" or "photometry".
1003 Reference object loader to use to load a reference catalog.
1005 Selector to use to pick objects
from the loaded reference catalog.
1006 fit_function : callable
1007 Function to call to perform fit (takes Associations object).
1008 tract : `str`, optional
1009 Name of tract currently being fit.
1010 match_cut : `float`, optional
1011 Radius
in arcseconds to find cross-catalog matches to during
1012 associations.associateCatalogs.
1013 reject_bad_fluxes : `bool`, optional
1014 Reject refCat sources
with NaN/inf flux
or NaN/0 fluxErr.
1015 epoch : `astropy.time.Time`, optional
1016 Epoch to which to correct refcat proper motion
and parallax,
1017 or `
None` to
not apply such corrections.
1021 result : `Photometry`
or `Astrometry`
1022 Result of `fit_function()`
1024 self.log.info("====== Now processing %s...", name)
1027 associations.associateCatalogs(match_cut)
1029 associations.fittedStarListSize())
1031 applyColorterms =
False if name.lower() ==
"astrometry" else self.config.applyColorTerms
1033 center, radius, defaultFilter,
1034 applyColorterms=applyColorterms,
1038 associations.collectRefStars(refCat,
1039 self.config.matchCut*lsst.geom.arcseconds,
1041 refCoordinateErr=refCoordErr,
1042 rejectBadFluxes=reject_bad_fluxes)
1044 associations.refStarListSize())
1046 associations.prepareFittedStars(self.config.minMeasurements)
1050 associations.nFittedStarsWithAssociatedRefStar())
1052 associations.fittedStarListSize())
1054 associations.nCcdImagesValidForFit())
1056 fit_profile_file =
'jointcal_fit_%s.prof'%name
if self.config.detailedProfile
else ''
1057 dataName =
"{}_{}".format(tract, defaultFilter.physicalLabel)
1058 with lsst.utils.timer.profile(fit_profile_file):
1059 result = fit_function(associations, dataName)
1062 if self.config.writeChi2FilesInitialFinal:
1063 baseName = self.
_getDebugPath(f
"{name}_final_chi2-{dataName}")
1064 result.fit.saveChi2Contributions(baseName+
"{type}")
1065 self.log.info(
"Wrote chi2 contributions files: %s", baseName)
1069 def _load_reference_catalog(self, refObjLoader, referenceSelector, center, radius, filterLabel,
1070 applyColorterms=False, epoch=None):
1071 """Load the necessary reference catalog sources, convert fluxes to
1072 correct units, and apply color term corrections
if requested.
1077 The reference catalog loader to use to get the data.
1079 Source selector to apply to loaded reference catalog.
1081 The center around which to load sources.
1083 The radius around ``center`` to load sources
in.
1085 The camera filter to load fluxes
for.
1086 applyColorterms : `bool`
1087 Apply colorterm corrections to the refcat
for ``filterName``?
1088 epoch : `astropy.time.Time`, optional
1089 Epoch to which to correct refcat proper motion
and parallax,
1090 or `
None` to
not apply such corrections.
1095 The loaded reference catalog.
1097 The name of the reference catalog flux field appropriate
for ``filterName``.
1099 skyCircle = refObjLoader.loadSkyCircle(center,
1101 filterLabel.bandLabel,
1104 selected = referenceSelector.run(skyCircle.refCat)
1106 if not selected.sourceCat.isContiguous():
1107 refCat = selected.sourceCat.copy(deep=
True)
1109 refCat = selected.sourceCat
1112 refCatName = refObjLoader.name
1113 self.log.info(
"Applying color terms for physical filter=%r reference catalog=%s",
1114 filterLabel.physicalLabel, refCatName)
1115 colorterm = self.config.colorterms.getColorterm(filterLabel.physicalLabel,
1119 refMag, refMagErr = colorterm.getCorrectedMagnitudes(refCat)
1120 refCat[skyCircle.fluxField] = u.Magnitude(refMag, u.ABmag).to_value(u.nJy)
1122 refCat[skyCircle.fluxField+
'Err'] = fluxErrFromABMagErr(refMagErr, refMag) * 1e9
1124 return refCat, skyCircle.fluxField
1126 def _check_star_lists(self, associations, name):
1128 if associations.nCcdImagesValidForFit() == 0:
1129 raise RuntimeError(
'No images in the ccdImageList!')
1130 if associations.fittedStarListSize() == 0:
1131 raise RuntimeError(
'No stars in the {} fittedStarList!'.format(name))
1132 if associations.refStarListSize() == 0:
1133 raise RuntimeError(
'No stars in the {} reference star list!'.format(name))
1135 def _logChi2AndValidate(self, associations, fit, model, chi2Label, writeChi2Name=None):
1136 """Compute chi2, log it, validate the model, and return chi2.
1141 The star/reference star associations to fit.
1143 The fitter to use for minimization.
1144 model : `lsst.jointcal.Model`
1145 The model being fit.
1147 Label to describe the chi2 (e.g.
"Initialized",
"Final").
1148 writeChi2Name : `str`, optional
1149 Filename prefix to write the chi2 contributions to.
1150 Do
not supply an extension: an appropriate one will be added.
1155 The chi2 object
for the current fitter
and model.
1160 Raised
if chi2
is infinite
or NaN.
1162 Raised
if the model
is not valid.
1164 if writeChi2Name
is not None:
1166 fit.saveChi2Contributions(fullpath+
"{type}")
1167 self.log.info(
"Wrote chi2 contributions files: %s", fullpath)
1169 chi2 = fit.computeChi2()
1170 self.log.info(
"%s %s", chi2Label, chi2)
1172 if not np.isfinite(chi2.chi2):
1173 raise FloatingPointError(f
'{chi2Label} chi2 is invalid: {chi2}')
1174 if not model.validate(associations.getCcdImageList(), chi2.ndof):
1175 raise ValueError(
"Model is not valid: check log messages for warnings.")
1178 def _fit_photometry(self, associations, dataName=None):
1180 Fit the photometric data.
1185 The star/reference star associations to fit.
1187 Name of the data being processed (e.g. "1234_HSC-Y"),
for
1188 identifying debugging files.
1192 fit_result : `namedtuple`
1194 The photometric fitter used to perform the fit.
1196 The photometric model that was fit.
1198 self.log.info("=== Starting photometric fitting...")
1201 if self.config.photometryModel ==
"constrainedFlux":
1204 visitOrder=self.config.photometryVisitOrder,
1205 errorPedestal=self.config.photometryErrorPedestal)
1207 doLineSearch = self.config.allowLineSearch
1208 elif self.config.photometryModel ==
"constrainedMagnitude":
1211 visitOrder=self.config.photometryVisitOrder,
1212 errorPedestal=self.config.photometryErrorPedestal)
1214 doLineSearch = self.config.allowLineSearch
1215 elif self.config.photometryModel ==
"simpleFlux":
1217 errorPedestal=self.config.photometryErrorPedestal)
1218 doLineSearch =
False
1219 elif self.config.photometryModel ==
"simpleMagnitude":
1221 errorPedestal=self.config.photometryErrorPedestal)
1222 doLineSearch =
False
1227 if self.config.writeChi2FilesInitialFinal:
1228 baseName = f
"photometry_initial_chi2-{dataName}"
1231 if self.config.writeInitialModel:
1232 fullpath = self.
_getDebugPath(f
"initial_photometry_model-{dataName}.txt")
1236 def getChi2Name(whatToFit):
1237 if self.config.writeChi2FilesOuterLoop:
1238 return f
"photometry_init-%s_chi2-{dataName}" % whatToFit
1244 if self.config.writeInitMatrix:
1245 dumpMatrixFile = self.
_getDebugPath(f
"photometry_preinit-{dataName}")
1248 if self.config.photometryModel.startswith(
"constrained"):
1251 fit.minimize(
"ModelVisit", dumpMatrixFile=dumpMatrixFile)
1253 writeChi2Name=getChi2Name(
"ModelVisit"))
1256 fit.minimize(
"Model", doLineSearch=doLineSearch, dumpMatrixFile=dumpMatrixFile)
1258 writeChi2Name=getChi2Name(
"Model"))
1260 fit.minimize(
"Fluxes")
1262 writeChi2Name=getChi2Name(
"Fluxes"))
1264 fit.minimize(
"Model Fluxes", doLineSearch=doLineSearch)
1266 writeChi2Name=getChi2Name(
"ModelFluxes"))
1268 model.freezeErrorTransform()
1269 self.log.debug(
"Photometry error scales are frozen.")
1273 self.config.maxPhotometrySteps,
1276 doRankUpdate=self.config.photometryDoRankUpdate,
1277 doLineSearch=doLineSearch,
1284 def _fit_astrometry(self, associations, dataName=None):
1286 Fit the astrometric data.
1291 The star/reference star associations to fit.
1293 Name of the data being processed (e.g. "1234_HSC-Y"),
for
1294 identifying debugging files.
1298 fit_result : `namedtuple`
1300 The astrometric fitter used to perform the fit.
1302 The astrometric model that was fit.
1304 The model
for the sky to tangent plane projection that was used
in the fit.
1307 self.log.info("=== Starting astrometric fitting...")
1309 associations.deprojectFittedStars()
1316 if self.config.astrometryModel ==
"constrained":
1318 sky_to_tan_projection,
1319 chipOrder=self.config.astrometryChipOrder,
1320 visitOrder=self.config.astrometryVisitOrder)
1321 elif self.config.astrometryModel ==
"simple":
1323 sky_to_tan_projection,
1324 self.config.useInputWcs,
1326 order=self.config.astrometrySimpleOrder)
1331 if self.config.writeChi2FilesInitialFinal:
1332 baseName = f
"astrometry_initial_chi2-{dataName}"
1335 if self.config.writeInitialModel:
1336 fullpath = self.
_getDebugPath(f
"initial_astrometry_model-{dataName}.txt")
1340 def getChi2Name(whatToFit):
1341 if self.config.writeChi2FilesOuterLoop:
1342 return f
"astrometry_init-%s_chi2-{dataName}" % whatToFit
1346 if self.config.writeInitMatrix:
1347 dumpMatrixFile = self.
_getDebugPath(f
"astrometry_preinit-{dataName}")
1352 if self.config.astrometryModel ==
"constrained":
1353 fit.minimize(
"DistortionsVisit", dumpMatrixFile=dumpMatrixFile)
1355 writeChi2Name=getChi2Name(
"DistortionsVisit"))
1358 fit.minimize(
"Distortions", dumpMatrixFile=dumpMatrixFile)
1360 writeChi2Name=getChi2Name(
"Distortions"))
1362 fit.minimize(
"Positions")
1364 writeChi2Name=getChi2Name(
"Positions"))
1366 fit.minimize(
"Distortions Positions")
1368 writeChi2Name=getChi2Name(
"DistortionsPositions"))
1372 self.config.maxAstrometrySteps,
1374 "Distortions Positions",
1375 sigmaRelativeTolerance=self.config.astrometryOutlierRelativeTolerance,
1376 doRankUpdate=self.config.astrometryDoRankUpdate,
1382 return Astrometry(fit, model, sky_to_tan_projection)
1384 def _check_stars(self, associations):
1385 """Count measured and reference stars per ccd and warn/log them."""
1386 for ccdImage
in associations.getCcdImageList():
1387 nMeasuredStars, nRefStars = ccdImage.countStars()
1388 self.log.debug(
"ccdImage %s has %s measured and %s reference stars",
1389 ccdImage.getName(), nMeasuredStars, nRefStars)
1390 if nMeasuredStars < self.config.minMeasuredStarsPerCcd:
1391 self.log.warning(
"ccdImage %s has only %s measuredStars (desired %s)",
1392 ccdImage.getName(), nMeasuredStars, self.config.minMeasuredStarsPerCcd)
1393 if nRefStars < self.config.minRefStarsPerCcd:
1394 self.log.warning(
"ccdImage %s has only %s RefStars (desired %s)",
1395 ccdImage.getName(), nRefStars, self.config.minRefStarsPerCcd)
1397 def _iterate_fit(self, associations, fitter, max_steps, name, whatToFit,
1399 sigmaRelativeTolerance=0,
1401 doLineSearch=False):
1402 """Run fitter.minimize up to max_steps times, returning the final chi2.
1407 The star/reference star associations to fit.
1409 The fitter to use for minimization.
1411 Maximum number of steps to run outlier rejection before declaring
1412 convergence failure.
1413 name : {
'photometry' or 'astrometry'}
1414 What type of data are we fitting (
for logs
and debugging files).
1416 Passed to ``fitter.minimize()`` to define the parameters to fit.
1417 dataName : `str`, optional
1418 Descriptive name
for this dataset (e.g. tract
and filter),
1420 sigmaRelativeTolerance : `float`, optional
1421 Convergence tolerance
for the fractional change
in the chi2 cut
1422 level
for determining outliers. If set to zero, iterations will
1423 continue until there are no outliers.
1424 doRankUpdate : `bool`, optional
1425 Do an Eigen rank update during minimization,
or recompute the full
1426 matrix
and gradient?
1427 doLineSearch : `bool`, optional
1428 Do a line search
for the optimum step during minimization?
1433 The final chi2 after the fit converges,
or is forced to end.
1438 Raised
if the fitter fails
with a non-finite value.
1440 Raised
if the fitter fails
for some other reason;
1441 log messages will provide further details.
1443 if self.config.writeInitMatrix:
1444 dumpMatrixFile = self.
_getDebugPath(f
"{name}_postinit-{dataName}")
1448 oldChi2.chi2 = float(
"inf")
1449 for i
in range(max_steps):
1450 if self.config.writeChi2FilesOuterLoop:
1451 writeChi2Name = f
"{name}_iterate_{i}_chi2-{dataName}"
1453 writeChi2Name =
None
1454 result = fitter.minimize(whatToFit,
1455 self.config.outlierRejectSigma,
1456 sigmaRelativeTolerance=sigmaRelativeTolerance,
1457 doRankUpdate=doRankUpdate,
1458 doLineSearch=doLineSearch,
1459 dumpMatrixFile=dumpMatrixFile)
1462 f
"Fit iteration {i}", writeChi2Name=writeChi2Name)
1464 if result == MinimizeResult.Converged:
1466 self.log.debug(
"fit has converged - no more outliers - redo minimization "
1467 "one more time in case we have lost accuracy in rank update.")
1469 result = fitter.minimize(whatToFit, self.config.outlierRejectSigma,
1470 sigmaRelativeTolerance=sigmaRelativeTolerance)
1474 if chi2.chi2/chi2.ndof >= 4.0:
1475 self.log.error(
"Potentially bad fit: High chi-squared/ndof.")
1478 elif result == MinimizeResult.Chi2Increased:
1479 self.log.warning(
"Still some outliers remaining but chi2 increased - retry")
1481 chi2Ratio = chi2.chi2 / oldChi2.chi2
1483 self.log.warning(
'Significant chi2 increase by a factor of %.4g / %.4g = %.4g',
1484 chi2.chi2, oldChi2.chi2, chi2Ratio)
1491 msg = (
"Large chi2 increase between steps: fit likely cannot converge."
1492 " Try setting one or more of the `writeChi2*` config fields and looking"
1493 " at how individual star chi2-values evolve during the fit.")
1494 raise RuntimeError(msg)
1496 elif result == MinimizeResult.NonFinite:
1497 filename = self.
_getDebugPath(
"{}_failure-nonfinite_chi2-{}.csv".format(name, dataName))
1499 fitter.saveChi2Contributions(filename+
"{type}")
1500 msg =
"Nonfinite value in chi2 minimization, cannot complete fit. Dumped star tables to: {}"
1501 raise FloatingPointError(msg.format(filename))
1502 elif result == MinimizeResult.Failed:
1503 raise RuntimeError(
"Chi2 minimization failure, cannot complete fit.")
1505 raise RuntimeError(
"Unxepected return code from minimize().")
1507 self.log.error(
"%s failed to converge after %d steps"%(name, max_steps))
1511 def _make_output(self, ccdImageList, model, func):
1512 """Return the internal jointcal models converted to the afw
1513 structures that will be saved to disk.
1518 The list of CcdImages to get the output for.
1522 The name of the function to call on ``model`` to get the converted
1529 The data to be saved, keyed on (visit, detector).
1532 for ccdImage
in ccdImageList:
1533 ccd = ccdImage.ccdId
1534 visit = ccdImage.visit
1535 self.log.debug(
"%s for visit: %d, ccd: %d", func, visit, ccd)
1536 output[(visit, ccd)] = getattr(model, func)(ccdImage)
1541 """Return an afw SourceTable to use as a base for creating the
1542 SourceCatalog to insert values from the dataFrame into.
1547 Table
with schema
and slots to use to make SourceCatalogs.
1550 schema.addField("centroid_x",
"D")
1551 schema.addField(
"centroid_y",
"D")
1552 schema.addField(
"centroid_xErr",
"F")
1553 schema.addField(
"centroid_yErr",
"F")
1554 schema.addField(
"shape_xx",
"D")
1555 schema.addField(
"shape_yy",
"D")
1556 schema.addField(
"shape_xy",
"D")
1557 schema.addField(
"flux_instFlux",
"D")
1558 schema.addField(
"flux_instFluxErr",
"D")
1560 table.defineCentroid(
"centroid")
1561 table.defineShape(
"shape")
1567 Get the sourceTable_visit columns to load from the catalogs.
1572 List of columns known to be available
in the sourceTable_visit.
1573 config : `JointcalConfig`
1574 A filled-
in config to to help define column names.
1576 A configured source selector to define column names to load.
1581 List of columns to read
from sourceTable_visit.
1583 Name of the ixx/iyy/ixy columns.
1585 columns = ['visit',
'detector',
1586 'sourceId',
'x',
'xErr',
'y',
'yErr',
1587 config.sourceFluxType +
'_instFlux', config.sourceFluxType +
'_instFluxErr']
1589 if 'ixx' in inColumns:
1591 ixxColumns = [
'ixx',
'iyy',
'ixy']
1594 ixxColumns = [
'Ixx',
'Iyy',
'Ixy']
1595 columns.extend(ixxColumns)
1597 if sourceSelector.config.doFlags:
1598 columns.extend(sourceSelector.config.flags.bad)
1599 if sourceSelector.config.doUnresolved:
1600 columns.append(sourceSelector.config.unresolved.name)
1601 if sourceSelector.config.doIsolated:
1602 columns.append(sourceSelector.config.isolated.parentName)
1603 columns.append(sourceSelector.config.isolated.nChildName)
1604 if sourceSelector.config.doRequireFiniteRaDec:
1605 columns.append(sourceSelector.config.requireFiniteRaDec.raColName)
1606 columns.append(sourceSelector.config.requireFiniteRaDec.decColName)
1608 return columns, ixxColumns
1612 ixxColumns, sourceFluxType, log):
1613 """Return an afw SourceCatalog extracted from a visit-level dataframe,
1614 limited to just one detector.
1619 Table factory to use to make the SourceCatalog that will be
1620 populated with data
from ``visitCatalog``.
1621 visitCatalog : `pandas.DataFrame`
1622 DataFrame to extract a detector catalog
from.
1624 Numeric id of the detector to extract
from ``visitCatalog``.
1625 ixxColumns : `list` [`str`]
1626 Names of the ixx/iyy/ixy columns
in the catalog.
1627 sourceFluxType : `str`
1628 Name of the catalog field to load instFluxes
from.
1629 log : `logging.Logger`
1630 Logging instance to log to.
1635 Detector-level catalog extracted
from ``visitCatalog``,
or `
None`
1636 if there was no data to load.
1639 mapping = {
'x':
'centroid_x',
1641 'xErr':
'centroid_xErr',
1642 'yErr':
'centroid_yErr',
1643 ixxColumns[0]:
'shape_xx',
1644 ixxColumns[1]:
'shape_yy',
1645 ixxColumns[2]:
'shape_xy',
1646 f
'{sourceFluxType}_instFlux':
'flux_instFlux',
1647 f
'{sourceFluxType}_instFluxErr':
'flux_instFluxErr',
1651 matched = visitCatalog[
'detector'] == detectorId
1655 catalog.resize(sum(matched))
1656 view = visitCatalog.loc[matched]
1657 catalog[
'id'] = view.index
1658 for dfCol, afwCol
in mapping.items():
1659 catalog[afwCol] = view[dfCol]
1661 log.debug(
"%d sources selected in visit %d detector %d",
1663 view[
'visit'].iloc[0],
An immutable representation of a camera.
A representation of a detector in a mosaic camera.
A 2-dimensional celestial WCS that transform pixels to ICRS RA/Dec, using the LSST standard for pixel...
A group of labels for a filter in an exposure or coadd.
The photometric calibration of an exposure.
Information about a single exposure of an imaging camera.
Custom catalog class for ExposureRecord/Table.
static Schema makeMinimalSchema()
Return a minimal schema for Exposure tables and records.
Custom catalog class for record/table subclasses that are guaranteed to have an ID,...
Table class that contains measurements made on a single exposure.
static std::shared_ptr< SourceTable > make(Schema const &schema, std::shared_ptr< IdFactory > const &idFactory)
Construct a new table.
static Schema makeMinimalSchema()
Return a minimal schema for Source tables and records.
Class for storing ordered metadata with comments.
A class representing an angle.
An integer coordinate rectangle.
Point in an unspecified spherical coordinate system.
The class that implements the relations between MeasuredStar and FittedStar.
Class that handles the astrometric least squares problem.
Interface between AstrometryFit and the combinations of Mappings from pixels to some tangent plane (a...
Handler of an actual image from a single CCD.
Base class for Chi2Statistic and Chi2List, to allow addEntry inside Fitter for either class.
Simple structure to accumulate chi2 and ndof.
A multi-component model, fitting mappings for sensors and visits simultaneously.
A projection handler in which all CCDs from the same visit have the same tangent point.
Class that handles the photometric least squares problem.
A model where there is one independent transform per CcdImage.
def __init__(self, *config=None)
def _load_data(self, inputSourceTableVisit, inputVisitSummary, associations, jointcalControl, camera)
def _compute_proper_motion_epoch(self, ccdImageList)
def _get_refcat_coordinate_error_override(self, refCat, name)
def _getDebugPath(self, filename)
def _check_star_lists(self, associations, name)
def _make_one_input_data(self, visitRecord, catalog, detectorDict)
def _iterate_fit(self, associations, fitter, max_steps, name, whatToFit, dataName="", sigmaRelativeTolerance=0, doRankUpdate=True, doLineSearch=False)
def _check_stars(self, associations)
def _prep_sky(self, associations, filters)
def runQuantum(self, butlerQC, inputRefs, outputRefs)
def _do_load_refcat_and_fit(self, associations, defaultFilter, center, radius, tract="", match_cut=3.0, reject_bad_fluxes=False, *name="", refObjLoader=None, referenceSelector=None, fit_function=None, epoch=None)
def _put_metrics(self, butlerQC, job, outputRefs)
def _build_ccdImage(self, data, associations, jointcalControl)
def run(self, inputSourceTableVisit, inputVisitSummary, inputCamera, tract=None)
def _fit_photometry(self, associations, dataName=None)
def _logChi2AndValidate(self, associations, fit, model, chi2Label, writeChi2Name=None)
def _make_output(self, ccdImageList, model, func)
def _put_output(self, butlerQC, outputs, outputRefs, camera, setter)
def _load_reference_catalog(self, refObjLoader, referenceSelector, center, radius, filterLabel, applyColorterms=False, epoch=None)
def _fit_astrometry(self, associations, dataName=None)
def extract_detector_catalog_from_visit_catalog(table, visitCatalog, detectorId, ixxColumns, sourceFluxType, log)
def add_measurement(job, name, value)
def get_sourceTable_visit_columns(inColumns, config, sourceSelector)
def lookupVisitRefCats(datasetType, registry, quantumDataId, collections)
def writeModel(model, filename, log)
This is a virtual class that allows a lot of freedom in the choice of the projection from "Sky" (wher...