29from astro_metadata_translator
import fix_header, DecamTranslator
34from lsst.obs.base.utils
import createInitialSkyWcs, InitialSkyWcsError
36from .makeDecamRawVisitInfo
import MakeDecamRawVisitInfo
37from ._instrument
import DarkEnergyCamera
39np.seterr(divide=
"ignore")
41__all__ = [
"DecamMapper"]
45 packageName =
'obs_decam'
46 _gen3instrument = DarkEnergyCamera
48 MakeRawVisitInfoClass = MakeDecamRawVisitInfo
51 1:
'S29', 2:
'S30', 3:
'S31', 4:
'S25', 5:
'S26', 6:
'S27', 7:
'S28', 8:
'S20', 9:
'S21',
52 10:
'S22', 11:
'S23', 12:
'S24', 13:
'S14', 14:
'S15', 15:
'S16', 16:
'S17', 17:
'S18',
53 18:
'S19', 19:
'S8', 20:
'S9', 21:
'S10', 22:
'S11', 23:
'S12', 24:
'S13', 25:
'S1', 26:
'S2',
54 27:
'S3', 28:
'S4', 29:
'S5', 30:
'S6', 31:
'S7', 32:
'N1', 33:
'N2', 34:
'N3', 35:
'N4',
55 36:
'N5', 37:
'N6', 38:
'N7', 39:
'N8', 40:
'N9', 41:
'N10', 42:
'N11', 43:
'N12', 44:
'N13',
56 45:
'N14', 46:
'N15', 47:
'N16', 48:
'N17', 49:
'N18', 50:
'N19', 51:
'N20', 52:
'N21',
57 53:
'N22', 54:
'N23', 55:
'N24', 56:
'N25', 57:
'N26', 58:
'N27', 59:
'N28', 60:
'N29',
60 def __init__(self, inputPolicy=None, **kwargs):
61 policyFile = Policy.defaultPolicyFile(self.
packageNamepackageName,
"DecamMapper.yaml",
"policy")
62 policy =
Policy(policyFile)
64 super(DecamMapper, self).
__init__(policy, os.path.dirname(policyFile), **kwargs)
71 for datasetType
in (
"raw",
"instcal",
"dqmask",
"wtmap",
"cpIllumcor"):
72 self.mappings[datasetType].keyDict.update({
'ccdnum': int})
73 self.mappings[
"raw"].keyDict.update({
'object': str})
77 DecamMapper._nbit_tract = 10
78 DecamMapper._nbit_patch = 10
79 DecamMapper._nbit_filter = 4
80 DecamMapper._nbit_id = 64 - (DecamMapper._nbit_tract
81 + 2*DecamMapper._nbit_patch
82 + DecamMapper._nbit_filter)
84 def _extractDetectorName(self, dataId):
87 return DecamMapper.detectorNames[copyId[
'ccdnum']]
89 raise RuntimeError(
"No name found for dataId: %s"%(dataId))
91 def _transformId(self, dataId):
92 copyId = CameraMapper._transformId(self, dataId)
94 copyId.setdefault(
"ccdnum", copyId[
"ccd"])
103 def _computeCcdExposureId(self, dataId):
104 """Compute the 64-bit (long) identifier for a CCD exposure.
109 Data identifier with visit, ccd.
114 Integer identifier
for a CCD exposure.
117 visit = copyId['visit']
118 ccdnum = copyId[
'ccdnum']
119 return int(
"%07d%02d" % (visit, ccdnum))
121 def _computeCoaddExposureId(self, dataId, singleFilter):
122 """Compute the 64-bit (long) identifier for a coadd.
127 Data identifier with tract
and patch.
128 singleFilter : `bool`
129 True means the desired ID
is for a single-filter coadd,
130 in which case the dataId must contain filter.
135 Unique integer identifier.
137 tract = int(dataId['tract'])
138 if tract < 0
or tract >= 2**DecamMapper._nbit_tract:
139 raise RuntimeError(
'tract not in range [0,%d)' % (2**DecamMapper._nbit_tract))
140 patchX, patchY = [
int(x)
for x
in dataId[
'patch'].split(
',')]
141 for p
in (patchX, patchY):
142 if p < 0
or p >= 2**DecamMapper._nbit_patch:
143 raise RuntimeError(
'patch component not in range [0, %d)' % 2**DecamMapper._nbit_patch)
144 oid = (((tract << DecamMapper._nbit_patch) + patchX) << DecamMapper._nbit_patch) + patchY
146 return (oid << DecamMapper._nbit_filter) + afwImage.Filter(dataId[
'filter']).getId()
153 return 64 - DecamMapper._nbit_id
159 return 64 - DecamMapper._nbit_id
162 return self.
bypass_deepCoaddIdbypass_deepCoaddId(datasetType, pythonType, location, dataId)
176 dqmArr = dqmask.getArray()
178 mArr = mask.getArray()
179 idxBad = np.where(dqmArr & 1)
180 idxSat = np.where(dqmArr & 2)
181 idxIntrp = np.where(dqmArr & 4)
182 idxCr = np.where(dqmArr & 16)
183 idxBleed = np.where(dqmArr & 64)
184 idxEdge = np.where(dqmArr & 512)
185 mArr[idxBad] |= mask.getPlaneBitMask(
"BAD")
186 mArr[idxSat] |= mask.getPlaneBitMask(
"SAT")
187 mArr[idxIntrp] |= mask.getPlaneBitMask(
"INTRP")
188 mArr[idxCr] |= mask.getPlaneBitMask(
"CR")
189 mArr[idxBleed] |= mask.getPlaneBitMask(
"SAT")
190 mArr[idxEdge] |= mask.getPlaneBitMask(
"EDGE")
194 wtmArr = wtmap.getArray()
195 idxUndefWeight = np.where(wtmArr <= 0)
197 wtmArr[idxUndefWeight] =
min(1e-14, np.min(wtmArr[np.where(wtmArr > 0)]))
199 varim = afwImage.ImageF(var)
204 instcalMap = self.map_instcal(dataId)
205 dqmaskMap = self.map_dqmask(dataId)
206 wtmapMap = self.map_wtmap(dataId)
207 instcalType = getattr(afwImage, instcalMap.getPythonType().split(
".")[-1])
208 dqmaskType = getattr(afwImage, dqmaskMap.getPythonType().split(
".")[-1])
209 wtmapType = getattr(afwImage, wtmapMap.getPythonType().split(
".")[-1])
210 instcal = instcalType(instcalMap.getLocationsWithRoot()[0])
211 dqmask = dqmaskType(dqmaskMap.getLocationsWithRoot()[0])
212 wtmap = wtmapType(wtmapMap.getLocationsWithRoot()[0])
217 mi = afwImage.MaskedImageF(afwImage.ImageF(instcal.getImage()), mask, variance)
219 fix_header(md, translator_class=DecamTranslator)
221 exp = afwImage.ExposureF(mi, wcs)
224 visitInfo = self.makeRawVisitInfo(md=md)
225 exp.getInfo().setVisitInfo(visitInfo)
227 exp.info.id = md[
'EXPID']
229 for kw
in (
'LTV1',
'LTV2'):
236 """Standardize a raw dataset by converting it to an Exposure.
238 Raw images are MEF files with one HDU
for each detector.
243 The image read by the butler.
250 The standardized Exposure.
252 return self._standardizeExposure(self.exposures[
'raw'], item, dataId,
253 trimmed=
False, setExposureId=
True)
255 def _createInitialSkyWcs(self, exposure):
260 self._createSkyWcsFromMetadata(exposure)
262 if exposure.getInfo().getVisitInfo()
is None:
263 msg =
"No VisitInfo; cannot access boresight information. Defaulting to metadata-based SkyWcs."
267 newSkyWcs = createInitialSkyWcs(exposure.getInfo().getVisitInfo(), exposure.getDetector(),
269 exposure.setWcs(newSkyWcs)
270 except InitialSkyWcsError
as e:
271 msg =
"Cannot create SkyWcs using VisitInfo and Detector, using metadata-based SkyWcs: %s"
272 self.log.
warn(msg, e)
273 self.log.
debug(
"Exception was: %s", traceback.TracebackException.from_exception(e))
274 if e.__context__
is not None:
275 self.log.
debug(
"Root-cause Exception was: %s",
276 traceback.TracebackException.from_exception(e.__context__))
280 rawPath = self.map_raw(dataId).getLocations()[0]
281 headerPath = re.sub(
r'[\[](\d+)[\]]$',
"[0]", rawPath)
283 fix_header(md0, translator_class=DecamTranslator)
284 visitInfo = self.makeRawVisitInfo(md0)
285 exp.getInfo().setVisitInfo(visitInfo)
286 return self._standardizeExposure(self.calibrations[
"dark"], exp, dataId, filter=
False)
290 return self._standardizeExposure(self.calibrations[
"bias"], exp, dataId, filter=
False)
294 return self._standardizeExposure(self.calibrations[
"flat"], exp, dataId, filter=
True)
298 return self._standardizeExposure(self.calibrations[
"illumcor"], exp, dataId, filter=
True)
300 def _standardizeCpMasterCal(self, datasetType, item, dataId, setFilter=False):
301 """Standardize a MasterCal image obtained from NOAO archive into Exposure
303 These MasterCal images are MEF files with one HDU
for each detector.
304 Some WCS header, eg CTYPE1, exists only
in the zeroth extensionr,
305 so info
in the zeroth header need to be copied over to metadata.
310 Dataset type (
"bias",
"flat",
or "illumcor").
312 The image read by the butler.
316 Whether to set the filter
in the Exposure.
321 The standardized Exposure.
324 md = item.getMetadata()
325 masterCalMap = getattr(self, "map_" + datasetType)
326 masterCalPath = masterCalMap(dataId).getLocationsWithRoot()[0]
327 headerPath = re.sub(
r'[\[](\d+)[\]]$',
"[0]", masterCalPath)
329 fix_header(md0, translator_class=DecamTranslator)
330 for kw
in (
'CTYPE1',
'CTYPE2',
'CRVAL1',
'CRVAL2',
'CUNIT1',
'CUNIT2',
331 'CD1_1',
'CD1_2',
'CD2_1',
'CD2_2'):
332 if kw
in md0.paramNames()
and kw
not in md.paramNames():
333 md.add(kw, md0.getScalar(kw))
337 return self._standardizeExposure(self.calibrations[datasetType], exp, dataId, filter=setFilter)
347 return self._standardizeExposure(self.calibrations[
"fringe"], exp, dataId)
354 """Directory containing linearizers"""
355 packageName = cls.getPackageName()
357 return os.path.join(packageDir,
"decam",
"linearizer")
360 """Map a linearizer"""
362 location = "%02d.fits" % (dataId[
"ccdnum"])
364 pythonType=
"lsst.ip.isr.LinearizeSquared",
366 storageName=
"PickleStorage",
367 locationList=[location],
375 """Directory containing crosstalk tables.
377 packageName = cls.getPackageName()
379 return os.path.join(packageDir,
"decam",
"crosstalk")
A container for an Image and its associated metadata.
A class to contain the data, WCS, and other information needed to describe an image of the sky.
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.
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::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.
std::string getPackageDir(std::string const &packageName)
return the root directory of a setup package