23 __all__ = (
"BootstrapRepoConfig",
"BootstrapRepoTask",
"BootstrapRepoInputs",
24 "BootstrapRepoSkyMapConfig",
"BootstrapRepoRefCatConfig")
27 from dataclasses
import dataclass
28 from typing
import List
31 from lsst
import sphgeom
32 from lsst.daf.butler
import Butler, DatasetType
33 from lsst.daf.butler.instrument
import Instrument
34 from lsst.pex.config import Config, Field, ConfigurableField, ConfigDictField, ConfigField
40 from .repoConverter
import RepoConverter
41 from .calibRepoConverter
import CalibRepoConverter
45 datasetTypeName =
Field((
"DatasetType used to write the SkyMap instance. If None, the instance will " 46 "not be written, and only the Registry will be modified."),
47 dtype=str, default=
"deepCoadd_skyMap", optional=
True)
48 collection =
Field((
"Butler collection the SkyMap instance should be written to. If None, the " 49 "collection used to initialize the butler will be used."),
50 dtype=str, default=
"skymaps", optional=
True)
51 skyMap = skyMapRegistry.makeField(
52 doc=
"Type and parameters for the SkyMap itself.",
58 datasetTypeName =
Field((
"DatasetType used to write the catalog shards.."),
59 dtype=str, default=
"ref_cat")
60 filterByRawRegions =
Field((
"If True, do not ingest shards that do not overlap visits. " 61 "Does not guarantee that all ingested shards will overlap a visit."),
62 dtype=bool, default=
True)
63 collection =
Field((
"Butler collection the reference catalog should be written to. If None, the " 64 "collection used to initialize the butler will be used. May also be a string with " 65 "the format placeholder '{name}', which will be replaced with the reference " 66 "catalog name (i.e. the key of the configuration dictionary,"),
67 dtype=str, default=
"refcats/{name}", optional=
True)
72 collection =
Field((
"Butler collection that datasets should be ingested into. " 73 "If None, the collection used to initialize the butler will be used."),
74 dtype=str, default=
None, optional=
True)
79 skymap =
Field(
"SkyMap dimension name used to define the tracts and patches for bright object masks.",
80 dtype=str, default=
None, optional=
False)
81 filterByRawRegions =
Field((
"If True, do not ingest files that do not overlap visits. " 82 "Does not guarantee that all ingested files will overlap a visit."),
83 dtype=bool, default=
True)
88 doc=(
"Configuration for subtask responsible for ingesting raws and adding " 89 "visit and exposure dimension entries."))
90 skymaps =
ConfigDictField(doc=(
"SkyMap definitions to register and ingest into the repo, keyed by " 91 "skymap dimension name."),
93 itemtype=BootstrapRepoSkyMapConfig,
95 refCats =
ConfigDictField(doc=(
"Reference catalogs to ingest into the repo, keyed by their subdirectory " 96 "within the overall reference catalog root."),
98 itemtype=BootstrapRepoRefCatConfig,
100 brightObjectMasks =
ConfigField(doc=
"Configuration for ingesting brightObjectMask files.",
101 dtype=BootstrapRepoBrightObjectMasksConfig)
102 calibrations =
ConfigField(doc=
"Configuration for ingesting and creating master calibration products.",
103 dtype=BootstrapRepoGenericIngestConfig)
106 self.
raws.transfer =
"symlink" 111 """Simple struct that aggregates all non-config inputs to 114 Generally, this stuct contains inputs that depend on the organization 115 of the input files on a particular system, while the config includes 116 everything else. The exception is the ``instrument`` attribute, which 117 cannot be included in the config because it's expected that driver code 118 will actually use it (via 119 `~lsst.daf.butler.instrument.Instrument.applyConfigOverrides`) to define 123 instrument: Instrument
124 """Instrument subclass instance for the raws and calibrations to be 125 included in the initial repo. 129 """List of filenames for raw files to ingest (complete paths). 133 """Root of the directory containing the reference catalogs, with immediate 134 subdirectories that correspond to different reference catalogs. 137 brightObjectMaskRoot: str
138 """Root of the Gen2 repository containing bright object masks. 142 """Root of the Gen2 calibraion repository containing flats, biases, 148 """A Task that populates a Gen3 repo with the minimum content needed to 149 run the DRP pipelines. 151 BootstrapRepoTask currently relies on Gen2 data repository information 152 for both bright object masks and master calibrations, but nothing else; 153 unlike dedicated Gen2->Gen3 conversion code, it will be updated in the 154 future as more pure-Gen3 approaches become available. 156 Like other Gen3 Tasks that are not PipelineTasks, BootstrapRepoTask does 157 not yet have a dedicated, general-purpose command-line driver. At least 158 for now, it is instead expected that custom driver scripts will be written 159 for different contexts and predefined datasets. 163 config : `BootstrapRepoConfig` 164 Configuration for the task. 165 butler : `lsst.daf.butler.Butler` 166 Gen3 Butler defining the repository to populate. New butlers with 167 different output collections will be created as necessary from this 168 butler to match the output collections defined in the configuration. 170 Additional keyword arguments are forwarded to the 171 `lsst.pipe.base.Task` constructor. 174 ConfigClass = BootstrapRepoConfig
176 _DefaultName =
"bootstrapRepo" 178 def __init__(self, config=None, *, butler, **kwds):
185 """Create a new butler that writes into the given collection. 189 collection : `str`, optional 190 The new output collection. If `None`, ``self.butler`` is returned 195 butler : `lsst.daf.butler.Butler` 196 Butler instance pointing at the same repository as 197 ``self.butler``, but possibly a different collection. 199 if collection
is not None:
200 return Butler(butler=self.
butler, run=collection)
204 """Run all steps involved in populating the new repository. 208 inputs : `BootstrapRepoInputs` 209 Filenames and paths for the data to be ingested. 219 """Add an instrument, associated metadata, and human-curated 220 calibrations to the repository. 224 instrument : `lsst.daf.butler.instrument.Instrument` 225 Instrument class that defines detectors, physical filters, and 226 curated calibrations to ingest. 228 self.
log.
info(
"Registering instrument '%s' and adding curated calibrations.", instrument.getName())
229 with self.
butler.transaction():
230 instrument.register(self.
butler.registry)
231 instrument.writeCuratedCalibrations(self.
getButler(self.
config.calibrations.collection))
234 """Add configured SkyMaps to the repository. 236 This both registers skymap dimension entries (the skymap, tract, and 237 patch tables, and their associated join tables) and adds a 238 ``<something>Coadd_skyMap`` dataset. 240 for name, config
in self.
config.skymaps.items():
241 self.
log.
info(
"Registering skymap '%s'.", name)
242 with self.
butler.transaction():
243 skyMap = config.skyMap.apply()
244 skyMap.register(name, self.
butler.registry)
245 if config.datasetTypeName
is not None:
246 datasetType = DatasetType(config.datasetTypeName, dimensions=[
"skymap"],
247 storageClass=
"SkyMap")
248 self.
butler.registry.registerDatasetType(datasetType)
249 self.
getButler(config.collection).put(skyMap, datasetType, skymap=name)
253 """Ingest raw images. 255 This step must be run after `bootstrapInstrument`, but may be run 256 multiple times with different arguments (which may be overlapping if 257 the nested `RawIngestTask` is configured to ignore duplicates). 261 files : sequence of `str` 262 The complete path names of the files to be ingested. 264 self.
log.
info(
"Ingesting raw images.")
265 return self.raws.
run(files)
268 """Compute and return the skypix dimension entries that overlap 269 already-ingested visits. 273 row[
"skypix"]
for row
in self.
butler.registry.query(
274 "SELECT DISTINCT skypix FROM visit_skypix_join" 279 """Ingest reference catalogs. 281 This step must be run after `bootstrapRaws` if the 282 ``filterByRawRegions`` config option is `True` for any reference 288 Root of the directory containing the reference catalogs, with 289 immediate subdirectories that correspond to different reference 292 if not self.
config.refCats:
294 if any(config.filterByRawRegions
for config
in self.
config.refCats.values()):
296 datasetType = DatasetType(
"ref_cat", dimensions=[
"skypix"], storageClass=
"SimpleCatalog")
297 self.
butler.registry.registerDatasetType(datasetType)
298 for name, config
in self.
config.refCats.items():
299 self.
log.
info(
"Ingesting reference catalog '%s'.", name)
300 with self.
butler.transaction():
302 onDiskConfig.load(os.path.join(root, name,
"config.py"))
303 if onDiskConfig.indexer.name !=
"HTM":
304 raise ValueError(f
"Reference catalog '{name}' uses unsupported " 305 f
"pixelization '{onDiskConfig.indexer.name}'.")
307 raise ValueError(f
"Registry uses unsupported pixelization class " 308 f
"{self.butler.registry.pixelization.__class__}.")
309 if onDiskConfig.indexer[
"HTM"].depth != self.
butler.registry.pixelization.getLevel():
310 raise ValueError(f
"Registry HTM level {self.butler.registry.pixelization.getLevel()} " 311 f
"does not match reference catalog level {onDiskConfig.indexer.depth}.")
312 butler = self.
getButler(config.collection.format(name))
313 if config.filterByRawRegions:
315 for index
in rawSkyPixels:
316 path = os.path.join(root, name, f
"{index}.fits")
317 if os.path.exists(path):
318 butler.ingest(path, datasetType, transfer=config.transfer, skypix=index)
320 missing.append(index)
322 self.
log.
warn(
"Some overlapping reference catalog shards missing: %s", missing)
324 for path
in glob.glob(os.path.join(root, name,
"*.fits")):
325 if path.endswith(
"master_schema.fits"):
327 _, filename = os.path.split(path)
328 basename, _ = os.path.splitext(filename)
330 index =
int(basename)
332 self.
log.
warn(
"Unrecognized file in reference catalog root: '%s'.", path)
334 butler.ingest(path, datasetType, transfer=config.transfer, skypix=index)
337 """Compute and return the tract dimension entries that overlap 338 already-ingested visits. 342 row[
"tract"]
for row
in self.
butler.registry.query(
343 "SELECT DISTINCT tract FROM visit_tract_join WHERE skymap=:skymap",
349 """Ingest bright object masks from a Gen2 data repository. 351 This step must be run after `bootstrapRaws` if the 352 ``filterByRawRegions`` config option is `True` for any reference 353 catalog, and must always be run after `bootstrapSkyMaps`. 358 Root of the Gen2 repository containing bright object masks. 359 instrument : `lsst.daf.butler.instrument.Instrument` 360 Instrument subclass instance; used to relate Gen2 filter 361 strings to Gen3 physical_filters and abstract_filters. 363 self.
log.
info(
"Ingesting bright object masks.")
366 "skymap": self.
config.brightObjectMasks.skymap,
367 "instrument": instrument.getName()
369 converter =
RepoConverter(root, universe=butler.registry.dimensions, baseDataId=baseDataId,
371 converter.addDatasetType(
"brightObjectMask",
"ObjectMaskCatalog")
372 if self.
config.brightObjectMasks.filterByRawRegions:
374 with self.
butler.transaction():
375 converter.convertRepo(butler, directory=f
"{root}/deepCoadd/BrightObjectMasks/{tract:d}",
376 transfer=self.
config.brightObjectMasks.transfer)
378 with self.
butler.transaction():
379 converter.convertRepo(butler, transfer=self.
config.brightObjectMasks.transfer)
382 """Ingest master calibrations from a Gen2 calibration data repository. 384 At present, all master calibrations in the Gen2 repostory are 385 transferred, even those unrelated to the ingested raws. 387 This step must be run after `bootstrapInstrument`. 391 instrument : `lsst.daf.butler.instrument.Instrument` 392 Instrument subclass instance for the raws and calibrations to be 393 included in the initial repo. 395 Root of the Gen2 calibration data repository. 397 self.
log.
info(
"Ingesting calibrations.")
398 baseDataId = {
"instrument": instrument.getName()}
400 converter =
CalibRepoConverter(root, universe=butler.registry.dimensions, baseDataId=baseDataId)
401 converter.addDatasetType(
"flat",
"MaskedImageF")
402 converter.addDatasetType(
"bias",
"ImageF")
403 converter.addDatasetType(
"dark",
"ImageF")
404 converter.addDatasetType(
"sky",
"ExposureF")
405 converter.addDatasetType(
"fringe",
"ExposureF")
407 with self.
butler.transaction():
408 converter.convertRepo(butler, transfer=self.
config.brightObjectMasks.transfer)
def makeSubtask(self, name, keyArgs)
def computeRawSkyPixels(self)
def __init__(self, config=None, butler, kwds)
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations...
bool any(CoordinateExpr< N > const &expr) noexcept
Return true if any elements are true.
def bootstrapCalibrations(self, instrument, root)
def bootstrapRaws(self, files)
def makeTransferChoiceField(doc="How to transfer files (None for no transfer).", default=None)
def getButler(self, collection=None)
def bootstrapRefCats(self, root)
def computeRawTracts(self, skymap)
def bootstrapInstrument(self, instrument)
HtmPixelization provides HTM indexing of points and regions.
daf::base::PropertyList * list
def bootstrapSkyMaps(self)
def bootstrapBrightObjectMasks(self, instrument, root)