LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
LSST Data Management Base Package
ingest.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2012,2015 LSST Corporation.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <http://www.lsstcorp.org/LegalNotices/>.
21 #
22 import os
23 import re
24 
25 from deprecated.sphinx import deprecated
26 
27 from astro_metadata_translator import fix_header, DecamTranslator
28 from lsst.afw.fits import readMetadata
29 from lsst.pipe.tasks.ingest import ParseTask, IngestTask, IngestArgumentParser
30 import lsst.obs.base
31 
32 __all__ = ["DecamRawIngestTask", "DecamIngestArgumentParser", "DecamIngestTask", "DecamParseTask"]
33 
34 
35 @deprecated(reason="DECam no longer requires a specialist gen3 ingest task. Please use the default." " Will be removed after v23.", category=FutureWarning, version="23.0")
36 class DecamRawIngestTask(lsst.obs.base.RawIngestTask):
37  """Task for ingesting raw DECam data into a Gen3 Butler repository.
38  """
39 
40 
42  """Gen2 DECam ingest additional arguments.
43  """
44 
45  def __init__(self, *args, **kwargs):
46  super(DecamIngestArgumentParser, self).__init__(*args, **kwargs)
47  self.add_argument("--filetype", default="raw", choices=["instcal", "raw"],
48  help="Data processing level of the files to be ingested")
49 
50 
52  """Gen2 DECam file ingest task.
53  """
54  ArgumentParser = DecamIngestArgumentParser
55 
56  def __init__(self, *args, **kwargs):
57  super(DecamIngestTask, self).__init__(*args, **kwargs)
58 
59  def run(self, args):
60  """Ingest all specified files and add them to the registry
61  """
62  if args.filetype == "instcal":
63  root = args.input
64  with self.register.openRegistry(root, create=args.create, dryrun=args.dryrun) as registry:
65  for infile in args.files:
66  fileInfo, hduInfoList = self.parse.getInfo(infile, args.filetype)
67  if len(hduInfoList) > 0:
68  outfileInstcal = os.path.join(root, self.parse.getDestination(args.butler,
69  hduInfoList[0],
70  infile, "instcal"))
71  outfileDqmask = os.path.join(root, self.parse.getDestination(args.butler,
72  hduInfoList[0], infile,
73  "dqmask"))
74  outfileWtmap = os.path.join(root, self.parse.getDestination(args.butler,
75  hduInfoList[0], infile,
76  "wtmap"))
77 
78  ingestedInstcal = self.ingestingest(fileInfo["instcal"], outfileInstcal,
79  mode=args.mode, dryrun=args.dryrun)
80  ingestedDqmask = self.ingestingest(fileInfo["dqmask"], outfileDqmask,
81  mode=args.mode, dryrun=args.dryrun)
82  ingestedWtmap = self.ingestingest(fileInfo["wtmap"], outfileWtmap,
83  mode=args.mode, dryrun=args.dryrun)
84 
85  if not (ingestedInstcal or ingestedDqmask or ingestedWtmap):
86  continue
87 
88  for info in hduInfoList:
89  self.register.addRow(registry, info, dryrun=args.dryrun, create=args.create)
90 
91  elif args.filetype == "raw":
92  IngestTask.run(self, args)
93 
94 
96  """Parse an image filename to get the required information to
97  put the file in the correct location and populate the registry.
98  """
99 
100  _translatorClass = DecamTranslator
101 
102  def __init__(self, *args, **kwargs):
103  super(ParseTask, self).__init__(*args, **kwargs)
104 
105  self.expnumMapperexpnumMapper = None
106 
107  # Note that these should be syncronized with the fields in
108  # root.register.columns defined in config/ingest.py
109  self.instcalPrefixinstcalPrefix = "instcal"
110  self.dqmaskPrefixdqmaskPrefix = "dqmask"
111  self.wtmapPrefixwtmapPrefix = "wtmap"
112 
113  def _listdir(self, path, prefix):
114  for file in os.listdir(path):
115  fileName = os.path.join(path, file)
116  md = readMetadata(fileName)
117  fix_header(md, translator_class=self._translatorClass_translatorClass)
118  if "EXPNUM" not in md:
119  return
120  expnum = md["EXPNUM"]
121  if expnum not in self.expnumMapperexpnumMapper:
122  self.expnumMapperexpnumMapper[expnum] = {self.instcalPrefixinstcalPrefix: None,
123  self.wtmapPrefixwtmapPrefix: None,
124  self.dqmaskPrefixdqmaskPrefix: None}
125  self.expnumMapperexpnumMapper[expnum][prefix] = fileName
126 
127  def buildExpnumMapper(self, basepath):
128  """Extract exposure numbers from filenames to set self.expnumMapper
129 
130  Parameters
131  ----------
132  basepath : `str`
133  Location on disk of instcal, dqmask, and wtmap subdirectories.
134  """
135  self.expnumMapperexpnumMapper = {}
136 
137  instcalPath = basepath
138  dqmaskPath = re.sub(self.instcalPrefixinstcalPrefix, self.dqmaskPrefixdqmaskPrefix, instcalPath)
139  wtmapPath = re.sub(self.instcalPrefixinstcalPrefix, self.wtmapPrefixwtmapPrefix, instcalPath)
140  if instcalPath == dqmaskPath:
141  raise RuntimeError("instcal and mask directories are the same")
142  if instcalPath == wtmapPath:
143  raise RuntimeError("instcal and weight map directories are the same")
144 
145  if not os.path.isdir(dqmaskPath):
146  raise OSError("Directory %s does not exist" % (dqmaskPath))
147  if not os.path.isdir(wtmapPath):
148  raise OSError("Directory %s does not exist" % (wtmapPath))
149 
150  # Traverse each directory and extract the expnums
151  for path, prefix in zip((instcalPath, dqmaskPath, wtmapPath),
152  (self.instcalPrefixinstcalPrefix, self.dqmaskPrefixdqmaskPrefix, self.wtmapPrefixwtmapPrefix)):
153  self._listdir_listdir(path, prefix)
154 
155  def getInfo(self, filename, filetype="raw"):
156  """Get metadata header info from multi-extension FITS decam image file.
157 
158  The science pixels, mask, and weight (inverse variance) are
159  stored in separate files each with a unique name but with a
160  common unique identifier EXPNUM in the FITS header. We have
161  to aggregate the 3 filenames for a given EXPNUM and return
162  this information along with that returned by the base class.
163 
164  Parameters
165  ----------
166  filename : `str`
167  Image file to retrieve info from.
168  filetype : `str`
169  One of "raw" or "instcal".
170 
171  Returns
172  -------
173  phuInfo : `dict`
174  Primary header unit info.
175  infoList : `list` of `dict`
176  Info for the other HDUs.
177 
178  Notes
179  -----
180  For filetype="instcal", we expect a directory structure that looks
181  like the following:
182 
183  .. code-block:: none
184 
185  dqmask/
186  instcal/
187  wtmap/
188 
189  The user creates the registry by running:
190 
191  .. code-block:: none
192 
193  ingestImagesDecam.py outputRepository --filetype=instcal --mode=link instcal/*fits
194  """
195  if filetype == "instcal":
196  if self.expnumMapperexpnumMapper is None:
197  self.buildExpnumMapperbuildExpnumMapper(os.path.dirname(os.path.abspath(filename)))
198 
199  # Note that phuInfo will have
200  # 'side': 'X', 'ccd': 0
201  phuInfo, infoList = super(DecamParseTask, self).getInfo(filename)
202  expnum = phuInfo["visit"]
203  phuInfo[self.instcalPrefixinstcalPrefix] = self.expnumMapperexpnumMapper[expnum][self.instcalPrefixinstcalPrefix]
204  phuInfo[self.dqmaskPrefixdqmaskPrefix] = self.expnumMapperexpnumMapper[expnum][self.dqmaskPrefixdqmaskPrefix]
205  phuInfo[self.wtmapPrefixwtmapPrefix] = self.expnumMapperexpnumMapper[expnum][self.wtmapPrefixwtmapPrefix]
206  for info in infoList:
207  expnum = info["visit"]
208  info[self.instcalPrefixinstcalPrefix] = self.expnumMapperexpnumMapper[expnum][self.instcalPrefixinstcalPrefix]
209  info[self.dqmaskPrefixdqmaskPrefix] = self.expnumMapperexpnumMapper[expnum][self.dqmaskPrefixdqmaskPrefix]
210  info[self.wtmapPrefixwtmapPrefix] = self.expnumMapperexpnumMapper[expnum][self.wtmapPrefixwtmapPrefix]
211 
212  elif filetype == "raw":
213  phuInfo, infoList = super(DecamParseTask, self).getInfo(filename)
214  for info in infoList:
215  info[self.instcalPrefixinstcalPrefix] = ""
216  info[self.dqmaskPrefixdqmaskPrefix] = ""
217  info[self.wtmapPrefixwtmapPrefix] = ""
218 
219  # Some data IDs can not be extracted from the zeroth extension
220  # of the MEF. Add them so Butler does not try to find them
221  # in the registry which may still yet to be created.
222  for key in ("ccdnum", "hdu", "ccd", "calib_hdu"):
223  if key not in phuInfo:
224  phuInfo[key] = 0
225 
226  return phuInfo, infoList
227 
228  def getDestination(self, butler, info, filename, filetype="raw"):
229  """Get destination for the file
230 
231  Parameters
232  ----------
233  butler : `lsst.daf.persistence.Butler`
234  Data butler.
235  info : data ID
236  File properties, used as dataId for the butler.
237  filename : `str`
238  Input filename.
239 
240  Returns
241  -------
242  raw : `str`
243  Destination filename.
244  """
245  raw = butler.get("%s_filename"%(filetype), info)[0]
246  # Ensure filename is devoid of cfitsio directions about HDUs
247  c = raw.find("[")
248  if c > 0:
249  raw = raw[:c]
250  return raw
251 
def __init__(self, *args, **kwargs)
Definition: ingest.py:45
def __init__(self, *args, **kwargs)
Definition: ingest.py:56
def buildExpnumMapper(self, basepath)
Definition: ingest.py:127
def _listdir(self, path, prefix)
Definition: ingest.py:113
def __init__(self, *args, **kwargs)
Definition: ingest.py:102
def getInfo(self, filename, filetype="raw")
Definition: ingest.py:155
def getDestination(self, butler, info, filename, filetype="raw")
Definition: ingest.py:228
def ingest(self, infile, outfile, mode="move", dryrun=False)
Definition: ingest.py:478
std::shared_ptr< daf::base::PropertyList > readMetadata(std::string const &fileName, int hdu=DEFAULT_HDU, bool strip=false)
Read FITS header.
Definition: fits.cc:1657