Coverage for python/lsst/obs/base/exposureIdInfo.py: 29%

30 statements  

« prev     ^ index     » next       coverage.py v6.4.1, created at 2022-06-28 02:15 -0700

1# This file is part of obs_base. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <https://www.gnu.org/licenses/>. 

21 

22__all__ = ["ExposureIdInfo"] 

23 

24from lsst.afw.table import IdFactory 

25from lsst.daf.butler import DataCoordinate 

26 

27 

28class ExposureIdInfo: 

29 """Struct representing an exposure ID and the number of bits it uses. 

30 

31 Parameters 

32 ---------- 

33 expId : `int` 

34 Exposure ID. Note that this is typically the ID of an 

35 `afw.image.Exposure`, not the ID of an actual observation, and hence it 

36 usually either includes a detector component or is derived from SkyMap 

37 IDs, and the observation ID component usually represents a ``visit`` 

38 rather than ``exposure``. For code using the Gen3 butler, this will 

39 usually be obtained via a `~lsst.daf.butler.DimensionPacker` (see 

40 example below). 

41 expBits : `int` 

42 Maximum number of bits allowed for exposure IDs of this type. 

43 maxBits : `int`, optional 

44 Maximum number of bits available for values that combine exposure ID 

45 with other information, such as source ID. If not provided 

46 (recommended when possible), `unusedBits` will be computed by assuming 

47 the full ID must fit an an `lsst.afw.table` RecordId field. 

48 

49 Examples 

50 -------- 

51 One common use is creating an ID factory for making a source table. 

52 For example, given a `ExposureIdInfo` instance ``info``, 

53 

54 .. code-block:: python 

55 

56 from lsst.afw.table import SourceTable 

57 schema = SourceTable.makeMinimalSchema() 

58 #...add fields to schema as desired, then... 

59 sourceTable = SourceTable.make(self.schema, info.makeSourceIdFactory()) 

60 

61 An `ExposureIdInfo` instance can be obtained from a Gen2 data butler 

62 ``butler`` and dictionary ``dataId`` that identifies a visit and a detector 

63 via 

64 

65 .. code-block:: python 

66 

67 info = butler.get("expIdInfo", dataId) 

68 

69 The Gen3 version is 

70 

71 .. code-block:: python 

72 

73 expandedDataId = butler.registry.expandDataId(dataId) 

74 info = ExposureIdInfo.fromDataId(expandedDataId, "visit_detector") 

75 

76 The first line should be unnecessary for the data IDs passed to 

77 `~lsst.pipe.base.PipelineTask` methods, as those are already expanded, and 

78 ``"visit_detector"`` can be replaced by other strings to pack data IDs with 

79 different dimensions (e.g. ``"tract_patch"`` or ``"tract_patch_band"``); 

80 see the data repository's dimensions configuration for other options. 

81 

82 At least one bit must be reserved for the exposure ID, even if there is no 

83 exposure ID, for reasons that are not entirely clear (this is DM-6664). 

84 """ 

85 

86 def __init__(self, expId=0, expBits=1, maxBits=None): 

87 """Construct an ExposureIdInfo 

88 

89 See the class doc string for an explanation of the arguments. 

90 """ 

91 expId = int(expId) 

92 expBits = int(expBits) 

93 

94 if expId.bit_length() > expBits: 

95 raise RuntimeError("expId=%s uses %s bits > expBits=%s" % (expId, expId.bit_length(), expBits)) 

96 

97 self.expId = expId 

98 self.expBits = expBits 

99 

100 if maxBits is not None: 

101 maxBits = int(maxBits) 

102 if maxBits < expBits: 

103 raise RuntimeError("expBits=%s > maxBits=%s" % (expBits, maxBits)) 

104 self.maxBits = maxBits 

105 

106 @classmethod 

107 def fromDataId(cls, dataId, name="visit_detector", maxBits=None): 

108 """Construct an instance from a fully-expanded data ID. 

109 

110 Parameters 

111 ---------- 

112 dataId : `lsst.daf.butler.DataCoordinate` 

113 An expanded data ID that identifies the dimensions to be packed and 

114 contains extra information about the maximum values for those 

115 dimensions. An expanded data ID can be obtained from 

116 `Registry.expandDataId`, but all data IDs passed to `PipelineTask` 

117 methods should already be expanded. 

118 name : `str`, optional 

119 Name of the packer to use. The set of available packers can be 

120 found in the data repository's dimension configuration (see the 

121 "packers" section of ``dimensions.yaml`` in ``daf_butler`` for the 

122 defaults). 

123 maxBits : `int`, optional 

124 Forwarded as the ``__init__`` parameter of the same name. Should 

125 usually be unnecessary. 

126 

127 Returns 

128 ------- 

129 info : `ExposureIdInfo` 

130 An `ExposureIdInfo` instance. 

131 """ 

132 if not isinstance(dataId, DataCoordinate) or not dataId.hasRecords(): 

133 raise RuntimeError( 

134 "A fully-expanded data ID is required; use Registry.expandDataId to obtain one." 

135 ) 

136 expId, expBits = dataId.pack(name, returnMaxBits=True) 

137 return cls(expId=expId, expBits=expBits, maxBits=maxBits) 

138 

139 @property 

140 def unusedBits(self): 

141 """Maximum number of bits available for non-exposure info `(int)`.""" 

142 if self.maxBits is None: 

143 from lsst.afw.table import IdFactory 

144 

145 return IdFactory.computeReservedFromMaxBits(self.expBits) 

146 else: 

147 return self.maxBits - self.expBits 

148 

149 def makeSourceIdFactory(self): 

150 """Make a `lsst.afw.table.SourceTable.IdFactory` instance from this 

151 exposure information. 

152 

153 Returns 

154 ------- 

155 idFactory : `lsst.afw.table.SourceTable.IdFactory` 

156 An ID factory that generates new IDs that fold in the image IDs 

157 managed by this object. 

158 """ 

159 return IdFactory.makeSource(self.expId, self.unusedBits)