Coverage for python/lsst/ip/isr/transmissionCurve.py: 22%

88 statements  

« prev     ^ index     » next       coverage.py v7.2.5, created at 2023-05-02 11:12 -0700

1# This file is part of ip_isr. 

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__ = ["IntermediateTransmissionCurve", 

23 "IntermediateOpticsTransmissionCurve", 

24 "IntermediateFilterTransmissionCurve", 

25 "IntermediateSensorTransmissionCurve", 

26 "IntermediateAtmosphereTransmissionCurve", 

27 "IntermediateSystemTransmissionCurve", ] 

28 

29import numpy as np 

30from astropy import units as u 

31from astropy.units import cds 

32 

33from lsst.afw.image import TransmissionCurve 

34from .calibType import IsrCalib 

35 

36 

37class IntermediateTransmissionCurve(IsrCalib): 

38 """Definition for the TransmissionCurve format used as inputs. 

39 

40 Parameters 

41 ---------- 

42 filename : `str` 

43 Filename of a transmission curve dataset. 

44 """ 

45 

46 _OBSTYPE = "transmission_curve" 

47 _SCHEMA = "" 

48 _VERSION = 1.0 

49 

50 def __init__(self, filename=None): 

51 self.data = None 

52 self.transmissionCurve = None 

53 self.isSpatiallyConstant = True 

54 super().__init__() 

55 

56 # Because we are not persisting this calib as itself, we 

57 # should skip adding any other attributes. 

58 self.requiredAttributes.update(['isSpatiallyConstant']) 

59 

60 def setMetadata(self, metadata): 

61 # Inherits from lsst.ip.isr.IsrCalib.setMetadata. 

62 super().setMetadata(metadata) 

63 

64 @classmethod 

65 def fromTable(cls, tableList): 

66 """Construct intermediate transmission curve from a list of input 

67 tables. Only the first table is used. 

68 

69 Parameters 

70 ---------- 

71 tableList : `list` [`astropy.table.Table`] 

72 List containing input tables. 

73 

74 Returns 

75 ------- 

76 calib : `lsst.ip.isr.IntermediateTransmissionCurve` 

77 The final calibration. 

78 """ 

79 calib = cls() 

80 

81 metadata = tableList[0].meta 

82 calib.setMetadata(metadata) 

83 calib.updateMetadata() 

84 

85 calib.data = tableList[0] 

86 calib.setTransmissionCurveRepresentation() 

87 return calib 

88 

89 def setTransmissionCurveRepresentation(self): 

90 """Construct transmission curve representation from the data that was 

91 read. 

92 

93 Raises 

94 ------ 

95 RuntimeError 

96 This is raised if no table data exists in the calibration, 

97 if there are array length mismatches, or if the wavelength 

98 sampling for multi-amp tables differ. 

99 """ 

100 if self.data is None: 

101 raise RuntimeError("No table data was found to convert to a transmission curve!") 

102 

103 throughputKey = None 

104 if 'wavelength' not in self.data.columns: 

105 raise RuntimeError("Expected column [wavelength] not found.") 

106 if 'efficiency' in self.data.columns: 

107 throughputKey = 'efficiency' 

108 elif 'throughput' in self.data.columns: 

109 throughputKey = 'throughput' 

110 else: 

111 raise RuntimeError("Expected columns [throughput|efficiency] not found.") 

112 

113 doAverageCurves = False 

114 if 'amp_name' in self.data.columns: 

115 doAverageCurves = True 

116 

117 # These will be used to construct the 

118 # ``lsst.afw.image.TransmissionCurve`` object. 

119 wavelengths = None 

120 throughput = None 

121 

122 if doAverageCurves: 

123 curveStack = None 

124 amplifierNames = set(self.data['amp_name']) 

125 comparisonIndices = np.where(self.data['amp_name'] == next(iter(amplifierNames))) 

126 wavelengths = self.data[comparisonIndices]['wavelength'] 

127 

128 for amplifier in amplifierNames: 

129 indices = np.where(self.data['amp_name'] == amplifier) 

130 if len(self.data[indices]) != len(self.data[comparisonIndices]): 

131 raise RuntimeError("Incompatible lengths in average.") 

132 if not np.array_equal(self.data[indices]['wavelength'], wavelengths): 

133 raise RuntimeError("Mismatch in wavelength samples.") 

134 

135 if curveStack is not None: 

136 curveStack = np.column_stack((curveStack, self.data[indices][throughputKey])) 

137 else: 

138 curveStack = self.data[indices][throughputKey] 

139 throughput = np.mean(curveStack, 1) 

140 

141 # This averaging operation has stripped units. 

142 throughput = throughput * self.data[throughputKey].unit 

143 else: 

144 wavelengths = self.data['wavelength'] 

145 throughput = self.data[throughputKey] 

146 

147 # Convert units: 

148 # import pdb; pdb.set_trace() 

149 with cds.enable(): 

150 # These need to be in Angstroms, for consistency. 

151 wavelengths = wavelengths.to(u.Angstrom).to_value() 

152 

153 if throughput.unit != u.dimensionless_unscaled and throughput.unit != u.UnrecognizedUnit('-'): 

154 # These need to be fractions, not percent. 

155 throughput = throughput.to(u.dimensionless_unscaled).to_value() 

156 

157 self.transmissionCurve = TransmissionCurve.makeSpatiallyConstant( 

158 throughput.astype(np.float64), 

159 wavelengths.astype(np.float64), 

160 throughputAtMin=0.0, 

161 throughputAtMax=0.0 

162 ) 

163 

164 def getTransmissionCurve(self): 

165 if self.transmissionCurve is None and self.data is None: 

166 raise RuntimeError("No transmission curve data found.") 

167 if self.transmissionCurve is None: 

168 self.setTransmissionCurveRepresentation() 

169 return self.transmissionCurve 

170 

171 def writeFits(self, outputFilename): 

172 """Write the transmission curve data to a file. 

173 

174 Parameters 

175 ---------- 

176 outputFilename : `str` 

177 Destination filename. 

178 

179 Returns 

180 ------- 

181 outputFilename : `str` 

182 The output filename actually used. 

183 

184 Raises 

185 ------ 

186 RuntimeError 

187 Raised if no transmission curve can be created. 

188 """ 

189 if self.transmissionCurve is None and self.data is None: 

190 raise RuntimeError("No transmission curve data found.") 

191 if self.transmissionCurve is None: 

192 self.setTransmissionCurveRepresentation() 

193 

194 return self.transmissionCurve.writeFits(outputFilename) 

195 

196 

197class IntermediateSensorTransmissionCurve(IntermediateTransmissionCurve): 

198 _OBSTYPE = 'transmission_sensor' 

199 

200 

201class IntermediateFilterTransmissionCurve(IntermediateTransmissionCurve): 

202 _OBSTYPE = 'transmission_filter' 

203 

204 

205class IntermediateOpticsTransmissionCurve(IntermediateTransmissionCurve): 

206 _OBSTYPE = 'transmission_optics' 

207 

208 

209class IntermediateAtmosphereTransmissionCurve(IntermediateTransmissionCurve): 

210 _OBSTYPE = 'transmission_atmosphere' 

211 

212 

213class IntermediateSystemTransmissionCurve(IntermediateTransmissionCurve): 

214 _OBSTYPE = 'transmission_system'