Coverage for python/lsst/obs/lsst/script/phosimToRafts.py : 11%

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/>.
23"""
24A python script to read phoSim's headers and write the per-raft datafiles
25used by generateCamera.py
27This is not really part of obs_lsst, but it's useful to keep it here.
28"""
30__all__ = ("main",)
32import argparse
33import re
34import sys
35import os
37import lsst.log
38import lsst.daf.persistence as dafPersist
40logger = lsst.log.getLogger(__name__)
43def writeRaftFile(fd, raftName, detectorType, raftSerial, ccdData):
44 print(f"""\
45{raftName} :
46 detectorType : {detectorType}
47 raftSerial : {raftSerial}
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
60 amplifiers :\
61""", file=fd)
62 for ccdName in ccdData:
63 print(f" {ccdName} :", file=fd)
65 for ampName, (gain, readNoise) in ccdData[ccdName].items():
66 print(f" {ampName} : {{ gain : {gain:5.3f}, readNoise : {readNoise:4.2f} }}", file=fd)
69def build_argparser():
70 """Construct an argument parser for the ``generateCamera.py`` script.
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")
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)
87 return parser
90def processPhosimData(ids, visit, inputDir, outputDir):
91 """Process the specified data.
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`.
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.
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))
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)")
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]
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]
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}")
155 dataId["run"], dataId["snap"] = queryResults[0]
157 logger.info("DataId = %s", dataId)
159 raftData = {}
160 for raftName, detectorName, detector in \
161 butler.queryMetadata("raw", ["raftName", "detectorName", "detector"], dataId):
163 dataId["detector"] = detector # more of the butler stupidity
165 logger.warn("Processing data from detector %s_%s", raftName, detectorName)
167 if raftName not in raftData:
168 raftData[raftName] = {}
169 if detectorName not in raftData[raftName]:
170 raftData[raftName][detectorName] = {}
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:])
179 raftData[raftName][detectorName][ampName] = (gain, readNoise)
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
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])
197 return
200def main():
201 args = build_argparser().parse_args()
203 logLevel = args.log.upper()
204 logger.setLevel(getattr(logger, logLevel))
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