29 from astro_metadata_translator
import fix_header, DecamTranslator
36 from .makeDecamRawVisitInfo
import MakeDecamRawVisitInfo
37 from .decamFilters
import DECAM_FILTER_DEFINITIONS
39 np.seterr(divide=
"ignore")
41 __all__ = [
"DecamMapper"]
45 packageName =
'obs_decam'
47 MakeRawVisitInfoClass = MakeDecamRawVisitInfo
50 1:
'S29', 2:
'S30', 3:
'S31', 4:
'S25', 5:
'S26', 6:
'S27', 7:
'S28', 8:
'S20', 9:
'S21',
51 10:
'S22', 11:
'S23', 12:
'S24', 13:
'S14', 14:
'S15', 15:
'S16', 16:
'S17', 17:
'S18',
52 18:
'S19', 19:
'S8', 20:
'S9', 21:
'S10', 22:
'S11', 23:
'S12', 24:
'S13', 25:
'S1', 26:
'S2',
53 27:
'S3', 28:
'S4', 29:
'S5', 30:
'S6', 31:
'S7', 32:
'N1', 33:
'N2', 34:
'N3', 35:
'N4',
54 36:
'N5', 37:
'N6', 38:
'N7', 39:
'N8', 40:
'N9', 41:
'N10', 42:
'N11', 43:
'N12', 44:
'N13',
55 45:
'N14', 46:
'N15', 47:
'N16', 48:
'N17', 49:
'N18', 50:
'N19', 51:
'N20', 52:
'N21',
56 53:
'N22', 54:
'N23', 55:
'N24', 56:
'N25', 57:
'N26', 58:
'N27', 59:
'N28', 60:
'N29',
59 def __init__(self, inputPolicy=None, **kwargs):
60 policyFile = Policy.defaultPolicyFile(self.
packageName,
"DecamMapper.yaml",
"policy")
61 policy =
Policy(policyFile)
63 super(DecamMapper, self).
__init__(policy, os.path.dirname(policyFile), **kwargs)
65 DECAM_FILTER_DEFINITIONS.defineFilters()
72 for datasetType
in (
"raw",
"instcal",
"dqmask",
"wtmap",
"cpIllumcor"):
73 self.
mappings[datasetType].keyDict.update({
'ccdnum': int})
74 self.
mappings[
"raw"].keyDict.update({
'object': str})
78 DecamMapper._nbit_tract = 10
79 DecamMapper._nbit_patch = 10
80 DecamMapper._nbit_filter = 4
81 DecamMapper._nbit_id = 64 - (DecamMapper._nbit_tract
82 + 2*DecamMapper._nbit_patch
83 + DecamMapper._nbit_filter)
85 def _extractDetectorName(self, dataId):
88 return DecamMapper.detectorNames[copyId[
'ccdnum']]
90 raise RuntimeError(
"No name found for dataId: %s"%(dataId))
92 def _transformId(self, dataId):
93 copyId = CameraMapper._transformId(self, dataId)
95 copyId.setdefault(
"ccdnum", copyId[
"ccd"])
104 def _computeCcdExposureId(self, dataId):
105 """Compute the 64-bit (long) identifier for a CCD exposure.
110 Data identifier with visit, ccd.
115 Integer identifier for a CCD exposure.
118 visit = copyId[
'visit']
119 ccdnum = copyId[
'ccdnum']
120 return int(
"%07d%02d" % (visit, ccdnum))
122 def _computeCoaddExposureId(self, dataId, singleFilter):
123 """Compute the 64-bit (long) identifier for a coadd.
128 Data identifier with tract and patch.
129 singleFilter : `bool`
130 True means the desired ID is for a single-filter coadd,
131 in which case the dataId must contain filter.
136 Unique integer identifier.
138 tract = int(dataId[
'tract'])
139 if tract < 0
or tract >= 2**DecamMapper._nbit_tract:
140 raise RuntimeError(
'tract not in range [0,%d)' % (2**DecamMapper._nbit_tract))
141 patchX, patchY = [int(x)
for x
in dataId[
'patch'].split(
',')]
142 for p
in (patchX, patchY):
143 if p < 0
or p >= 2**DecamMapper._nbit_patch:
144 raise RuntimeError(
'patch component not in range [0, %d)' % 2**DecamMapper._nbit_patch)
145 oid = (((tract << DecamMapper._nbit_patch) + patchX) << DecamMapper._nbit_patch) + patchY
147 return (oid << DecamMapper._nbit_filter) +
afwImage.Filter(dataId[
'filter']).getId()
154 return 64 - DecamMapper._nbit_id
160 return 64 - DecamMapper._nbit_id
177 dqmArr = dqmask.getArray()
179 mArr = mask.getArray()
180 idxBad = np.where(dqmArr & 1)
181 idxSat = np.where(dqmArr & 2)
182 idxIntrp = np.where(dqmArr & 4)
183 idxCr = np.where(dqmArr & 16)
184 idxBleed = np.where(dqmArr & 64)
185 idxEdge = np.where(dqmArr & 512)
186 mArr[idxBad] |= mask.getPlaneBitMask(
"BAD")
187 mArr[idxSat] |= mask.getPlaneBitMask(
"SAT")
188 mArr[idxIntrp] |= mask.getPlaneBitMask(
"INTRP")
189 mArr[idxCr] |= mask.getPlaneBitMask(
"CR")
190 mArr[idxBleed] |= mask.getPlaneBitMask(
"SAT")
191 mArr[idxEdge] |= mask.getPlaneBitMask(
"EDGE")
195 wtmArr = wtmap.getArray()
196 idxUndefWeight = np.where(wtmArr <= 0)
198 wtmArr[idxUndefWeight] =
min(1e-14, np.min(wtmArr[np.where(wtmArr > 0)]))
200 varim = afwImage.ImageF(var)
205 instcalMap = self.map_instcal(dataId)
206 dqmaskMap = self.map_dqmask(dataId)
207 wtmapMap = self.map_wtmap(dataId)
208 instcalType = getattr(afwImage, instcalMap.getPythonType().split(
".")[-1])
209 dqmaskType = getattr(afwImage, dqmaskMap.getPythonType().split(
".")[-1])
210 wtmapType = getattr(afwImage, wtmapMap.getPythonType().split(
".")[-1])
211 instcal = instcalType(instcalMap.getLocationsWithRoot()[0])
212 dqmask = dqmaskType(dqmaskMap.getLocationsWithRoot()[0])
213 wtmap = wtmapType(wtmapMap.getLocationsWithRoot()[0])
218 mi = afwImage.MaskedImageF(afwImage.ImageF(instcal.getImage()), mask, variance)
220 fix_header(md, translator_class=DecamTranslator)
222 exp = afwImage.ExposureF(mi, wcs)
226 exp.getInfo().setVisitInfo(visitInfo)
228 for kw
in (
'LTV1',
'LTV2'):
235 """Standardize a raw dataset by converting it to an Exposure.
237 Raw images are MEF files with one HDU for each detector.
241 item : `lsst.afw.image.DecoratedImage`
242 The image read by the butler.
248 result : `lsst.afw.image.Exposure`
249 The standardized Exposure.
254 def _createInitialSkyWcs(self, exposure):
261 if exposure.getInfo().getVisitInfo()
is None:
262 msg =
"No VisitInfo; cannot access boresight information. Defaulting to metadata-based SkyWcs."
268 exposure.setWcs(newSkyWcs)
269 except InitialSkyWcsError
as e:
270 msg =
"Cannot create SkyWcs using VisitInfo and Detector, using metadata-based SkyWcs: %s"
272 self.
log.
debug(
"Exception was: %s", traceback.TracebackException.from_exception(e))
273 if e.__context__
is not None:
274 self.
log.
debug(
"Root-cause Exception was: %s",
275 traceback.TracebackException.from_exception(e.__context__))
279 rawPath = self.map_raw(dataId).getLocations()[0]
280 headerPath = re.sub(
r'[\[](\d+)[\]]$',
"[0]", rawPath)
282 fix_header(md0, translator_class=DecamTranslator)
284 exp.getInfo().setVisitInfo(visitInfo)
299 def _standardizeCpMasterCal(self, datasetType, item, dataId, setFilter=False):
300 """Standardize a MasterCal image obtained from NOAO archive into Exposure
302 These MasterCal images are MEF files with one HDU for each detector.
303 Some WCS header, eg CTYPE1, exists only in the zeroth extensionr,
304 so info in the zeroth header need to be copied over to metadata.
309 Dataset type ("bias", "flat", or "illumcor").
310 item : `lsst.afw.image.DecoratedImage`
311 The image read by the butler.
315 Whether to set the filter in the Exposure.
319 result : `lsst.afw.image.Exposure`
320 The standardized Exposure.
323 md = item.getMetadata()
324 masterCalMap = getattr(self,
"map_" + datasetType)
325 masterCalPath = masterCalMap(dataId).getLocationsWithRoot()[0]
326 headerPath = re.sub(
r'[\[](\d+)[\]]$',
"[0]", masterCalPath)
328 fix_header(md0, translator_class=DecamTranslator)
329 for kw
in (
'CTYPE1',
'CTYPE2',
'CRVAL1',
'CRVAL2',
'CUNIT1',
'CUNIT2',
330 'CD1_1',
'CD1_2',
'CD2_1',
'CD2_2'):
331 if kw
in md0.paramNames()
and kw
not in md.paramNames():
332 md.add(kw, md0.getScalar(kw))
353 """Directory containing linearizers"""
356 return os.path.join(packageDir,
"decam",
"linearizer")
359 """Map a linearizer"""
361 location =
"%02d.fits" % (dataId[
"ccdnum"])
363 pythonType=
"lsst.ip.isr.LinearizeSquared",
365 storageName=
"PickleStorage",
366 locationList=[location],