Coverage for tests/test_gbdesAstrometricFit.py: 13%
296 statements
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-26 11:22 +0000
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-26 11:22 +0000
1# This file is part of drp_tasks
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
22import os.path
23import unittest
25import astropy.units as u
26import lsst.afw.geom as afwgeom
27import lsst.afw.table as afwTable
28import lsst.geom
29import lsst.utils
30import numpy as np
31import pandas as pd
32import wcsfit
33import yaml
34from lsst import sphgeom
35from lsst.daf.base import PropertyList
36from lsst.drp.tasks.gbdesAstrometricFit import GbdesAstrometricFitConfig, GbdesAstrometricFitTask
37from lsst.meas.algorithms import ReferenceObjectLoader
38from lsst.meas.algorithms.testUtils import MockRefcatDataId
39from lsst.pipe.base import InMemoryDatasetHandle
41TESTDIR = os.path.abspath(os.path.dirname(__file__))
44class TestGbdesAstrometricFit(lsst.utils.tests.TestCase):
45 @classmethod
46 def setUpClass(cls):
47 # Set random seed
48 np.random.seed(1234)
50 # Fraction of simulated stars in the reference catalog and science
51 # exposures
52 inReferenceFraction = 1
53 inScienceFraction = 1
55 # Make fake data
56 cls.datadir = os.path.join(TESTDIR, "data")
58 cls.fieldNumber = 0
59 cls.instrumentName = "HSC"
60 cls.instrument = wcsfit.Instrument(cls.instrumentName)
61 cls.refEpoch = 57205.5
63 # Make test inputVisitSummary. VisitSummaryTables are taken from
64 # collection HSC/runs/RC2/w_2022_20/DM-34794
65 cls.testVisits = [1176, 17900, 17930, 17934]
66 cls.inputVisitSummary = []
67 for testVisit in cls.testVisits:
68 visSum = afwTable.ExposureCatalog.readFits(
69 os.path.join(cls.datadir, f"visitSummary_{testVisit}.fits")
70 )
71 cls.inputVisitSummary.append(visSum)
73 cls.config = GbdesAstrometricFitConfig()
74 cls.config.systematicError = 0
75 cls.config.devicePolyOrder = 4
76 cls.config.exposurePolyOrder = 6
77 cls.config.fitReserveFraction = 0
78 cls.config.fitReserveRandomSeed = 1234
79 cls.config.saveModelParams = True
80 cls.task = GbdesAstrometricFitTask(config=cls.config)
82 cls.exposureInfo, cls.exposuresHelper, cls.extensionInfo = cls.task._get_exposure_info(
83 cls.inputVisitSummary, cls.instrument, refEpoch=cls.refEpoch
84 )
86 cls.fields, cls.fieldCenter, cls.fieldRadius = cls.task._prep_sky(
87 cls.inputVisitSummary, cls.exposureInfo.medianEpoch
88 )
90 # Bounding box of observations:
91 raMins, raMaxs = [], []
92 decMins, decMaxs = [], []
93 for visSum in cls.inputVisitSummary:
94 raMins.append(visSum["raCorners"].min())
95 raMaxs.append(visSum["raCorners"].max())
96 decMins.append(visSum["decCorners"].min())
97 decMaxs.append(visSum["decCorners"].max())
98 raMin = min(raMins)
99 raMax = max(raMaxs)
100 decMin = min(decMins)
101 decMax = max(decMaxs)
103 corners = [
104 lsst.geom.SpherePoint(raMin, decMin, lsst.geom.degrees).getVector(),
105 lsst.geom.SpherePoint(raMax, decMin, lsst.geom.degrees).getVector(),
106 lsst.geom.SpherePoint(raMax, decMax, lsst.geom.degrees).getVector(),
107 lsst.geom.SpherePoint(raMin, decMax, lsst.geom.degrees).getVector(),
108 ]
109 cls.boundingPolygon = sphgeom.ConvexPolygon(corners)
111 # Make random set of data in a bounding box determined by input visits
112 # Make wcs objects for the "true" model
113 cls.nStars = 10000
114 starIds = np.arange(cls.nStars)
115 starRAs = np.random.random(cls.nStars) * (raMax - raMin) + raMin
116 starDecs = np.random.random(cls.nStars) * (decMax - decMin) + decMin
118 # Make a reference catalog and load it into ReferenceObjectLoader
119 refDataId, deferredRefCat = cls._make_refCat(starIds, starRAs, starDecs, inReferenceFraction)
120 cls.refObjectLoader = ReferenceObjectLoader([refDataId], [deferredRefCat])
121 cls.refObjectLoader.config.requireProperMotion = False
122 cls.refObjectLoader.config.anyFilterMapsToThis = "test_filter"
124 cls.task.refObjectLoader = cls.refObjectLoader
126 # Get True WCS for stars:
127 with open(os.path.join(cls.datadir, "sample_wcs.yaml"), "r") as f:
128 cls.trueModel = yaml.load(f, Loader=yaml.Loader)
130 trueWCSs = cls._make_wcs(cls.trueModel, cls.inputVisitSummary)
132 # Make source catalogs:
133 cls.inputCatalogRefs = cls._make_sourceCat(starIds, starRAs, starDecs, trueWCSs, inScienceFraction)
135 cls.outputs = cls.task.run(
136 cls.inputCatalogRefs,
137 cls.inputVisitSummary,
138 instrumentName=cls.instrumentName,
139 refEpoch=cls.refEpoch,
140 refObjectLoader=cls.refObjectLoader,
141 )
143 @classmethod
144 def _make_refCat(cls, starIds, starRas, starDecs, inReferenceFraction):
145 """Make reference catalog from a subset of the simulated data
147 Parameters
148 ----------
149 starIds : `np.ndarray` of `int`
150 Source ids for the simulated stars
151 starRas : `np.ndarray` of `float`
152 RAs of the simulated stars
153 starDecs : `np.ndarray` of `float`
154 Decs of the simulated stars
155 inReferenceFraction : float
156 Percentage of simulated stars to include in reference catalog
158 Returns
159 -------
160 refDataId : `lsst.meas.algorithms.testUtils.MockRefcatDataId`
161 Object that replicates the functionality of a dataId.
162 deferredRefCat : `lsst.pipe.base.InMemoryDatasetHandle`
163 Dataset handle for reference catalog.
164 """
165 nRefs = int(cls.nStars * inReferenceFraction)
166 refStarIndices = np.random.choice(cls.nStars, nRefs, replace=False)
167 # Make simpleCatalog to hold data, create datasetRef with `region`
168 # determined by bounding box used in above simulate.
169 refSchema = afwTable.SimpleTable.makeMinimalSchema()
170 idKey = refSchema.addField("sourceId", type="I")
171 fluxKey = refSchema.addField("test_filter_flux", units="nJy", type=np.float64)
172 raErrKey = refSchema.addField("coord_raErr", type=np.float64)
173 decErrKey = refSchema.addField("coord_decErr", type=np.float64)
174 pmraErrKey = refSchema.addField("pm_raErr", type=np.float64)
175 pmdecErrKey = refSchema.addField("pm_decErr", type=np.float64)
176 refCat = afwTable.SimpleCatalog(refSchema)
177 ref_md = PropertyList()
178 ref_md.set("REFCAT_FORMAT_VERSION", 1)
179 refCat.table.setMetadata(ref_md)
180 for i in refStarIndices:
181 record = refCat.addNew()
182 record.set(idKey, starIds[i])
183 record.setRa(lsst.geom.Angle(starRas[i], lsst.geom.degrees))
184 record.setDec(lsst.geom.Angle(starDecs[i], lsst.geom.degrees))
185 record.set(fluxKey, 1)
186 record.set(raErrKey, 0.00001)
187 record.set(decErrKey, 0.00001)
188 record.set(pmraErrKey, 1e-9)
189 record.set(pmdecErrKey, 1e-9)
190 refDataId = MockRefcatDataId(cls.boundingPolygon)
191 deferredRefCat = InMemoryDatasetHandle(refCat, storageClass="SourceCatalog", htm7="mockRefCat")
193 return refDataId, deferredRefCat
195 @classmethod
196 def _make_sourceCat(cls, starIds, starRas, starDecs, trueWCSs, inScienceFraction):
197 """Make a `pd.DataFrame` catalog with the columns needed for the
198 object selector.
200 Parameters
201 ----------
202 starIds : `np.ndarray` of `int`
203 Source ids for the simulated stars
204 starRas : `np.ndarray` of `float`
205 RAs of the simulated stars
206 starDecs : `np.ndarray` of `float`
207 Decs of the simulated stars
208 trueWCSs : `list` of `lsst.afw.geom.SkyWcs`
209 WCS with which to simulate the source pixel coordinates
210 inReferenceFraction : float
211 Percentage of simulated stars to include in reference catalog
213 Returns
214 -------
215 sourceCat : `list` of `lsst.pipe.base.InMemoryDatasetHandle`
216 List of reference to source catalogs.
217 """
218 inputCatalogRefs = []
219 # Take a subset of the simulated data
220 # Use true wcs objects to put simulated data into ccds
221 bbox = lsst.geom.BoxD(
222 lsst.geom.Point2D(
223 cls.inputVisitSummary[0][0]["bbox_min_x"], cls.inputVisitSummary[0][0]["bbox_min_y"]
224 ),
225 lsst.geom.Point2D(
226 cls.inputVisitSummary[0][0]["bbox_max_x"], cls.inputVisitSummary[0][0]["bbox_max_y"]
227 ),
228 )
229 bboxCorners = bbox.getCorners()
230 cls.inputCatalogRefs = []
231 for v, visit in enumerate(cls.testVisits):
232 nVisStars = int(cls.nStars * inScienceFraction)
233 visitStarIndices = np.random.choice(cls.nStars, nVisStars, replace=False)
234 visitStarIds = starIds[visitStarIndices]
235 visitStarRas = starRas[visitStarIndices]
236 visitStarDecs = starDecs[visitStarIndices]
237 sourceCats = []
238 for detector in trueWCSs[visit]:
239 detWcs = detector.getWcs()
240 detectorId = detector["id"]
241 radecCorners = detWcs.pixelToSky(bboxCorners)
242 detectorFootprint = sphgeom.ConvexPolygon([rd.getVector() for rd in radecCorners])
243 detectorIndices = detectorFootprint.contains(
244 (visitStarRas * u.degree).to(u.radian), (visitStarDecs * u.degree).to(u.radian)
245 )
246 nDetectorStars = detectorIndices.sum()
247 detectorArray = np.ones(nDetectorStars, dtype=bool) * detector["id"]
249 ones_like = np.ones(nDetectorStars)
250 zeros_like = np.zeros(nDetectorStars, dtype=bool)
252 x, y = detWcs.skyToPixelArray(
253 visitStarRas[detectorIndices], visitStarDecs[detectorIndices], degrees=True
254 )
256 origWcs = (cls.inputVisitSummary[v][cls.inputVisitSummary[v]["id"] == detectorId])[0].getWcs()
257 inputRa, inputDec = origWcs.pixelToSkyArray(x, y, degrees=True)
259 sourceDict = {}
260 sourceDict["detector"] = detectorArray
261 sourceDict["sourceId"] = visitStarIds[detectorIndices]
262 sourceDict["x"] = x
263 sourceDict["y"] = y
264 sourceDict["xErr"] = 1e-3 * ones_like
265 sourceDict["yErr"] = 1e-3 * ones_like
266 sourceDict["inputRA"] = inputRa
267 sourceDict["inputDec"] = inputDec
268 sourceDict["trueRA"] = visitStarRas[detectorIndices]
269 sourceDict["trueDec"] = visitStarDecs[detectorIndices]
270 for key in ["apFlux_12_0_flux", "apFlux_12_0_instFlux", "ixx", "iyy"]:
271 sourceDict[key] = ones_like
272 for key in [
273 "pixelFlags_edge",
274 "pixelFlags_saturated",
275 "pixelFlags_interpolatedCenter",
276 "pixelFlags_interpolated",
277 "pixelFlags_crCenter",
278 "pixelFlags_bad",
279 "hsmPsfMoments_flag",
280 "apFlux_12_0_flag",
281 "extendedness",
282 "parentSourceId",
283 "deblend_nChild",
284 "ixy",
285 ]:
286 sourceDict[key] = zeros_like
287 sourceDict["apFlux_12_0_instFluxErr"] = 1e-3 * ones_like
288 sourceDict["detect_isPrimary"] = ones_like.astype(bool)
290 sourceCat = pd.DataFrame(sourceDict)
291 sourceCats.append(sourceCat)
293 visitSourceTable = pd.concat(sourceCats)
295 inputCatalogRef = InMemoryDatasetHandle(
296 visitSourceTable, storageClass="DataFrame", dataId={"visit": visit}
297 )
299 inputCatalogRefs.append(inputCatalogRef)
301 return inputCatalogRefs
303 @classmethod
304 def _make_wcs(cls, model, inputVisitSummaries):
305 """Make a `lsst.afw.geom.SkyWcs` from given model parameters
307 Parameters
308 ----------
309 model : `dict`
310 Dictionary with WCS model parameters
311 inputVisitSummaries : `list` of `lsst.afw.table.ExposureCatalog`
312 Visit summary catalogs
313 Returns
314 -------
315 catalogs : `dict` of `lsst.afw.table.ExposureCatalog`
316 Visit summary catalogs with WCS set to input model
317 """
319 # Pixels will need to be rescaled before going into the mappings
320 xscale = inputVisitSummaries[0][0]["bbox_max_x"] - inputVisitSummaries[0][0]["bbox_min_x"]
321 yscale = inputVisitSummaries[0][0]["bbox_max_y"] - inputVisitSummaries[0][0]["bbox_min_y"]
323 catalogs = {}
324 schema = lsst.afw.table.ExposureTable.makeMinimalSchema()
325 schema.addField("visit", type="L", doc="Visit number")
326 for visitSum in inputVisitSummaries:
327 visit = visitSum[0]["visit"]
328 visitMapName = f"{visit}/poly"
329 visitModel = model[visitMapName]
331 catalog = lsst.afw.table.ExposureCatalog(schema)
332 catalog.resize(len(visitSum))
333 catalog["visit"] = visit
335 raDec = visitSum[0].getVisitInfo().getBoresightRaDec()
337 visitMapType = visitModel["Type"]
338 visitDict = {"Type": visitMapType}
339 if visitMapType == "Poly":
340 mapCoefficients = visitModel["XPoly"]["Coefficients"] + visitModel["YPoly"]["Coefficients"]
341 visitDict["Coefficients"] = mapCoefficients
343 for d, detector in enumerate(visitSum):
344 detectorId = detector["id"]
345 detectorMapName = f"HSC/{detectorId}/poly"
346 detectorModel = model[detectorMapName]
348 detectorMapType = detectorModel["Type"]
349 mapDict = {detectorMapName: {"Type": detectorMapType}, visitMapName: visitDict}
350 if detectorMapType == "Poly":
351 mapCoefficients = (
352 detectorModel["XPoly"]["Coefficients"] + detectorModel["YPoly"]["Coefficients"]
353 )
354 mapDict[detectorMapName]["Coefficients"] = mapCoefficients
356 outWCS = cls.task._make_afw_wcs(
357 mapDict,
358 raDec.getRa(),
359 raDec.getDec(),
360 doNormalizePixels=True,
361 xScale=xscale,
362 yScale=yscale,
363 )
364 catalog[d].setId(detectorId)
365 catalog[d].setWcs(outWCS)
367 catalog.sort()
368 catalogs[visit] = catalog
370 return catalogs
372 def test_get_exposure_info(self):
373 """Test that information for input exposures is as expected and that
374 the WCS in the class object gives approximately the same results as the
375 input `lsst.afw.geom.SkyWcs`.
376 """
378 # The total number of extensions is the number of detectors for each
379 # visit plus one for the reference catalog
380 totalExtensions = sum([len(visSum) for visSum in self.inputVisitSummary]) + 1
382 self.assertEqual(totalExtensions, len(self.extensionInfo.visit))
384 taskVisits = set(self.extensionInfo.visit)
385 self.assertEqual(taskVisits, set(self.testVisits + [-1]))
387 xx = np.linspace(0, 2000, 3)
388 yy = np.linspace(0, 4000, 6)
389 xgrid, ygrid = np.meshgrid(xx, yy)
390 for visSum in self.inputVisitSummary:
391 visit = visSum[0]["visit"]
392 for detectorInfo in visSum:
393 detector = detectorInfo["id"]
394 extensionIndex = np.flatnonzero(
395 (self.extensionInfo.visit == visit) & (self.extensionInfo.detector == detector)
396 )[0]
397 fitWcs = self.extensionInfo.wcs[extensionIndex]
398 calexpWcs = detectorInfo.getWcs()
400 tanPlaneXY = np.array([fitWcs.toWorld(x, y) for (x, y) in zip(xgrid.ravel(), ygrid.ravel())])
402 calexpra, calexpdec = calexpWcs.pixelToSkyArray(xgrid.ravel(), ygrid.ravel(), degrees=True)
404 tangentPoint = calexpWcs.pixelToSky(
405 calexpWcs.getPixelOrigin().getX(), calexpWcs.getPixelOrigin().getY()
406 )
407 cdMatrix = afwgeom.makeCdMatrix(1.0 * lsst.geom.degrees, 0 * lsst.geom.degrees, True)
408 iwcToSkyWcs = afwgeom.makeSkyWcs(lsst.geom.Point2D(0, 0), tangentPoint, cdMatrix)
409 newRAdeg, newDecdeg = iwcToSkyWcs.pixelToSkyArray(
410 tanPlaneXY[:, 0], tanPlaneXY[:, 1], degrees=True
411 )
413 np.testing.assert_allclose(calexpra, newRAdeg)
414 np.testing.assert_allclose(calexpdec, newDecdeg)
416 def test_refCatLoader(self):
417 """Test that we can load objects from refCat"""
419 tmpAssociations = wcsfit.FoFClass(
420 self.fields,
421 [self.instrument],
422 self.exposuresHelper,
423 [self.fieldRadius.asDegrees()],
424 (self.task.config.matchRadius * u.arcsec).to(u.degree).value,
425 )
427 self.task._load_refcat(
428 tmpAssociations,
429 self.refObjectLoader,
430 self.fieldCenter,
431 self.fieldRadius,
432 self.extensionInfo,
433 epoch=2015,
434 )
436 # We have only loaded one catalog, so getting the 'matches' should just
437 # return the same objects we put in, except some random objects that
438 # are too close together.
439 tmpAssociations.sortMatches(self.fieldNumber, minMatches=1)
441 nMatches = (np.array(tmpAssociations.sequence) == 0).sum()
443 self.assertLessEqual(nMatches, self.nStars)
444 self.assertGreater(nMatches, self.nStars * 0.9)
446 def test_load_catalogs_and_associate(self):
447 tmpAssociations = wcsfit.FoFClass(
448 self.fields,
449 [self.instrument],
450 self.exposuresHelper,
451 [self.fieldRadius.asDegrees()],
452 (self.task.config.matchRadius * u.arcsec).to(u.degree).value,
453 )
454 self.task._load_catalogs_and_associate(tmpAssociations, self.inputCatalogRefs, self.extensionInfo)
456 tmpAssociations.sortMatches(self.fieldNumber, minMatches=2)
458 matchIds = []
459 correctMatches = []
460 for s, e, o in zip(tmpAssociations.sequence, tmpAssociations.extn, tmpAssociations.obj):
461 objVisitInd = self.extensionInfo.visitIndex[e]
462 objDet = self.extensionInfo.detector[e]
463 ExtnInds = self.inputCatalogRefs[objVisitInd].get()["detector"] == objDet
464 objInfo = self.inputCatalogRefs[objVisitInd].get()[ExtnInds].iloc[o]
465 if s == 0:
466 if len(matchIds) > 0:
467 correctMatches.append(len(set(matchIds)) == 1)
468 matchIds = []
470 matchIds.append(objInfo["sourceId"])
472 # A few matches may incorrectly associate sources because of the random
473 # positions
474 self.assertGreater(sum(correctMatches), len(correctMatches) * 0.95)
476 def test_make_outputs(self):
477 """Test that the run method recovers the input model parameters."""
478 for v, visit in enumerate(self.testVisits):
479 visitSummary = self.inputVisitSummary[v]
480 outputWcsCatalog = self.outputs.outputWCSs[visit]
481 visitSources = self.inputCatalogRefs[v].get()
482 for d, detectorRow in enumerate(visitSummary):
483 detectorId = detectorRow["id"]
484 fitwcs = outputWcsCatalog[d].getWcs()
485 detSources = visitSources[visitSources["detector"] == detectorId]
486 fitRA, fitDec = fitwcs.pixelToSkyArray(detSources["x"], detSources["y"], degrees=True)
487 dRA = fitRA - detSources["trueRA"]
488 dDec = fitDec - detSources["trueDec"]
489 # Check that input coordinates match the output coordinates
490 self.assertAlmostEqual(np.mean(dRA), 0)
491 self.assertAlmostEqual(np.std(dRA), 0)
492 self.assertAlmostEqual(np.mean(dDec), 0)
493 self.assertAlmostEqual(np.std(dDec), 0)
495 def test_compute_model_params(self):
496 """Test the optional model parameters and covariance output."""
497 modelParams = pd.DataFrame(self.outputs.modelParams)
498 # Check that DataFrame is the expected size.
499 shape = modelParams.shape
500 self.assertEqual(shape[0] + 4, shape[1])
501 # Check that covariance matrix is symmetric.
502 covariance = (modelParams.iloc[:, 4:]).to_numpy()
503 np.testing.assert_allclose(covariance, covariance.T, atol=1e-18)
505 def test_run(self):
506 """Test that run method recovers the input model parameters"""
507 outputMaps = self.outputs.fitModel.mapCollection.getParamDict()
509 for v, visit in enumerate(self.testVisits):
510 visitSummary = self.inputVisitSummary[v]
511 visitMapName = f"{visit}/poly"
513 origModel = self.trueModel[visitMapName]
514 if origModel["Type"] != "Identity":
515 fitModel = outputMaps[visitMapName]
516 origXPoly = origModel["XPoly"]["Coefficients"]
517 origYPoly = origModel["YPoly"]["Coefficients"]
518 fitXPoly = fitModel[: len(origXPoly)]
519 fitYPoly = fitModel[len(origXPoly) :]
521 absDiffX = abs(fitXPoly - origXPoly)
522 absDiffY = abs(fitYPoly - origYPoly)
523 # Check that input visit model matches fit
524 np.testing.assert_array_less(absDiffX, 1e-6)
525 np.testing.assert_array_less(absDiffY, 1e-6)
526 for d, detectorRow in enumerate(visitSummary):
527 detectorId = detectorRow["id"]
528 detectorMapName = f"HSC/{detectorId}/poly"
529 origModel = self.trueModel[detectorMapName]
530 if (origModel["Type"] != "Identity") and (v == 0):
531 fitModel = outputMaps[detectorMapName]
532 origXPoly = origModel["XPoly"]["Coefficients"]
533 origYPoly = origModel["YPoly"]["Coefficients"]
534 fitXPoly = fitModel[: len(origXPoly)]
535 fitYPoly = fitModel[len(origXPoly) :]
536 absDiffX = abs(fitXPoly - origXPoly)
537 absDiffY = abs(fitYPoly - origYPoly)
538 # Check that input detector model matches fit
539 np.testing.assert_array_less(absDiffX, 1e-7)
540 np.testing.assert_array_less(absDiffY, 1e-7)
542 def test_missingWcs(self):
543 """Test that task does not fail when the input WCS is None for one
544 extension and that the fit WCS for that extension returns a finite
545 result.
546 """
547 inputVisitSummary = self.inputVisitSummary.copy()
548 # Set one WCS to be None
549 testVisit = 0
550 testDetector = 20
551 inputVisitSummary[testVisit][testDetector].setWcs(None)
553 outputs = self.task.run(
554 self.inputCatalogRefs,
555 inputVisitSummary,
556 instrumentName=self.instrumentName,
557 refEpoch=self.refEpoch,
558 refObjectLoader=self.refObjectLoader,
559 )
561 # Check that the fit WCS for the extension with input WCS=None returns
562 # finite sky values.
563 testWcs = outputs.outputWCSs[self.testVisits[testVisit]][testDetector].getWcs()
564 testSky = testWcs.pixelToSky(0, 0)
565 self.assertTrue(testSky.isFinite())
568def setup_module(module):
569 lsst.utils.tests.init()
572if __name__ == "__main__": 572 ↛ 573line 572 didn't jump to line 573, because the condition on line 572 was never true
573 lsst.utils.tests.init()
574 unittest.main()