29 from astro_metadata_translator
import fix_header, DecamTranslator
34 from lsst.obs.base.utils
import createInitialSkyWcs, InitialSkyWcsError
36 from .makeDecamRawVisitInfo
import MakeDecamRawVisitInfo
37 from .decamFilters
import DECAM_FILTER_DEFINITIONS
38 from ._instrument
import DarkEnergyCamera
40 np.seterr(divide=
"ignore")
42 __all__ = [
"DecamMapper"]
46 packageName =
'obs_decam'
47 _gen3instrument = DarkEnergyCamera
49 MakeRawVisitInfoClass = MakeDecamRawVisitInfo
52 1:
'S29', 2:
'S30', 3:
'S31', 4:
'S25', 5:
'S26', 6:
'S27', 7:
'S28', 8:
'S20', 9:
'S21',
53 10:
'S22', 11:
'S23', 12:
'S24', 13:
'S14', 14:
'S15', 15:
'S16', 16:
'S17', 17:
'S18',
54 18:
'S19', 19:
'S8', 20:
'S9', 21:
'S10', 22:
'S11', 23:
'S12', 24:
'S13', 25:
'S1', 26:
'S2',
55 27:
'S3', 28:
'S4', 29:
'S5', 30:
'S6', 31:
'S7', 32:
'N1', 33:
'N2', 34:
'N3', 35:
'N4',
56 36:
'N5', 37:
'N6', 38:
'N7', 39:
'N8', 40:
'N9', 41:
'N10', 42:
'N11', 43:
'N12', 44:
'N13',
57 45:
'N14', 46:
'N15', 47:
'N16', 48:
'N17', 49:
'N18', 50:
'N19', 51:
'N20', 52:
'N21',
58 53:
'N22', 54:
'N23', 55:
'N24', 56:
'N25', 57:
'N26', 58:
'N27', 59:
'N28', 60:
'N29',
61 def __init__(self, inputPolicy=None, **kwargs):
62 policyFile = Policy.defaultPolicyFile(self.
packageNamepackageName,
"DecamMapper.yaml",
"policy")
63 policy =
Policy(policyFile)
65 super(DecamMapper, self).
__init__(policy, os.path.dirname(policyFile), **kwargs)
67 DECAM_FILTER_DEFINITIONS.defineFilters()
74 for datasetType
in (
"raw",
"instcal",
"dqmask",
"wtmap",
"cpIllumcor"):
75 self.mappings[datasetType].keyDict.update({
'ccdnum': int})
76 self.mappings[
"raw"].keyDict.update({
'object': str})
80 DecamMapper._nbit_tract = 10
81 DecamMapper._nbit_patch = 10
82 DecamMapper._nbit_filter = 4
83 DecamMapper._nbit_id = 64 - (DecamMapper._nbit_tract
84 + 2*DecamMapper._nbit_patch
85 + DecamMapper._nbit_filter)
87 def _extractDetectorName(self, dataId):
90 return DecamMapper.detectorNames[copyId[
'ccdnum']]
92 raise RuntimeError(
"No name found for dataId: %s"%(dataId))
94 def _transformId(self, dataId):
95 copyId = CameraMapper._transformId(self, dataId)
97 copyId.setdefault(
"ccdnum", copyId[
"ccd"])
106 def _computeCcdExposureId(self, dataId):
107 """Compute the 64-bit (long) identifier for a CCD exposure.
112 Data identifier with visit, ccd.
117 Integer identifier for a CCD exposure.
120 visit = copyId[
'visit']
121 ccdnum = copyId[
'ccdnum']
122 return int(
"%07d%02d" % (visit, ccdnum))
124 def _computeCoaddExposureId(self, dataId, singleFilter):
125 """Compute the 64-bit (long) identifier for a coadd.
130 Data identifier with tract and patch.
131 singleFilter : `bool`
132 True means the desired ID is for a single-filter coadd,
133 in which case the dataId must contain filter.
138 Unique integer identifier.
140 tract = int(dataId[
'tract'])
141 if tract < 0
or tract >= 2**DecamMapper._nbit_tract:
142 raise RuntimeError(
'tract not in range [0,%d)' % (2**DecamMapper._nbit_tract))
143 patchX, patchY = [int(x)
for x
in dataId[
'patch'].split(
',')]
144 for p
in (patchX, patchY):
145 if p < 0
or p >= 2**DecamMapper._nbit_patch:
146 raise RuntimeError(
'patch component not in range [0, %d)' % 2**DecamMapper._nbit_patch)
147 oid = (((tract << DecamMapper._nbit_patch) + patchX) << DecamMapper._nbit_patch) + patchY
149 return (oid << DecamMapper._nbit_filter) +
afwImage.Filter(dataId[
'filter']).getId()
156 return 64 - DecamMapper._nbit_id
162 return 64 - DecamMapper._nbit_id
165 return self.
bypass_deepCoaddIdbypass_deepCoaddId(datasetType, pythonType, location, dataId)
179 dqmArr = dqmask.getArray()
181 mArr = mask.getArray()
182 idxBad = np.where(dqmArr & 1)
183 idxSat = np.where(dqmArr & 2)
184 idxIntrp = np.where(dqmArr & 4)
185 idxCr = np.where(dqmArr & 16)
186 idxBleed = np.where(dqmArr & 64)
187 idxEdge = np.where(dqmArr & 512)
188 mArr[idxBad] |= mask.getPlaneBitMask(
"BAD")
189 mArr[idxSat] |= mask.getPlaneBitMask(
"SAT")
190 mArr[idxIntrp] |= mask.getPlaneBitMask(
"INTRP")
191 mArr[idxCr] |= mask.getPlaneBitMask(
"CR")
192 mArr[idxBleed] |= mask.getPlaneBitMask(
"SAT")
193 mArr[idxEdge] |= mask.getPlaneBitMask(
"EDGE")
197 wtmArr = wtmap.getArray()
198 idxUndefWeight = np.where(wtmArr <= 0)
200 wtmArr[idxUndefWeight] =
min(1e-14, np.min(wtmArr[np.where(wtmArr > 0)]))
202 varim = afwImage.ImageF(var)
207 instcalMap = self.map_instcal(dataId)
208 dqmaskMap = self.map_dqmask(dataId)
209 wtmapMap = self.map_wtmap(dataId)
210 instcalType = getattr(afwImage, instcalMap.getPythonType().split(
".")[-1])
211 dqmaskType = getattr(afwImage, dqmaskMap.getPythonType().split(
".")[-1])
212 wtmapType = getattr(afwImage, wtmapMap.getPythonType().split(
".")[-1])
213 instcal = instcalType(instcalMap.getLocationsWithRoot()[0])
214 dqmask = dqmaskType(dqmaskMap.getLocationsWithRoot()[0])
215 wtmap = wtmapType(wtmapMap.getLocationsWithRoot()[0])
220 mi = afwImage.MaskedImageF(afwImage.ImageF(instcal.getImage()), mask, variance)
222 fix_header(md, translator_class=DecamTranslator)
224 exp = afwImage.ExposureF(mi, wcs)
227 visitInfo = self.makeRawVisitInfo(md=md)
228 exp.getInfo().setVisitInfo(visitInfo)
230 for kw
in (
'LTV1',
'LTV2'):
237 """Standardize a raw dataset by converting it to an Exposure.
239 Raw images are MEF files with one HDU for each detector.
243 item : `lsst.afw.image.DecoratedImage`
244 The image read by the butler.
250 result : `lsst.afw.image.Exposure`
251 The standardized Exposure.
253 return self._standardizeExposure(self.exposures[
'raw'], item, dataId,
256 def _createInitialSkyWcs(self, exposure):
261 self._createSkyWcsFromMetadata(exposure)
263 if exposure.getInfo().getVisitInfo()
is None:
264 msg =
"No VisitInfo; cannot access boresight information. Defaulting to metadata-based SkyWcs."
268 newSkyWcs = createInitialSkyWcs(exposure.getInfo().getVisitInfo(), exposure.getDetector(),
270 exposure.setWcs(newSkyWcs)
271 except InitialSkyWcsError
as e:
272 msg =
"Cannot create SkyWcs using VisitInfo and Detector, using metadata-based SkyWcs: %s"
273 self.log.
warn(msg, e)
274 self.log.
debug(
"Exception was: %s", traceback.TracebackException.from_exception(e))
275 if e.__context__
is not None:
276 self.log.
debug(
"Root-cause Exception was: %s",
277 traceback.TracebackException.from_exception(e.__context__))
281 rawPath = self.map_raw(dataId).getLocations()[0]
282 headerPath = re.sub(
r'[\[](\d+)[\]]$',
"[0]", rawPath)
284 fix_header(md0, translator_class=DecamTranslator)
285 visitInfo = self.makeRawVisitInfo(md0)
286 exp.getInfo().setVisitInfo(visitInfo)
287 return self._standardizeExposure(self.calibrations[
"dark"], exp, dataId, filter=
False)
291 return self._standardizeExposure(self.calibrations[
"bias"], exp, dataId, filter=
False)
295 return self._standardizeExposure(self.calibrations[
"flat"], exp, dataId, filter=
True)
299 return self._standardizeExposure(self.calibrations[
"illumcor"], exp, dataId, filter=
True)
301 def _standardizeCpMasterCal(self, datasetType, item, dataId, setFilter=False):
302 """Standardize a MasterCal image obtained from NOAO archive into Exposure
304 These MasterCal images are MEF files with one HDU for each detector.
305 Some WCS header, eg CTYPE1, exists only in the zeroth extensionr,
306 so info in the zeroth header need to be copied over to metadata.
311 Dataset type ("bias", "flat", or "illumcor").
312 item : `lsst.afw.image.DecoratedImage`
313 The image read by the butler.
317 Whether to set the filter in the Exposure.
321 result : `lsst.afw.image.Exposure`
322 The standardized Exposure.
325 md = item.getMetadata()
326 masterCalMap = getattr(self,
"map_" + datasetType)
327 masterCalPath = masterCalMap(dataId).getLocationsWithRoot()[0]
328 headerPath = re.sub(
r'[\[](\d+)[\]]$',
"[0]", masterCalPath)
330 fix_header(md0, translator_class=DecamTranslator)
331 for kw
in (
'CTYPE1',
'CTYPE2',
'CRVAL1',
'CRVAL2',
'CUNIT1',
'CUNIT2',
332 'CD1_1',
'CD1_2',
'CD2_1',
'CD2_2'):
333 if kw
in md0.paramNames()
and kw
not in md.paramNames():
334 md.add(kw, md0.getScalar(kw))
338 return self._standardizeExposure(self.calibrations[datasetType], exp, dataId, filter=setFilter)
348 return self._standardizeExposure(self.calibrations[
"fringe"], exp, dataId)
355 """Directory containing linearizers"""
356 packageName = cls.getPackageName()
358 return os.path.join(packageDir,
"decam",
"linearizer")
361 """Map a linearizer"""
363 location =
"%02d.fits" % (dataId[
"ccdnum"])
365 pythonType=
"lsst.ip.isr.LinearizeSquared",
367 storageName=
"PickleStorage",
368 locationList=[location],
376 """Directory containing crosstalk tables.
378 packageName = cls.getPackageName()
380 return os.path.join(packageDir,
"decam",
"crosstalk")
Represent a 2-dimensional array of bitmask pixels.
def bypass_deepMergedCoaddId(self, datasetType, pythonType, location, dataId)
def translate_wtmap(self, wtmap)
def bypass_deepMergedCoaddId_bits(self, *args, **kwargs)
def std_cpIllumcor(self, item, dataId)
def std_dark(self, item, dataId)
def std_fringe(self, item, dataId)
def bypass_dcrMergedCoaddId_bits(self, *args, **kwargs)
def bypass_dcrCoaddId_bits(self, *args, **kwargs)
def bypass_ccdExposureId(self, datasetType, pythonType, location, dataId)
def bypass_deepCoaddId(self, datasetType, pythonType, location, dataId)
def std_illumcor(self, item, dataId)
def bypass_dcrCoaddId(self, datasetType, pythonType, location, dataId)
def bypass_ccdExposureId_bits(self, datasetType, pythonType, location, dataId)
def bypass_dcrMergedCoaddId(self, datasetType, pythonType, location, dataId)
def std_raw(self, item, dataId)
def map_linearizer(self, dataId, write=False)
def _standardizeCpMasterCal(self, datasetType, item, dataId, setFilter=False)
def __init__(self, inputPolicy=None, **kwargs)
def _transformId(self, dataId)
def bypass_instcal(self, datasetType, pythonType, butlerLocation, dataId)
def std_bias(self, item, dataId)
def _computeCoaddExposureId(self, dataId, singleFilter)
def std_flat(self, item, dataId)
def _computeCcdExposureId(self, dataId)
def translate_dqmask(self, dqmask)
def std_cpBias(self, item, dataId)
def getLinearizerDir(cls)
def std_cpFlat(self, item, dataId)
def bypass_deepCoaddId_bits(self, *args, **kwargs)
std::shared_ptr< daf::base::PropertyList > readMetadata(std::string const &fileName, int hdu=DEFAULT_HDU, bool strip=false)
Read FITS header.
std::shared_ptr< SkyWcs > makeSkyWcs(daf::base::PropertySet &metadata, bool strip=false)
Construct a SkyWcs from FITS keywords.
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
std::shared_ptr< PhotoCalib > makePhotoCalibFromCalibZeroPoint(double instFluxMag0, double instFluxMag0Err)
Construct a PhotoCalib from the deprecated Calib-style instFluxMag0/instFluxMag0Err values.
std::shared_ptr< Exposure< ImagePixelT, MaskPixelT, VariancePixelT > > makeExposure(MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > &mimage, std::shared_ptr< geom::SkyWcs const > wcs=std::shared_ptr< geom::SkyWcs const >())
A function to return an Exposure of the correct type (cf.
MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > * makeMaskedImage(typename std::shared_ptr< Image< ImagePixelT >> image, typename std::shared_ptr< Mask< MaskPixelT >> mask=Mask< MaskPixelT >(), typename std::shared_ptr< Image< VariancePixelT >> variance=Image< VariancePixelT >())
A function to return a MaskedImage of the correct type (cf.
std::string getPackageDir(std::string const &packageName)
return the root directory of a setup package