Exemplo n.º 1
0
    def testTransaction(self):
        butler = Butler(self.tmpConfigFile, run="ingest")
        datasetTypeName = "test_metric"
        dimensions = butler.registry.dimensions.extract(
            ["instrument", "visit"])
        dimensionEntries = (("instrument", {
            "instrument": "DummyCam"
        }), ("physical_filter", {
            "instrument": "DummyCam",
            "name": "d-r",
            "abstract_filter": "R"
        }), ("visit", {
            "instrument": "DummyCam",
            "id": 42,
            "name": "fortytwo",
            "physical_filter": "d-r"
        }))
        storageClass = self.storageClassFactory.getStorageClass(
            "StructuredData")
        metric = makeExampleMetrics()
        dataId = {"instrument": "DummyCam", "visit": 42}
        with self.assertRaises(TransactionTestError):
            with butler.transaction():
                # Create and register a DatasetType
                datasetType = self.addDatasetType(datasetTypeName, dimensions,
                                                  storageClass,
                                                  butler.registry)
                # Add needed Dimensions
                for args in dimensionEntries:
                    butler.registry.insertDimensionData(*args)
                # Store a dataset
                ref = butler.put(metric, datasetTypeName, dataId)
                self.assertIsInstance(ref, DatasetRef)
                # Test getDirect
                metricOut = butler.getDirect(ref)
                self.assertEqual(metric, metricOut)
                # Test get
                metricOut = butler.get(datasetTypeName, dataId)
                self.assertEqual(metric, metricOut)
                # Check we can get components
                self.assertGetComponents(butler, ref,
                                         ("summary", "data", "output"), metric)
                raise TransactionTestError(
                    "This should roll back the entire transaction")

        with self.assertRaises(KeyError):
            butler.registry.getDatasetType(datasetTypeName)
        with self.assertRaises(LookupError):
            butler.registry.expandDataId(dataId)
        # Should raise KeyError for missing DatasetType
        with self.assertRaises(KeyError):
            butler.get(datasetTypeName, dataId)
        # Also check explicitly if Dataset entry is missing
        self.assertIsNone(
            butler.registry.find(butler.collection, datasetType, dataId))
        # Direct retrieval should not find the file in the Datastore
        with self.assertRaises(FileNotFoundError):
            butler.getDirect(ref)
Exemplo n.º 2
0
    def testBasicPutGet(self):
        butler = Butler(self.configFile)
        # Create and register a DatasetType
        datasetTypeName = "test_metric"
        dataUnits = ("Camera", "Visit")
        storageClass = self.storageClassFactory.getStorageClass(
            "StructuredData")
        self.registerDatasetTypes(datasetTypeName, dataUnits, storageClass,
                                  butler.registry)

        # Create and store a dataset
        metric = makeExampleMetrics()
        dataId = {"camera": "DummyCam", "visit": 42}
        ref = butler.put(metric, datasetTypeName, dataId)
        self.assertIsInstance(ref, DatasetRef)
        # Test getDirect
        metricOut = butler.getDirect(ref)
        self.assertEqual(metric, metricOut)
        # Test get
        metricOut = butler.get(datasetTypeName, dataId)
        self.assertEqual(metric, metricOut)

        # Check we can get components
        self.assertGetComponents(butler, datasetTypeName, dataId,
                                 ("summary", "data", "output"), metric)
Exemplo n.º 3
0
class TestHipsOutputs(unittest.TestCase, MockCheckMixin):
    """Check that HIPS outputs are as expected."""
    def setUp(self):
        self.butler = Butler(os.path.join(getPackageDir("ci_hsc_gen3"),
                                          "DATA"),
                             instrument="HSC",
                             skymap="discrete/ci_hsc",
                             writeable=False,
                             collections=["HSC/runs/ci_hsc_hips"])
        self.skip_mock()
        self._bands = ['r', 'i']

    def test_hips_exist(self):
        """Test that the HIPS images exist and are readable."""
        for band in self._bands:
            datasets = set(
                self.butler.registry.queryDatasets("deepCoadd_hpx", band=band))

            # There are 64 HIPS images for each band.
            self.assertEqual(len(datasets), 64)

            for dataset in datasets:
                self.assertTrue(self.butler.datastore.exists(dataset),
                                msg="File exists for deepCoadd_hpx")

            exp = self.butler.getDirect(list(datasets)[0])

            self.assertEqual(exp.wcs.getFitsMetadata()["CTYPE1"], "RA---HPX")
            self.assertEqual(exp.wcs.getFitsMetadata()["CTYPE2"], "DEC--HPX")
Exemplo n.º 4
0
    def testStrayLightIngest(self):
        """Ingested stray light files."""

        butler = Butler(self.root, run=self.outputRun)

        straylightDir = os.path.join(testDataDirectory, "hsc", "straylight")

        instrument = self.instrumentClass()

        # This will warn about lots of missing files
        with self.assertLogs(level="WARNING") as cm:
            instrument.ingestStrayLightData(butler,
                                            straylightDir,
                                            transfer="auto")

        collection = self.instrumentClass.makeCalibrationCollectionName()
        datasets = list(
            butler.registry.queryDatasetAssociations("yBackground",
                                                     collections=collection))
        # Should have at least one dataset and dataset+warnings = 112
        self.assertGreaterEqual(len(datasets), 1)
        self.assertEqual(
            len(datasets) + len(cm.output), len(instrument.getCamera()))

        # Ensure that we can read the first stray light file
        strayLight = butler.getDirect(datasets[0].ref)
        self.assertIsInstance(strayLight, SubaruStrayLightData)
Exemplo n.º 5
0
    def runPutGetTest(self, storageClass, datasetTypeName):
        butler = Butler(self.tmpConfigFile)

        # There will not be a collection yet
        collections = butler.registry.getAllCollections()
        self.assertEqual(collections, set())

        # Create and register a DatasetType
        dimensions = butler.registry.dimensions.extract(
            ["instrument", "visit"])

        datasetType = self.addDatasetType(datasetTypeName, dimensions,
                                          storageClass, butler.registry)

        # Add needed Dimensions
        butler.registry.addDimensionEntry("instrument",
                                          {"instrument": "DummyCamComp"})
        butler.registry.addDimensionEntry("physical_filter", {
            "instrument": "DummyCamComp",
            "physical_filter": "d-r"
        })
        butler.registry.addDimensionEntry("visit", {
            "instrument": "DummyCamComp",
            "visit": 423,
            "physical_filter": "d-r"
        })

        # Create and store a dataset
        metric = makeExampleMetrics()
        dataId = {"instrument": "DummyCamComp", "visit": 423}

        # Create a DatasetRef for put
        refIn = DatasetRef(datasetType, dataId, id=None)

        # Put with a preexisting id should fail
        with self.assertRaises(ValueError):
            butler.put(metric, DatasetRef(datasetType, dataId, id=100))

        # Put and remove the dataset once as a DatasetRef, once as a dataId,
        # and once with a DatasetType
        for args in ((refIn, ), (datasetTypeName, dataId), (datasetType,
                                                            dataId)):
            with self.subTest(args=args):
                ref = butler.put(metric, *args)
                self.assertIsInstance(ref, DatasetRef)

                # Test getDirect
                metricOut = butler.getDirect(ref)
                self.assertEqual(metric, metricOut)
                # Test get
                metricOut = butler.get(ref.datasetType.name, dataId)
                self.assertEqual(metric, metricOut)
                # Test get with a datasetRef
                metricOut = butler.get(ref)
                self.assertEqual(metric, metricOut)

                # Check we can get components
                if storageClass.isComposite():
                    self.assertGetComponents(butler, ref,
                                             ("summary", "data", "output"),
                                             metric)

                # Remove from collection only; after that we shouldn't be able
                # to find it unless we use the dataset_id.
                butler.remove(*args, delete=False)
                with self.assertRaises(LookupError):
                    butler.datasetExists(*args)
                # If we use the output ref with the dataset_id, we should
                # still be able to load it with getDirect().
                self.assertEqual(metric, butler.getDirect(ref))

                # Reinsert into collection, then delete from Datastore *and*
                # remove from collection.
                butler.registry.associate(butler.collection, [ref])
                butler.remove(*args)
                # Lookup with original args should still fail.
                with self.assertRaises(LookupError):
                    butler.datasetExists(*args)
                # Now getDirect() should fail, too.
                with self.assertRaises(FileNotFoundError):
                    butler.getDirect(ref)
                # Registry still knows about it, if we use the dataset_id.
                self.assertEqual(butler.registry.getDataset(ref.id), ref)

                # Put again, then remove completely (this generates a new
                # dataset record in registry, with a new ID - the old one
                # still exists but it is not in any collection so we don't
                # care).
                ref = butler.put(metric, *args)
                butler.remove(*args, remember=False)
                # Lookup with original args should still fail.
                with self.assertRaises(LookupError):
                    butler.datasetExists(*args)
                # getDirect() should still fail.
                with self.assertRaises(FileNotFoundError):
                    butler.getDirect(ref)
                # Registry shouldn't be able to find it by dataset_id anymore.
                self.assertIsNone(butler.registry.getDataset(ref.id))

        # Put the dataset again, since the last thing we did was remove it.
        ref = butler.put(metric, refIn)

        # Get with parameters
        stop = 4
        sliced = butler.get(ref, parameters={"slice": slice(stop)})
        self.assertNotEqual(metric, sliced)
        self.assertEqual(metric.summary, sliced.summary)
        self.assertEqual(metric.output, sliced.output)
        self.assertEqual(metric.data[:stop], sliced.data)

        # Combining a DatasetRef with a dataId should fail
        with self.assertRaises(ValueError):
            butler.get(ref, dataId)
        # Getting with an explicit ref should fail if the id doesn't match
        with self.assertRaises(ValueError):
            butler.get(DatasetRef(ref.datasetType, ref.dataId, id=101))

        # Getting a dataset with unknown parameters should fail
        with self.assertRaises(KeyError):
            butler.get(ref, parameters={"unsupported": True})

        # Check we have a collection
        collections = butler.registry.getAllCollections()
        self.assertEqual(collections, {
            "ingest",
        })
Exemplo n.º 6
0
def makeDiscreteSkyMap(repo,
                       config_file,
                       collections,
                       instrument,
                       skymap_id='discrete',
                       old_skymap_id=None):
    """Implements the command line interface `butler make-discrete-skymap` subcommand,
    should only be called by command line tools and unit test code that tests
    this function.

    Constructs a skymap from calibrated exposure in the butler repository

    Parameters
    ----------
    repo : `str`
        URI to the location to read the repo.
    config_file : `str` or `None`
        Path to a config file that contains overrides to the skymap config.
    collections : `list` [`str`]
        An expression specifying the collections to be searched (in order) when
        reading datasets, and optionally dataset type restrictions on them.
        At least one collection must be specified.  This is the collection
        with the calibrated exposures.
    instrument : `str`
        The name or fully-qualified class name of an instrument.
    skymap_id : `str`, optional
        The identifier of the skymap to save.  Default is 'discrete'.
    old_skymap_id : `str`, optional
        The identifer of the skymap to append to.  Must differ from
        ``skymap_id``.  Ignored unless ``config.doAppend=True``.
    """
    butler = Butler(repo, collections=collections, writeable=True)
    instr = getInstrument(instrument, butler.registry)
    config = MakeDiscreteSkyMapConfig()
    instr.applyConfigOverrides(MakeDiscreteSkyMapTask._DefaultName, config)

    if config_file is not None:
        config.load(config_file)
    # The coaddName for a SkyMap is only relevant in Gen2, and we completely
    # ignore it here; once Gen2 is gone it can be removed.
    oldSkyMap = None
    if config.doAppend:
        if old_skymap_id is None:
            raise ValueError(
                "old_skymap_id must be provided if config.doAppend is True.")
        dataId = {'skymap': old_skymap_id}
        try:
            oldSkyMap = butler.get(BaseSkyMap.SKYMAP_DATASET_TYPE_NAME,
                                   collections=collections,
                                   dataId=dataId)
        except LookupError as e:
            msg = (
                f"Could not find seed skymap with dataId {dataId} "
                f"in collections {collections} but doAppend is {config.doAppend}.  Aborting..."
            )
            raise LookupError(msg, *e.args[1:])

    datasets = butler.registry.queryDatasets('calexp', collections=collections)
    wcs_md_tuple_list = [(butler.getDirect('calexp.metadata', ref),
                          butler.getDirect('calexp.wcs', ref))
                         for ref in datasets]
    task = MakeDiscreteSkyMapTask(config=config)
    result = task.run(wcs_md_tuple_list, oldSkyMap)
    result.skyMap.register(skymap_id, butler)
    butler.put(result.skyMap,
               BaseSkyMap.SKYMAP_DATASET_TYPE_NAME,
               dataId={'skymap': skymap_id},
               run=BaseSkyMap.SKYMAP_RUN_COLLECTION_NAME)
Exemplo n.º 7
0
    def checkRepo(self, files=None):
        # Test amp parameter implementation for the LSST raw formatter.  This
        # is the same for all instruments, so repeating it in other test cases
        # is wasteful.
        butler = Butler(self.root, run=self.outputRun)
        ref = butler.registry.findDataset("raw", self.dataIds[0])
        full_assembled = butler.getDirect(ref)
        unassembled_detector = self.instrumentClass().getCamera()[
            ref.dataId["detector"]]
        assembled_detector = full_assembled.getDetector()
        for unassembled_amp, assembled_amp in zip(unassembled_detector,
                                                  assembled_detector):
            # Check that we're testing what we think we're testing: these
            # amps should differ in assembly state (offsets, flips), and they
            # _may_ differ in fundamental geometry if we had to patch the
            # overscan region sizes.
            comparison = unassembled_amp.compareGeometry(assembled_amp)
            self.assertTrue(comparison
                            & AmplifierGeometryComparison.ASSEMBLY_DIFFERS)
            assembled_subimage = butler.getDirect(
                ref, parameters={"amp": assembled_amp})
            unassembled_subimage = butler.getDirect(
                ref, parameters={"amp": unassembled_amp.getName()})
            self.assertEqual(len(assembled_subimage.getDetector()), 1)
            self.assertEqual(len(unassembled_subimage.getDetector()), 1)
            self.assertEqual(len(assembled_subimage.getDetector()), 1)
            self.assertEqual(len(unassembled_subimage.getDetector()), 1)
            self.assertImagesEqual(
                assembled_subimage.image,
                full_assembled.image[assembled_amp.getRawBBox()])
            self.assertImagesEqual(
                unassembled_subimage.image,
                flipImage(
                    full_assembled.image[assembled_amp.getRawBBox()],
                    flipLR=unassembled_amp.getRawFlipX(),
                    flipTB=unassembled_amp.getRawFlipY(),
                ),
            )
            self.assertAmplifiersEqual(assembled_subimage.getDetector()[0],
                                       assembled_amp)
            if comparison & comparison.REGIONS_DIFFER:
                # We needed to patch overscans, but unassembled_amp (which
                # comes straight from the camera) won't have those patches, so
                # we can't compare it to the amp attached to
                # unassembled_subimage (which does have those patches).
                comparison2 = unassembled_subimage.getDetector(
                )[0].compareGeometry(unassembled_amp)

                self.assertTrue(comparison2
                                & AmplifierGeometryComparison.REGIONS_DIFFER)
                # ...and that unassembled_subimage's amp has the same regions
                # (after accounting for assembly/orientation) as assembled_amp.
                comparison3 = unassembled_subimage.getDetector(
                )[0].compareGeometry(assembled_amp)
                self.assertTrue(comparison3
                                & AmplifierGeometryComparison.ASSEMBLY_DIFFERS)
                self.assertFalse(comparison3
                                 & AmplifierGeometryComparison.REGIONS_DIFFER)
            else:
                self.assertAmplifiersEqual(
                    unassembled_subimage.getDetector()[0], unassembled_amp)
Exemplo n.º 8
0
    def runPutGetTest(self, storageClass, datasetTypeName):
        butler = Butler(self.tmpConfigFile, run="ingest")

        # There will not be a collection yet
        collections = butler.registry.getAllCollections()
        self.assertEqual(collections, set())

        # Create and register a DatasetType
        dimensions = butler.registry.dimensions.extract(
            ["instrument", "visit"])

        datasetType = self.addDatasetType(datasetTypeName, dimensions,
                                          storageClass, butler.registry)

        # Add needed Dimensions
        butler.registry.insertDimensionData("instrument",
                                            {"name": "DummyCamComp"})
        butler.registry.insertDimensionData("physical_filter", {
            "instrument": "DummyCamComp",
            "name": "d-r",
            "abstract_filter": "R"
        })
        butler.registry.insertDimensionData(
            "visit", {
                "instrument": "DummyCamComp",
                "id": 423,
                "name": "fourtwentythree",
                "physical_filter": "d-r"
            })

        # Create and store a dataset
        metric = makeExampleMetrics()
        dataId = {"instrument": "DummyCamComp", "visit": 423}

        # Create a DatasetRef for put
        refIn = DatasetRef(datasetType, dataId, id=None)

        # Put with a preexisting id should fail
        with self.assertRaises(ValueError):
            butler.put(metric, DatasetRef(datasetType, dataId, id=100))

        # Put and remove the dataset once as a DatasetRef, once as a dataId,
        # and once with a DatasetType
        for args in ((refIn, ), (datasetTypeName, dataId), (datasetType,
                                                            dataId)):
            with self.subTest(args=args):
                ref = butler.put(metric, *args)
                self.assertIsInstance(ref, DatasetRef)

                # Test getDirect
                metricOut = butler.getDirect(ref)
                self.assertEqual(metric, metricOut)
                # Test get
                metricOut = butler.get(ref.datasetType.name, dataId)
                self.assertEqual(metric, metricOut)
                # Test get with a datasetRef
                metricOut = butler.get(ref)
                self.assertEqual(metric, metricOut)
                # Test getDeferred with dataId
                metricOut = butler.getDeferred(ref.datasetType.name,
                                               dataId).get()
                self.assertEqual(metric, metricOut)
                # Test getDeferred with a datasetRef
                metricOut = butler.getDeferred(ref).get()
                self.assertEqual(metric, metricOut)

                # Check we can get components
                if storageClass.isComposite():
                    self.assertGetComponents(butler, ref,
                                             ("summary", "data", "output"),
                                             metric)

                # Remove from collection only; after that we shouldn't be able
                # to find it unless we use the dataset_id.
                butler.remove(*args, delete=False)
                with self.assertRaises(LookupError):
                    butler.datasetExists(*args)
                # If we use the output ref with the dataset_id, we should
                # still be able to load it with getDirect().
                self.assertEqual(metric, butler.getDirect(ref))

                # Reinsert into collection, then delete from Datastore *and*
                # remove from collection.
                butler.registry.associate(butler.collection, [ref])
                butler.remove(*args)
                # Lookup with original args should still fail.
                with self.assertRaises(LookupError):
                    butler.datasetExists(*args)
                # Now getDirect() should fail, too.
                with self.assertRaises(FileNotFoundError):
                    butler.getDirect(ref)
                # Registry still knows about it, if we use the dataset_id.
                self.assertEqual(butler.registry.getDataset(ref.id), ref)

                # Put again, then remove completely (this generates a new
                # dataset record in registry, with a new ID - the old one
                # still exists but it is not in any collection so we don't
                # care).
                ref = butler.put(metric, *args)
                butler.remove(*args, remember=False)
                # Lookup with original args should still fail.
                with self.assertRaises(LookupError):
                    butler.datasetExists(*args)
                # getDirect() should still fail.
                with self.assertRaises(FileNotFoundError):
                    butler.getDirect(ref)
                # Registry shouldn't be able to find it by dataset_id anymore.
                self.assertIsNone(butler.registry.getDataset(ref.id))

        # Put the dataset again, since the last thing we did was remove it.
        ref = butler.put(metric, refIn)

        # Get with parameters
        stop = 4
        sliced = butler.get(ref, parameters={"slice": slice(stop)})
        self.assertNotEqual(metric, sliced)
        self.assertEqual(metric.summary, sliced.summary)
        self.assertEqual(metric.output, sliced.output)
        self.assertEqual(metric.data[:stop], sliced.data)
        # getDeferred with parameters
        sliced = butler.getDeferred(ref, parameters={
            "slice": slice(stop)
        }).get()
        self.assertNotEqual(metric, sliced)
        self.assertEqual(metric.summary, sliced.summary)
        self.assertEqual(metric.output, sliced.output)
        self.assertEqual(metric.data[:stop], sliced.data)
        # getDeferred with deferred parameters
        sliced = butler.getDeferred(ref).get(parameters={"slice": slice(stop)})
        self.assertNotEqual(metric, sliced)
        self.assertEqual(metric.summary, sliced.summary)
        self.assertEqual(metric.output, sliced.output)
        self.assertEqual(metric.data[:stop], sliced.data)

        if storageClass.isComposite():
            # Delete one component and check that the other components
            # can still be retrieved
            metricOut = butler.get(ref.datasetType.name, dataId)
            compNameS = DatasetType.nameWithComponent(datasetTypeName,
                                                      "summary")
            compNameD = DatasetType.nameWithComponent(datasetTypeName, "data")
            summary = butler.get(compNameS, dataId)
            self.assertEqual(summary, metric.summary)
            self.assertTrue(butler.datastore.exists(ref.components["summary"]))

            butler.remove(compNameS, dataId, remember=True)
            with self.assertRaises(LookupError):
                butler.datasetExists(compNameS, dataId)
            self.assertFalse(butler.datastore.exists(
                ref.components["summary"]))
            self.assertTrue(butler.datastore.exists(ref.components["data"]))
            data = butler.get(compNameD, dataId)
            self.assertEqual(data, metric.data)

        # Combining a DatasetRef with a dataId should fail
        with self.assertRaises(ValueError):
            butler.get(ref, dataId)
        # Getting with an explicit ref should fail if the id doesn't match
        with self.assertRaises(ValueError):
            butler.get(DatasetRef(ref.datasetType, ref.dataId, id=101))

        # Getting a dataset with unknown parameters should fail
        with self.assertRaises(KeyError):
            butler.get(ref, parameters={"unsupported": True})

        # Check we have a collection
        collections = butler.registry.getAllCollections()
        self.assertEqual(collections, {
            "ingest",
        })

        # Clean up to check that we can remove something that may have
        # already had a component removed
        butler.remove(ref.datasetType.name, dataId)

        # Add a dataset back in since some downstream tests require
        # something to be present
        ref = butler.put(metric, refIn)

        return butler

        # Construct a butler with no run or collection, but make it writeable.
        butler = Butler(self.tmpConfigFile, writeable=True)
        # Create and register a DatasetType
        dimensions = butler.registry.dimensions.extract(
            ["instrument", "visit"])
        datasetType = self.addDatasetType(
            "example", dimensions,
            self.storageClassFactory.getStorageClass("StructuredData"),
            butler.registry)
        # Add needed Dimensions
        butler.registry.insertDimensionData("instrument",
                                            {"name": "DummyCamComp"})
        butler.registry.insertDimensionData("physical_filter", {
            "instrument": "DummyCamComp",
            "name": "d-r",
            "abstract_filter": "R"
        })
        butler.registry.insertDimensionData(
            "visit", {
                "instrument": "DummyCamComp",
                "id": 423,
                "name": "fourtwentythree",
                "physical_filter": "d-r"
            })
        dataId = {"instrument": "DummyCamComp", "visit": 423}
        # Create dataset.
        metric = makeExampleMetrics()
        # Register a new run and put dataset.
        run = "deferred"
        butler.registry.registerRun(run)
        ref = butler.put(metric, datasetType, dataId, run=run)
        # Putting with no run should fail with TypeError.
        with self.assertRaises(TypeError):
            butler.put(metric, datasetType, dataId)
        # Dataset should exist.
        self.assertTrue(
            butler.datasetExists(datasetType, dataId, collection=run))
        # We should be able to get the dataset back, but with and without
        # a deferred dataset handle.
        self.assertEqual(metric, butler.get(datasetType,
                                            dataId,
                                            collection=run))
        self.assertEqual(
            metric,
            butler.getDeferred(datasetType, dataId, collection=run).get())
        # Trying to find the dataset without any collection is a TypeError.
        with self.assertRaises(TypeError):
            butler.datasetExists(datasetType, dataId)
        with self.assertRaises(TypeError):
            butler.get(datasetType, dataId)
        with self.assertRaises(TypeError):
            butler.remove(datasetType, dataId)
        # Associate the dataset with a different collection.
        butler.registry.associate("tagged", [ref])
        # Deleting the dataset from the new collection should make it findable
        # in the original collection but without a Datastore entry.
        butler.remove(datasetType, dataId, collection="tagged")
        self.assertFalse(
            butler.datasetExists(datasetType, dataId, collection=run))
Exemplo n.º 9
0
class TestValidateOutputs(unittest.TestCase, MockCheckMixin):
    """Check that ci_hsc_gen3 outputs are as expected."""
    def setUp(self):
        self.butler = Butler(os.path.join(getPackageDir("ci_hsc_gen3"),
                                          "DATA"),
                             instrument="HSC",
                             skymap="discrete/ci_hsc",
                             writeable=False,
                             collections=["HSC/runs/ci_hsc"])

        self._num_exposures = len(DATA_IDS)
        self._num_exposures_good_templates = 29
        self._num_visits = len({data_id["visit"] for data_id in DATA_IDS})
        self._num_tracts = 1
        self._num_patches = 1
        self._num_bands = len(
            {data_id["physical_filter"]
             for data_id in DATA_IDS})
        self._min_sources = 100

    def check_pipetasks(self, names, n_metadata, n_log):
        """Check general pipetask outputs (metadata, log, config).

        Parameters
        ----------
        names : `list` [`str`]
            Task label names.
        n_metadata : `int`
            Number of expected metadata quanta.
        n_log : `int`
            Number of expected log quanta
        """
        for name in names:
            self.check_datasets([f"{name}_config"], 1)
            self.check_datasets([f"{name}_metadata"], n_metadata)
            self.check_datasets([f"{name}_log"], n_log)

    def check_datasets(self,
                       dataset_types,
                       n_expected,
                       additional_checks=[],
                       **kwargs):
        """Check dataset existence, and run additional checks.

        Parameters
        ----------
        dataset_types : `list` [`str`]
            List of dataset types to check.
        n_expected : `int`
            Number of each dataset_type expected in repo.
        additional_checks : `list` [`func`], optional
            List of additional check functions to run on each dataset.
        **kwargs : `dict`, optional
            Additional keywords to send to ``additional_checks``.
        """
        for dataset_type in dataset_types:

            self.skip_mock(dataset_type)

            datasets = set(self.butler.registry.queryDatasets(dataset_type))

            self.assertEqual(len(datasets),
                             n_expected,
                             msg=f"Number of {dataset_type}")

            for dataset in datasets:
                self.assertTrue(self.butler.datastore.exists(dataset),
                                msg=f"File exists for {dataset}")

                if additional_checks:
                    data = self.butler.getDirect(dataset)
                    for additional_check in additional_checks:
                        additional_check(data, **kwargs)

    def check_sources(self,
                      source_dataset_types,
                      n_expected,
                      min_src,
                      additional_checks=[],
                      **kwargs):
        """Check that the source catalogs have enough sources and
        run additional checks.

        Parameters
        ----------
        dataset_types : `list` [`str`]
            List of dataset types to check.
        n_expected : `int`
            Number of each dataset_type expected in repo.
        min_src : `int`
            Minimum number of sources for each dataset.
        additional_checks : `list` [`func`], optional
            List of additional check functions to run on each dataset.
        **kwargs : `dict`, optional
            Additional keywords to send to ``additional_checks``.
        """
        for source_dataset_type in source_dataset_types:

            self.skip_mock(source_dataset_type)

            datasets = set(
                self.butler.registry.queryDatasets(source_dataset_type))

            self.assertEqual(len(datasets),
                             n_expected,
                             msg=f"Number of {source_dataset_type}")

            for dataset in datasets:
                catalog = self.butler.getDirect(dataset)
                self.assertGreater(len(catalog),
                                   min_src,
                                   msg=f"Number of sources in {dataset}")

                for additional_check in additional_checks:
                    additional_check(catalog, **kwargs)

    def test_raw(self):
        "Test existence of raw exposures." ""
        self.check_datasets(["raw"], self._num_exposures)

    def test_isr_characterize_calibrate(self):
        """Test existence of isr/calibration related files."""
        self.check_pipetasks(["isr", "characterizeImage", "calibrate"],
                             self._num_exposures, self._num_exposures)
        self.check_datasets([
            "postISRCCD", "icExp", "icExpBackground", "icSrc", "calexp",
            "calexpBackground"
        ], self._num_exposures)
        self.check_datasets(["icSrc_schema", "src_schema"], 1)
        self.check_sources(["src"],
                           self._num_exposures,
                           self._min_sources,
                           additional_checks=[
                               self.check_aperture_corrections,
                               self.check_psf_stars_and_flags
                           ])

    def test_source_tables(self):
        """Test existence of source tables."""
        self.check_pipetasks(
            ["writeRecalibratedSourceTable", "transformSourceTable"],
            self._num_exposures, self._num_exposures)
        self.check_pipetasks(["consolidateSourceTable"], self._num_visits,
                             self._num_visits)
        self.check_sources(["sourceTable"], self._num_exposures,
                           self._min_sources)
        self.check_sources(["sourceTable_visit"], self._num_visits,
                           self._min_sources)

    def test_visit_summary(self):
        """Test existence of visit summaries."""
        self.check_pipetasks(["consolidateVisitSummary"], self._num_visits,
                             self._num_visits)
        self.check_datasets(["visitSummary"], self._num_visits)

    def test_isolated_star_association(self):
        """Test existence of isolated star tables."""
        self.check_pipetasks(["isolatedStarAssociation"], self._num_tracts,
                             self._num_tracts)
        self.check_datasets(["isolated_star_cat", "isolated_star_sources"],
                            self._num_tracts)

    def test_finalize_characterization(self):
        """Test existence of finalized characterization outputs."""
        self.check_pipetasks(["finalizeCharacterization"], self._num_visits,
                             self._num_visits)
        self.check_datasets(
            ["finalized_psf_ap_corr_catalog", "finalized_src_table"],
            self._num_visits)

    def test_make_tables(self):
        """Test existence of ccd and visit tables."""
        self.check_pipetasks(["makeCcdVisitTable", "makeVisitTable"], 1, 1)
        self.check_datasets(["ccdVisitTable", "visitTable"], 1)

    def test_make_warp(self):
        """Test existence of warps."""
        self.check_pipetasks(["makeWarp"], self._num_visits, self._num_visits)
        self.check_datasets(["deepCoadd_directWarp"], self._num_visits)

    def test_assemble_coadd(self):
        """Test existence of coadds."""
        def check_bright_star_mask(coadd):
            mask = coadd.getMaskedImage().getMask()
            mask_val = mask.getPlaneBitMask("BRIGHT_OBJECT")
            num_bright = (mask.getArray() & mask_val).sum()
            self.assertGreater(num_bright,
                               0,
                               msg="Some pixels are masked as BRIGHT_OBJECT")

        def check_transmission_curves(coadd):
            self.assertTrue(coadd.getInfo().getTransmissionCurve() is not None,
                            msg="TransmissionCurves are attached to coadds")

        n_output = self._num_patches * self._num_bands
        self.check_pipetasks(["assembleCoadd"], n_output, n_output)
        self.check_datasets(["deepCoadd"],
                            n_output,
                            additional_checks=[
                                check_bright_star_mask,
                                check_transmission_curves
                            ])

    def test_healsparse_property_maps(self):
        """Test existence of healsparse property maps."""
        self.check_pipetasks(["healSparsePropertyMaps"],
                             self._num_tracts * self._num_bands,
                             self._num_tracts * self._num_bands)
        self.check_pipetasks(["consolidateHealSparsePropertyMaps"],
                             self._num_bands, self._num_bands)
        self.check_datasets([
            "deepCoadd_dcr_ddec_map_weighted_mean",
            "deepCoadd_dcr_dra_map_weighted_mean",
            "deepCoadd_dcr_e1_map_weighted_mean",
            "deepCoadd_dcr_e2_map_weighted_mean",
            "deepCoadd_exposure_time_map_sum",
            "deepCoadd_psf_e1_map_weighted_mean",
            "deepCoadd_psf_e2_map_weighted_mean",
            "deepCoadd_psf_maglim_map_weighted_mean",
            "deepCoadd_psf_size_map_weighted_mean",
            "deepCoadd_sky_background_map_weighted_mean",
            "deepCoadd_sky_noise_map_weighted_mean"
        ], self._num_tracts * self._num_bands)
        self.check_datasets([
            "deepCoadd_dcr_ddec_consolidated_map_weighted_mean",
            "deepCoadd_dcr_dra_consolidated_map_weighted_mean",
            "deepCoadd_dcr_e1_consolidated_map_weighted_mean",
            "deepCoadd_dcr_e2_consolidated_map_weighted_mean",
            "deepCoadd_exposure_time_consolidated_map_sum",
            "deepCoadd_psf_e1_consolidated_map_weighted_mean",
            "deepCoadd_psf_e2_consolidated_map_weighted_mean",
            "deepCoadd_psf_maglim_consolidated_map_weighted_mean",
            "deepCoadd_psf_size_consolidated_map_weighted_mean",
            "deepCoadd_sky_background_consolidated_map_weighted_mean",
            "deepCoadd_sky_noise_consolidated_map_weighted_mean"
        ], self._num_bands)

    def test_coadd_detection(self):
        """Test existence of coadd detection catalogs."""
        n_output = self._num_patches * self._num_bands
        self.check_pipetasks(["detection", "measure"], n_output, n_output)
        self.check_pipetasks(
            ["mergeDetections", "deblend", "mergeMeasurements"], 1, 1)
        self.check_datasets(
            ["deepCoadd_calexp", "deepCoadd_calexp_background"], n_output)
        self.check_datasets(["deepCoadd_calexp"],
                            self._num_patches * self._num_bands)
        self.check_sources(["deepCoadd_det", "deepCoadd_meas"], n_output,
                           self._min_sources)

        self.check_sources(["deepCoadd_deblendedCatalog"], self._num_patches,
                           self._min_sources)

        self.check_datasets(
            ["deepCoadd_scarletModelData"],
            self._num_patches,
        )

        def check_propagated_flags(catalog, **kwargs):
            self.assertTrue(
                "calib_psf_candidate" in catalog.schema,
                msg="calib_psf_candidate field exists in deepCoadd_meas catalog"
            )
            self.assertTrue(
                "calib_psf_used" in catalog.schema,
                msg="calib_psf_used field exists in deepCoadd_meas catalog")
            self.assertTrue(
                "calib_astrometry_used" in catalog.schema,
                msg=
                "calib_astrometry_used field exists in deepCoadd_meas catalog")
            self.assertTrue(
                "calib_photometry_used" in catalog.schema,
                msg=
                "calib_photometry_used field exists in deepCoadd_meas catalog")
            self.assertTrue(
                "calib_psf_reserved" in catalog.schema,
                msg="calib_psf_reserved field exists in deepCoadd_meas catalog"
            )

        def check_failed_children(catalog, **kwargs):
            children_failed = []
            for column in catalog.schema:
                if column.field.getName().startswith("merge_footprint"):
                    for parent in catalog.getChildren(0):
                        for child in catalog.getChildren(parent.getId()):
                            if child[column.key] != parent[column.key]:
                                children_failed.append(child.getId())

            self.assertEqual(
                len(children_failed),
                0,
                msg=
                f"merge_footprint from parent propagated to children {children_failed}"
            )

        def check_deepcoadd_stellar_fraction(catalog):
            # Check that at least 90% of the stars we used to model the PSF end
            # up classified as stars on the coadd.  We certainly need much more
            # purity than that to build good PSF models, but this should verify
            # that flag propagation, aperture correction, and extendendess are
            # all running and configured reasonably (but it may not be
            # sensitive enough to detect subtle bugs).
            # 2020-1-13: There is an issue with the PSF that was
            # identified in DM-28294 and will be fixed in DM-12058,
            # which affects scarlet i-band models. So we set the
            # minStellarFraction based on the deblender and band used.
            # TODO: Once DM-12058 is merged this band-aid can be removed.
            min_stellar_fraction = 0.9
            if "deblend_scarletFlux" in catalog.schema.getNames():
                min_stellar_fraction = 0.7
            self.check_psf_stars_and_flags(
                catalog,
                min_stellar_fraction=min_stellar_fraction,
                do_check_flags=False)

        self.check_sources(["deepCoadd_meas"],
                           n_output,
                           self._min_sources,
                           additional_checks=[
                               self.check_aperture_corrections,
                               check_propagated_flags, check_failed_children,
                               check_deepcoadd_stellar_fraction
                           ])

        self.check_sources(["deepCoadd_ref", "deepCoadd_mergeDet"],
                           self._num_patches, self._min_sources)

        self.check_datasets([
            "deepCoadd_det_schema", "deepCoadd_meas_schema",
            "deepCoadd_mergeDet_schema", "deepCoadd_peak_schema",
            "deepCoadd_ref_schema"
        ], 1)

    def test_object_tables(self):
        """Test existence of object tables."""
        self.check_pipetasks(["writeObjectTable", "transformObjectTable"],
                             self._num_patches, self._num_patches)
        self.check_pipetasks(["consolidateObjectTable"], self._num_tracts,
                             self._num_tracts)
        self.check_datasets(["deepCoadd_obj", "objectTable"],
                            self._num_patches)
        self.check_datasets(["objectTable_tract"], self._num_tracts)

    def test_forced_phot_ccd(self):
        """Test existence of forced photometry tables (sources)."""
        self.check_pipetasks(["forcedPhotCcd"], self._num_exposures,
                             self._num_exposures)
        self.check_sources(["forced_src"],
                           self._num_exposures,
                           self._min_sources,
                           additional_checks=[self.check_aperture_corrections])
        self.check_datasets(["forced_src_schema"], 1)

    def test_forced_phot_coadd(self):
        """Test existence of forced photometry tables (objects)."""
        n_output = self._num_patches * self._num_bands
        self.check_pipetasks(["forcedPhotCoadd"], n_output, n_output)
        self.check_sources(["deepCoadd_forced_src"],
                           n_output,
                           self._min_sources,
                           additional_checks=[self.check_aperture_corrections])

    def test_forced_phot_diffim(self):
        """Test existence of forced photometry tables (diffim)."""
        self.check_pipetasks([
            "forcedPhotDiffim", "forcedPhotCcdOnDiaObjects",
            "forcedPhotDiffOnDiaObjects"
        ], self._num_exposures, self._num_exposures)
        self.check_sources(["forced_diff", "forced_diff_diaObject"],
                           self._num_exposures_good_templates,
                           self._min_sources)
        self.check_datasets(
            ["forced_diff_schema", "forced_diff_diaObject_schema"], 1)

    def test_templates(self):
        """Test existence of templates."""
        self.check_pipetasks(["getTemplate"], self._num_exposures,
                             self._num_exposures)
        self.check_pipetasks(["templateGen", "selectGoodSeeingVisits"],
                             self._num_patches * self._num_bands,
                             self._num_patches * self._num_bands)
        self.check_datasets(["goodSeeingDiff_templateExp"],
                            self._num_exposures)
        self.check_datasets(["goodSeeingDiff_diaSrc_schema"], 1)

    def test_image_difference(self):
        """Test existence of image differences."""
        self.check_pipetasks(["imageDifference"], self._num_exposures,
                             self._num_exposures)
        self.check_datasets(["goodSeeingDiff_differenceExp"],
                            self._num_exposures_good_templates)
        self.check_datasets(["goodSeeingDiff_diaSrc_schema"], 1)

    def test_dia_source_tables(self):
        """Test existence of dia source tables."""
        self.check_pipetasks(["consolidateAssocDiaSourceTable"], 1, 1)
        self.check_pipetasks(["consolidateDiaSourceTable"], self._num_visits,
                             self._num_visits)
        self.check_sources(["diaSourceTable"], self._num_visits,
                           self._min_sources // 2)
        self.check_sources(["diaSourceTable_tract"], self._num_tracts,
                           self._min_sources // 2)

    def test_forced_source_tables(self):
        """Test existence of forces source tables."""
        self.check_pipetasks(
            ["writeForcedSourceTable", "writeForcedSourceOnDiaObjectTable"],
            self._num_exposures, self._num_exposures)
        self.check_pipetasks([
            "transformForcedSourceTable",
            "transformForcedSourceOnDiaObjectTable",
            "consolidateForcedSourceTable",
            "consolidateForcedSourceOnDiaObjectTable"
        ], 1, 1)
        self.check_sources(["forced_diff_diaObject"],
                           self._num_exposures_good_templates,
                           self._min_sources)
        # There are fewer forced sources
        self.check_sources(["forced_src_diaObject"], self._num_exposures,
                           self._min_sources // 4)
        self.check_datasets(
            ["forced_diff_diaObject_schema", "forced_src_diaObject_schema"], 1)

    def test_skymap(self):
        """Test existence of skymap."""
        self.check_datasets(["skyMap"], 1)

    def test_skycorr(self):
        """Test existence of skycorr."""
        self.check_pipetasks(["skyCorr"], self._num_visits, self._num_visits)
        self.check_datasets(["skyCorr"], self._num_exposures)

    def check_aperture_corrections(self, catalog, **kwargs):
        for alg in ("base_PsfFlux", "base_GaussianFlux"):
            self.assertTrue(f"{alg}_apCorr" in catalog.schema,
                            msg=f"{alg}_apCorr in schema")
            self.assertTrue(f"{alg}_apCorrErr" in catalog.schema,
                            msg=f"{alg}_apCorrErr in schema")
            self.assertTrue(f"{alg}_flag_apCorr" in catalog.schema,
                            msg=f"{alg}_flag_apCorr in schema")

    def check_psf_stars_and_flags(self,
                                  catalog,
                                  min_stellar_fraction=0.95,
                                  do_check_flags=True,
                                  **kwargs):
        primary = catalog["detect_isPrimary"]

        psf_stars_used = catalog["calib_psf_used"] & primary

        ext_stars = catalog["base_ClassificationExtendedness_value"] < 0.5
        self.assertGreater(
            (ext_stars & psf_stars_used).sum(),
            min_stellar_fraction * psf_stars_used.sum(),
            msg=
            f"At least {min_stellar_fraction} of PSF sources are classified as stars"
        )

        if do_check_flags:
            psf_stars_reserved = catalog["calib_psf_reserved"]
            psf_stars_candidate = catalog["calib_psf_candidate"]
            self.assertGreaterEqual(
                psf_stars_candidate.sum(),
                psf_stars_used.sum() + psf_stars_reserved.sum(),
                msg=
                "Number of candidate PSF stars >= sum of used and reserved stars"
            )
Exemplo n.º 10
0
class JobReporter:
    """A class for extracting metric values from a Gen 3 repository and
    repackaging them as Job objects.

    Parameters
    ----------
    repository : `str`
        Path to a Butler configuration YAML file or a directory containing one.
    collection : `str`
        Name of the collection to search for metric values.
    metrics_package : `str` or `None`
        If provided, the namespace by which to filter selected metrics.
    spec : `str`
        The level of specification to filter metrics by.
    dataset_name : `str`
        The name of the dataset to report to SQuaSH through the
        ``ci_dataset`` tag.
    """
    def __init__(self, repository, collection, metrics_package, spec,
                 dataset_name):
        # Hard coding verify_metrics as the packager for now.
        # It would be easy to pass this in as an argument, if necessary.
        self.metrics = MetricSet.load_metrics_package(
            package_name_or_path='verify_metrics', subset=metrics_package)
        self.butler = Butler(repository)
        self.registry = self.butler.registry
        self.spec = spec
        self.collection = collection
        self.dataset_name = dataset_name

    def run(self):
        """Collate job information.

        Returns
        -------
        jobs : `dict` [`str`, `lsst.verify.Job`]
            A mapping of `~lsst.verify.Job` objects, indexed by a string
            representation of their data ID.
        """
        jobs = {}
        for metric in self.metrics:
            dataset = f'metricvalue_{metric.package}_{metric.metric}'
            datasetRefs = set(
                self.registry.queryDatasets(dataset,
                                            collections=self.collection,
                                            findFirst=True))
            for ref in datasetRefs:
                # getDirect skips dataset resolution; ref is guaranteed to
                # be valid.
                m = self.butler.getDirect(ref)
                # make the name the same as what SQuaSH Expects
                m.metric_name = metric

                # queryDatasets guarantees ref.dataId.hasFull()
                dataId = ref.dataId.full.byName()
                key = make_key(ref)

                # For backward-compatibility with Gen 2 SQuaSH uploads
                pfilt = dataId.get('physical_filter')
                if not pfilt:
                    # Grab the physical filter associated with the abstract
                    # filter. In general there may be more than one. Take the
                    # shortest assuming it is the most generic.
                    pfilts = [
                        el.name for el in self.registry.queryDimensionRecords(
                            'physical_filter', dataId=ref.dataId)
                    ]
                    pfilt = min(pfilts, key=len)

                if key not in jobs.keys():
                    job_metadata = {
                        'filter': pfilt,
                        'butler_generation': 'Gen3',
                        'ci_dataset': self.dataset_name,
                    }
                    job_metadata.update(dataId)
                    # Get dataset_repo_url from repository somehow?
                    jobs[key] = Job(meta=job_metadata, metrics=self.metrics)
                jobs[key].measurements.insert(m)
        return jobs
Exemplo n.º 11
0
class TestCoaddOutputs(unittest.TestCase, MockCheckMixin):
    """Check that coadd outputs are as expected.

    Many tests here are ported from
    https://github.com/lsst/pipe_tasks/blob/
    fd7d5e23d3c71e5d440153bc4faae7de9d5918c5/tests/nopytest_test_coadds.py
    """
    def setUp(self):
        self.butler = Butler(os.path.join(getPackageDir("ci_hsc_gen3"), "DATA"),
                             instrument="HSC", skymap="discrete/ci_hsc",
                             writeable=False, collections=["HSC/runs/ci_hsc"])
        self.skip_mock()
        self._tract = 0
        self._patch = 69
        self._bands = ['r', 'i']

    def test_forced_id_names(self):
        """Test that forced photometry ID fields are named as expected
        (DM-8210).

        Specifically, coadd forced photometry should have only "id" and
        "parent" fields, while CCD forced photometry should have those,
        "objectId", and "parentObjectId".
        """
        coadd_schema = self.butler.get("deepCoadd_forced_src_schema").schema
        self.assertIn("id", coadd_schema)
        self.assertIn("parent", coadd_schema)
        self.assertNotIn("objectId", coadd_schema)
        self.assertNotIn("parentObjectId", coadd_schema)
        ccd_schema = self.butler.get("forced_src_schema").schema
        self.assertIn("id", ccd_schema)
        self.assertIn("parent", ccd_schema)
        self.assertIn("objectId", ccd_schema)
        self.assertIn("parentObjectId", ccd_schema)

    def test_alg_metadata_output(self):
        """Test that the algorithm metadata is persisted correctly
        from MeasureMergedCoaddSourcesTask.
        """
        for band in self._bands:
            cat = self.butler.get(
                "deepCoadd_meas",
                band=band,
                tract=self._tract,
                patch=self._patch
            )
            meta = cat.getMetadata()
            for circ_aperture_flux_radius in meta.getArray('BASE_CIRCULARAPERTUREFLUX_RADII'):
                self.assertIsInstance(circ_aperture_flux_radius, numbers.Number)
            # Each time the run method of a measurement task is executed,
            # algorithm metadata is appended to the algorithm metadata object.
            # Depending on how many times a measurement task is run,
            # a metadata entry may be a single value or multiple values.
            for n_offset in meta.getArray('NOISE_OFFSET'):
                self.assertIsInstance(n_offset, numbers.Number)
            for noise_src in meta.getArray('NOISE_SOURCE'):
                self.assertEqual(noise_src, 'measure')
            for noise_exp_id in meta.getArray('NOISE_EXPOSURE_ID'):
                self.assertIsInstance(noise_exp_id, numbers.Number)
            for noise_seed_mul in meta.getArray('NOISE_SEED_MULTIPLIER'):
                self.assertIsInstance(noise_seed_mul, numbers.Number)

    def test_schema_consistency(self):
        """Test that _schema catalogs are consistent with the data catalogs."""
        det_schema = self.butler.get("deepCoadd_det_schema").schema
        meas_schema = self.butler.get("deepCoadd_meas_schema").schema
        mergeDet_schema = self.butler.get("deepCoadd_mergeDet_schema").schema
        ref_schema = self.butler.get("deepCoadd_ref_schema").schema
        coadd_forced_schema = self.butler.get("deepCoadd_forced_src_schema").schema
        ccd_forced_schema = self.butler.get("forced_src_schema").schema
        for band in self._bands:
            det = self.butler.get("deepCoadd_det", band=band, tract=self._tract, patch=self._patch)
            self.assertEqual(det.schema, det_schema)
            mergeDet = self.butler.get("deepCoadd_mergeDet", band=band, tract=self._tract, patch=self._patch)
            self.assertEqual(mergeDet.schema, mergeDet_schema)
            meas = self.butler.get("deepCoadd_meas", band=band, tract=self._tract, patch=self._patch)
            self.assertEqual(meas.schema, meas_schema)
            ref = self.butler.get("deepCoadd_ref", band=band, tract=self._tract, patch=self._patch)
            self.assertEqual(ref.schema, ref_schema)
            coadd_forced_src = self.butler.get(
                "deepCoadd_forced_src",
                band=band,
                tract=self._tract,
                patch=self._patch
            )
            self.assertEqual(coadd_forced_src.schema, coadd_forced_schema)
        ccd_forced_src = self.butler.get(
            "forced_src",
            tract=self._tract,
            visit=DATA_IDS[0]["visit"],
            detector=DATA_IDS[0]["detector"]
        )
        self.assertEqual(ccd_forced_src.schema, ccd_forced_schema)

    def test_coadd_transmission_curves(self):
        """Test that coadded TransmissionCurves agree with the inputs."""
        wavelengths = np.linspace(4000, 7000, 10)
        n_object_test = 10
        ctx = np.random.RandomState(12345)

        for band in self._bands:
            n_tested = 0
            exp = self.butler.get("deepCoadd_calexp", band=band, tract=self._tract, patch=self._patch)
            cat = self.butler.get("objectTable", band=band, tract=self._tract, patch=self._patch)
            transmission_curve = exp.getInfo().getTransmissionCurve()
            coadd_inputs = exp.getInfo().getCoaddInputs().ccds
            wcs = exp.getWcs()

            to_check = ctx.choice(len(cat), size=n_object_test, replace=False)
            for index in to_check:
                coadd_coord = geom.SpherePoint(cat["coord_ra"].values[index]*geom.degrees,
                                               cat["coord_dec"].values[index]*geom.degrees)
                summed_throughput = np.zeros(wavelengths.shape, dtype=np.float64)
                weight_sum = 0.0
                for rec in coadd_inputs.subsetContaining(coadd_coord, includeValidPolygon=True):
                    det_pos = rec.getWcs().skyToPixel(coadd_coord)
                    det_trans = rec.getTransmissionCurve()
                    weight = rec.get("weight")
                    summed_throughput += det_trans.sampleAt(det_pos, wavelengths)*weight
                    weight_sum += weight
                if weight_sum == 0.0:
                    continue
                summed_throughput /= weight_sum
                coadd_pos = wcs.skyToPixel(coadd_coord)
                coadd_throughput = transmission_curve.sampleAt(coadd_pos, wavelengths)
                np.testing.assert_array_almost_equal(coadd_throughput, summed_throughput)
                n_tested += 1
            self.assertGreater(n_tested, 5)

    def test_mask_planes_exist(self):
        """Test that the input mask planes have been added."""
        for data_id in DATA_IDS:
            mask = self.butler.get("calexp.mask", data_id)
            self.assertIn("CROSSTALK", mask.getMaskPlaneDict())
            self.assertIn("NOT_DEBLENDED", mask.getMaskPlaneDict())

    # Expected to fail until DM-5174 is fixed.
    @unittest.expectedFailure
    def test_masks_removed(self):
        """Test that certain mask planes have been removed from the coadds.

        This is expected to fail until DM-5174 is fixed.
        """
        for band in self._bands:
            mask = self.butler.get("deepCoadd_calexp.mask", band=band, tract=self._tract, patch=self._patch)
            self.assertNotIn("CROSSTALK", mask.getMaskPlaneDict())
            self.assertNotIn("NOT_DEBLENDED", mask.getMaskPlaneDict())

    def test_warp_inputs(self):
        """Test that the warps have the correct inputs."""
        skymap = self.butler.get("skyMap")
        tract_info = skymap[self._tract]
        for warp_type in ["directWarp", "psfMatchedWarp"]:
            datasets = set(self.butler.registry.queryDatasets(f"deepCoadd_{warp_type}"))
            # We only need to test one dataset
            dataset = list(datasets)[0]

            warp = self.butler.getDirect(dataset)
            self.assertEqual(warp.wcs, tract_info.wcs)
            coadd_inputs = warp.getInfo().getCoaddInputs()
            self.assertEqual(len(coadd_inputs.visits), 1)
            visit_record = coadd_inputs.visits[0]
            self.assertEqual(visit_record.getWcs(), warp.wcs)
            self.assertEqual(visit_record.getBBox(), warp.getBBox())
            self.assertGreater(len(coadd_inputs.ccds), 0)

            wcs_cat = self.butler.get(
                "jointcalSkyWcsCatalog",
                visit=visit_record.getId(),
                tract=self._tract
            )
            photocalib_cat = self.butler.get(
                "jointcalPhotoCalibCatalog",
                visit=visit_record.getId(),
                tract=self._tract
            )
            final_psf_cat = self.butler.get(
                "finalized_psf_ap_corr_catalog",
                visit=visit_record.getId()
            )

            # We only need to test one input ccd
            det_record = coadd_inputs.ccds[0]
            exp_bbox = self.butler.get(
                "calexp.bbox",
                visit=det_record["visit"],
                detector=det_record["ccd"]
            )
            self.assertEqual(det_record.getWcs(), wcs_cat.find(det_record["ccd"]).getWcs())
            self.assertEqual(
                det_record.getPhotoCalib(),
                photocalib_cat.find(det_record["ccd"]).getPhotoCalib()
            )
            self.assertEqual(det_record.getBBox(), exp_bbox)
            self.assertIsNotNone(det_record.getTransmissionCurve())
            center = det_record.getBBox().getCenter()
            np.testing.assert_array_almost_equal(
                det_record.getPsf().computeKernelImage(center).array,
                final_psf_cat.find(det_record["ccd"]).getPsf().computeKernelImage(center).array
            )
            input_map = det_record.getApCorrMap()
            final_map = final_psf_cat.find(det_record["ccd"]).getApCorrMap()
            self.assertEqual(len(input_map), len(final_map))
            for key in input_map.keys():
                self.assertEqual(input_map[key], final_map[key])
            self.assertIsNotNone(coadd_inputs.visits.find(det_record["visit"]))

    def test_coadd_inputs(self):
        """Test that the coadds have the correct inputs."""
        skymap = self.butler.get("skyMap")
        tract_info = skymap[self._tract]
        for band in self._bands:
            wcs = self.butler.get("deepCoadd_calexp.wcs", band=band, tract=self._tract, patch=self._patch)
            self.assertEqual(wcs, tract_info.wcs)
            coadd_inputs = self.butler.get(
                "deepCoadd_calexp.coaddInputs",
                band=band,
                tract=self._tract,
                patch=self._patch
            )
            # We only need to test one input ccd
            det_record = coadd_inputs.ccds[0]
            wcs_cat = self.butler.get(
                "jointcalSkyWcsCatalog",
                visit=det_record["visit"],
                tract=self._tract
            )
            photocalib_cat = self.butler.get(
                "jointcalPhotoCalibCatalog",
                visit=det_record["visit"],
                tract=self._tract
            )
            final_psf_cat = self.butler.get(
                "finalized_psf_ap_corr_catalog",
                visit=det_record["visit"]
            )
            exp_bbox = self.butler.get(
                "calexp.bbox",
                visit=det_record["visit"],
                detector=det_record["ccd"]
            )
            self.assertEqual(det_record.getWcs(), wcs_cat.find(det_record["ccd"]).getWcs())
            self.assertEqual(
                det_record.getPhotoCalib(),
                photocalib_cat.find(det_record["ccd"]).getPhotoCalib()
            )
            self.assertEqual(det_record.getBBox(), exp_bbox)
            self.assertIsNotNone(det_record.getTransmissionCurve())
            center = det_record.getBBox().getCenter()
            np.testing.assert_array_almost_equal(
                det_record.getPsf().computeKernelImage(center).array,
                final_psf_cat.find(det_record["ccd"]).getPsf().computeKernelImage(center).array
            )
            input_map = det_record.getApCorrMap()
            final_map = final_psf_cat.find(det_record["ccd"]).getApCorrMap()
            self.assertEqual(len(input_map), len(final_map))
            for key in input_map.keys():
                self.assertEqual(input_map[key], final_map[key])
            self.assertIsNotNone(coadd_inputs.visits.find(det_record["visit"]))

    def test_psf_installation(self):
        """Test that the coadd psf is installed."""
        for band in self._bands:
            wcs = self.butler.get("deepCoadd_calexp.wcs", band=band, tract=self._tract, patch=self._patch)
            coadd_inputs = self.butler.get(
                "deepCoadd_calexp.coaddInputs",
                band=band,
                tract=self._tract,
                patch=self._patch
            )
            coadd_psf = self.butler.get(
                "deepCoadd_calexp.psf",
                band=band,
                tract=self._tract,
                patch=self._patch
            )
            new_psf = lsst.meas.algorithms.CoaddPsf(coadd_inputs.ccds, wcs)
            self.assertEqual(coadd_psf.getComponentCount(), len(coadd_inputs.ccds))
            self.assertEqual(new_psf.getComponentCount(), len(coadd_inputs.ccds))
            for n, record in enumerate(coadd_inputs.ccds):
                center = record.getBBox().getCenter()
                np.testing.assert_array_almost_equal(
                    coadd_psf.getPsf(n).computeKernelImage(center).array,
                    record.getPsf().computeKernelImage(center).array
                )
                np.testing.assert_array_almost_equal(
                    new_psf.getPsf(n).computeKernelImage(center).array,
                    record.getPsf().computeKernelImage(center).array
                )
                self.assertEqual(coadd_psf.getWcs(n), record.getWcs())
                self.assertEqual(new_psf.getWcs(n), record.getWcs())
                self.assertEqual(coadd_psf.getBBox(n), record.getBBox())
                self.assertEqual(new_psf.getBBox(n), record.getBBox())

    def test_coadd_psf(self):
        """Test that the stars on the coadd are well represented by
        the attached PSF.
        """
        n_object_test = 10
        n_good_test = 5
        ctx = np.random.RandomState(12345)

        for band in self._bands:
            exp = self.butler.get("deepCoadd_calexp", band=band, tract=self._tract, patch=self._patch)
            coadd_psf = exp.getPsf()
            cat = self.butler.get("objectTable", band=band, tract=self._tract, patch=self._patch)

            star_cat = cat[(cat["i_extendedness"] < 0.5)
                           & (cat["detect_isPrimary"])
                           & (cat[f"{band}_psfFlux"] > 0.0)
                           & (cat[f"{band}_psfFlux"]/cat[f"{band}_psfFluxErr"] > 50.0)
                           & (cat[f"{band}_psfFlux"]/cat[f"{band}_psfFluxErr"] < 200.0)]

            to_check = ctx.choice(len(star_cat), size=n_object_test, replace=False)
            n_good = 0
            for index in to_check:
                position = geom.Point2D(star_cat["x"].values[index], star_cat["y"].values[index])
                psf_image = coadd_psf.computeImage(position)
                psf_image_bbox = psf_image.getBBox()
                star_image = lsst.afw.image.ImageF(
                    exp.maskedImage.image,
                    psf_image_bbox
                ).convertD()
                star_image /= star_image.array.sum()
                psf_image /= psf_image.array.sum()
                residuals = lsst.afw.image.ImageD(star_image, True)
                residuals -= psf_image
                # This is just a quick check that the coadd psf model works
                # reasonably well for the stars. It is not meant as a detailed
                # test of the psf modeling capability.
                if np.max(np.abs(residuals.array)) < 0.01:
                    n_good += 1

            self.assertGreater(n_good, n_good_test)
Exemplo n.º 12
0
class HscIngestTestCase(lsst.utils.tests.TestCase):
    def setUp(self):
        # Use a temporary working directory
        self.root = tempfile.mkdtemp(dir=TESTDIR)
        Butler.makeRepo(self.root)
        self.butler = Butler(self.root, run="raw")
        # Register the instrument and its static metadata
        HyperSuprimeCam().register(self.butler.registry)
        # Make a default config for test methods to play with
        self.config = RawIngestTask.ConfigClass()
        self.config.onError = "break"
        self.file = os.path.join(testDataDirectory, "hsc", "raw",
                                 "HSCA90402512.fits.gz")
        self.dataId = dict(instrument="HSC", exposure=904024, detector=50)

    def tearDown(self):
        if os.path.exists(self.root):
            shutil.rmtree(self.root, ignore_errors=True)

    def runIngest(self, files=None):
        if files is None:
            files = [self.file]
        task = RawIngestTask(config=self.config, butler=self.butler)
        task.log.setLevel(
            task.log.FATAL)  # silence logs, since we expect a lot of warnings
        task.run(files)

    def runIngestTest(self, files=None):
        self.runIngest(files)
        exposure = self.butler.get("raw", self.dataId)
        metadata = self.butler.get("raw.metadata", self.dataId)
        image = self.butler.get("raw.image", self.dataId)
        self.assertImagesEqual(exposure.image, image)
        self.assertEqual(metadata.toDict(), exposure.getMetadata().toDict())

    def testSymLink(self):
        self.config.transfer = "symlink"
        self.runIngestTest()

    def testCopy(self):
        self.config.transfer = "copy"
        self.runIngestTest()

    def testHardLink(self):
        self.config.transfer = "hardlink"
        self.runIngestTest()

    def testInPlace(self):
        # hardlink into repo root manually
        newPath = os.path.join(self.butler.datastore.root,
                               os.path.basename(self.file))
        os.link(self.file, newPath)
        self.config.transfer = None
        self.runIngestTest([newPath])

    def testOnConflictFail(self):
        self.config.transfer = "symlink"
        self.config.conflict = "fail"
        self.runIngest()  # this one shd
        with self.assertRaises(Exception):
            self.runIngest()  # this ont

    def testOnConflictIgnore(self):
        self.config.transfer = "symlink"
        self.config.conflict = "ignore"
        self.runIngest()  # this one should succeed
        n1, = self.butler.registry.query("SELECT COUNT(*) FROM Dataset")
        self.runIngest()  # this ong should silently fail
        n2, = self.butler.registry.query("SELECT COUNT(*) FROM Dataset")
        self.assertEqual(n1, n2)

    def testOnConflictStash(self):
        self.config.transfer = "symlink"
        self.config.conflict = "ignore"
        self.config.stash = "stash"
        self.runIngest()  # this one should write to 'rawn
        self.runIngest()  # this one should write to 'stashn
        dt = self.butler.registry.getDatasetType("raw.metadata")
        ref1 = self.butler.registry.find(self.butler.collection, dt,
                                         self.dataId)
        ref2 = self.butler.registry.find("stash", dt, self.dataId)
        self.assertNotEqual(ref1.id, ref2.id)
        self.assertEqual(
            self.butler.get(ref1).toDict(),
            self.butler.getDirect(ref2).toDict())

    def testOnErrorBreak(self):
        self.config.transfer = "symlink"
        self.config.onError = "break"
        # Failing to ingest this nonexistent file after ingesting the valid one should
        # leave the valid one in the registry, despite raising an exception.
        with self.assertRaises(Exception):
            self.runIngest(files=[self.file, "nonexistent.fits"])
        dt = self.butler.registry.getDatasetType("raw.metadata")
        self.assertIsNotNone(
            self.butler.registry.find(self.butler.collection, dt, self.dataId))

    def testOnErrorContinue(self):
        self.config.transfer = "symlink"
        self.config.onError = "continue"
        # Failing to ingest nonexistent files before and after ingesting the
        # valid one should leave the valid one in the registry and not raise
        # an exception.
        self.runIngest(
            files=["nonexistent.fits", self.file, "still-not-here.fits"])
        dt = self.butler.registry.getDatasetType("raw.metadata")
        self.assertIsNotNone(
            self.butler.registry.find(self.butler.collection, dt, self.dataId))

    def testOnErrorRollback(self):
        self.config.transfer = "symlink"
        self.config.onError = "rollback"
        # Failing to ingest nonexistent files after ingesting the
        # valid one should leave the registry empty.
        with self.assertRaises(Exception):
            self.runIngest(file=[self.file, "nonexistent.fits"])
        try:
            dt = self.butler.registry.getDatasetType("raw.metadata")
        except KeyError:
            # If we also rollback registering the DatasetType, that's fine,
            # but not required.
            pass
        else:
            self.assertIsNotNone(
                self.butler.registry.find(self.butler.collection, dt,
                                          self.dataId))