Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# 

2# This file is part of obs_lsst. 

3# 

4# Developed for the LSST Data Management System. 

5# This product includes software developed by the LSST Project 

6# (http://www.lsst.org). 

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

8# for details of code ownership. 

9# 

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

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

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

13# (at your option) any later version. 

14# 

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

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

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

18# GNU General Public License for more details. 

19# 

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

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

22 

23""" 

24A python script to read phoSim's headers and write the per-raft datafiles 

25used by generateCamera.py 

26 

27This is not really part of obs_lsst, but it's useful to keep it here. 

28""" 

29 

30__all__ = ("main",) 

31 

32import argparse 

33import re 

34import sys 

35import os 

36 

37import lsst.log 

38import lsst.daf.persistence as dafPersist 

39 

40logger = lsst.log.getLogger(__name__) 

41 

42 

43def writeRaftFile(fd, raftName, detectorType, raftSerial, ccdData): 

44 print(f"""\ 

45{raftName} : 

46 detectorType : {detectorType} 

47 raftSerial : {raftSerial} 

48 

49 ccdSerials : 

50 S00 : ITL-3800C-145-Dev 

51 S01 : ITL-3800C-022-Dev 

52 S02 : ITL-3800C-041-Dev 

53 S10 : ITL-3800C-100-Dev 

54 S11 : ITL-3800C-017-Dev 

55 S12 : ITL-3800C-018-Dev 

56 S20 : ITL-3800C-102-Dev 

57 S21 : ITL-3800C-146-Dev 

58 S22 : ITL-3800C-103-Dev 

59 

60 amplifiers :\ 

61""", file=fd) 

62 for ccdName in ccdData: 

63 print(f" {ccdName} :", file=fd) 

64 

65 for ampName, (gain, readNoise) in ccdData[ccdName].items(): 

66 print(f" {ampName} : {{ gain : {gain:5.3f}, readNoise : {readNoise:4.2f} }}", file=fd) 

67 

68 

69def build_argparser(): 

70 """Construct an argument parser for the ``generateCamera.py`` script. 

71 

72 Returns 

73 ------- 

74 argparser : `argparse.ArgumentParser` 

75 The argument parser that defines the ``translate_header.py`` 

76 command-line interface. 

77 """ 

78 parser = argparse.ArgumentParser(description="Read phoSim headers and write the per-raft datafiles") 

79 

80 parser.add_argument('input', type=str, help="Path to input data repository") 

81 parser.add_argument('--id', type=str, help="ID for data (visit=XXX)", default=None) 

82 parser.add_argument('--visit', type=int, help="visit to read", default=None) 

83 parser.add_argument('--log', type=str, help="Specify logging level to use", default="WARN") 

84 parser.add_argument('--output_dir', type=str, 

85 help="Path to output data directory (must exist)", default=None) 

86 

87 return parser 

88 

89 

90def processPhosimData(ids, visit, inputDir, outputDir): 

91 """Process the specified data. 

92 

93 Parameters 

94 ---------- 

95 ids : `str` 

96 DataId to be used to access the data. Can only be given as 

97 ``visit=NNN`` form. ``visit`` is used if this is `None`. 

98 ``expId`` is assumed to be an alias for ``visit`` in PhoSim. 

99 visit : `int` 

100 Explicit visit number to read from repository. Only used if 

101 ``ids`` is `None` or to check that it is consistent with the value 

102 in ``ids``. 

103 inputDir : `str` 

104 Location of input butler repository. 

105 outputDir : `str` 

106 Location to write raft files. Will use current directory if `None`. 

107 

108 Notes 

109 ----- 

110 If no visit is specified in the ``ids`` or ``visit`` arguments the 

111 repository is queried and the first visit found is used. 

112 

113 Raises 

114 ------ 

115 RuntimeError: 

116 Raised if there is an inconsistency with the visit specification. 

117 Other exceptions may be raised by butler usage. 

118 """ 

119 if ids is not None: 

120 mat = re.search(r"visit=(\d+)", ids) 

121 if not mat: 

122 raise RuntimeError("Please specify an integer visit in the id string") 

123 idvisit = int(mat.group(1)) 

124 

125 # Only allowed to specify a visit and id if they happen to be the same 

126 if visit is None: 

127 visit = idvisit 

128 elif idvisit != visit: 

129 raise RuntimeError("Please specify either --id or --visit (or be consistent)") 

130 

131 butler = dafPersist.Butler(inputDir) 

132 # 

133 # Lookup the amp names 

134 # 

135 camera = butler.get("camera") 

136 det = list(camera)[0] 

137 ampNames = [a.getName() for a in det] 

138 

139 if visit is None: 

140 rawData = butler.queryMetadata("raw", ["visit"]) 

141 if not rawData: 

142 raise RuntimeError(f"No raw data found in butler repository at {inputDir}") 

143 visit = rawData[0] 

144 

145 # For phosim we declare that visit==expId 

146 dataId = dict(expId=visit) 

147 # 

148 # Due to butler stupidity it can't/won't lookup things when you also 

149 # specify a channel. Sigh 

150 # 

151 queryResults = butler.queryMetadata("raw", ["run", "snap"], dataId) 

152 if not queryResults: 

153 raise RuntimeError(f"Unable to find any data to match dataId {dataId}") 

154 

155 dataId["run"], dataId["snap"] = queryResults[0] 

156 

157 logger.info("DataId = %s", dataId) 

158 

159 raftData = {} 

160 for raftName, detectorName, detector in \ 

161 butler.queryMetadata("raw", ["raftName", "detectorName", "detector"], dataId): 

162 

163 dataId["detector"] = detector # more of the butler stupidity 

164 

165 logger.warn("Processing data from detector %s_%s", raftName, detectorName) 

166 

167 if raftName not in raftData: 

168 raftData[raftName] = {} 

169 if detectorName not in raftData[raftName]: 

170 raftData[raftName][detectorName] = {} 

171 

172 for i, ampName in enumerate(ampNames, 1): 

173 md = butler.get("raw_amp_md", dataId, 

174 raftName=raftName, detectorName=detectorName, channel=i) 

175 gain = md["GAIN"] 

176 readNoise = md["RDNOISE"] 

177 assert(ampName[-2:] == md["EXTNAME"][-2:]) 

178 

179 raftData[raftName][detectorName][ampName] = (gain, readNoise) 

180 

181 raftId = 0 

182 for raftName, ccdData in raftData.items(): 

183 raftSerial = f"LCA-11021_RTM-{raftId:03d}" # won't be deterministic in reality! 

184 raftId += 1 

185 

186 if raftName in ("R00", "R40", "R04", "R44"): 

187 continue 

188 if outputDir: 

189 prefix = outputDir 

190 else: 

191 prefix = os.path.curdir 

192 outfile = os.path.join(prefix, f"{raftName}.yaml") 

193 logger.debug("Writing raft information to %s", outfile) 

194 with open(outfile, "w") as fd: 

195 writeRaftFile(fd, raftName, "ITL", raftSerial, raftData[raftName]) 

196 

197 return 

198 

199 

200def main(): 

201 args = build_argparser().parse_args() 

202 

203 logLevel = args.log.upper() 

204 logger.setLevel(getattr(logger, logLevel)) 

205 

206 try: 

207 processPhosimData(args.id, args.visit, args.input, args.output_dir) 

208 except Exception as e: 

209 print(f"{e}", file=sys.stderr) 

210 return 1 

211 return 0