LSST Applications g0da5cf3356+25b44625d0,g17e5ecfddb+50a5ac4092,g1c76d35bf8+585f0f68a2,g295839609d+8ef6456700,g2e2c1a68ba+cc1f6f037e,g38293774b4+62d12e78cb,g3b44f30a73+2891c76795,g48ccf36440+885b902d19,g4b2f1765b6+0c565e8f25,g5320a0a9f6+bd4bf1dc76,g56364267ca+403c24672b,g56b687f8c9+585f0f68a2,g5c4744a4d9+78cd207961,g5ffd174ac0+bd4bf1dc76,g6075d09f38+3075de592a,g667d525e37+cacede5508,g6f3e93b5a3+da81c812ee,g71f27ac40c+cacede5508,g7212e027e3+eb621d73aa,g774830318a+18d2b9fa6c,g7985c39107+62d12e78cb,g79ca90bc5c+fa2cc03294,g881bdbfe6c+cacede5508,g91fc1fa0cf+82a115f028,g961520b1fb+2534687f64,g96f01af41f+f2060f23b6,g9ca82378b8+cacede5508,g9d27549199+78cd207961,gb065e2a02a+ad48cbcda4,gb1df4690d6+585f0f68a2,gb35d6563ee+62d12e78cb,gbc3249ced9+bd4bf1dc76,gbec6a3398f+bd4bf1dc76,gd01420fc67+bd4bf1dc76,gd59336e7c4+c7bb92e648,gf46e8334de+81c9a61069,gfed783d017+bd4bf1dc76,v25.0.1.rc3
LSST Data Management Base Package
Loading...
Searching...
No Matches
testUtils.py
Go to the documentation of this file.
2# LSST Data Management System
3#
4# Copyright 2008-2017 AURA/LSST.
5#
6# This product includes software developed by the
7# LSST Project (http://www.lsst.org/).
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 LSST License Statement and
20# the GNU General Public License along with this program. If not,
21# see <https://www.lsstcorp.org/LegalNotices/>.
22#
23
24__all__ = ["plantSources", "makeRandomTransmissionCurve", "makeDefectList",
25 "MockReferenceObjectLoaderFromFiles"]
26
27import numpy as np
28import esutil
29
30import lsst.geom
31import lsst.afw.image as afwImage
32from lsst import sphgeom
33from . import SingleGaussianPsf
34from . import Defect
35
36from . import ReferenceObjectLoader
37import lsst.afw.table as afwTable
38
39
40def plantSources(bbox, kwid, sky, coordList, addPoissonNoise=True):
41 """Make an exposure with stars (modelled as Gaussians)
42
43 Parameters
44 ----------
45 bbox : `lsst.geom.Box2I`
46 Parent bbox of exposure
47 kwid : `int`
48 Kernal width (and height; kernal is square)
49 sky : `float`
50 Amount of sky background (counts)
51 coordList : `list [tuple]`
52 A list of [x, y, counts, sigma] where:
53 * x,y are relative to exposure origin
54 * counts is the integrated counts for the star
55 * sigma is the Gaussian sigma in pixels
56 addPoissonNoise : `bool`
57 If True: add Poisson noise to the exposure
58 """
59 # make an image with sources
60 img = afwImage.ImageD(bbox)
61 meanSigma = 0.0
62 for coord in coordList:
63 x, y, counts, sigma = coord
64 meanSigma += sigma
65
66 # make a single gaussian psf
67 psf = SingleGaussianPsf(kwid, kwid, sigma)
68
69 # make an image of it and scale to the desired number of counts
70 thisPsfImg = psf.computeImage(lsst.geom.PointD(x, y))
71 thisPsfImg *= counts
72
73 # bbox a window in our image and add the fake star image
74 psfBox = thisPsfImg.getBBox()
75 psfBox.clip(bbox)
76 if psfBox != thisPsfImg.getBBox():
77 thisPsfImg = thisPsfImg[psfBox, afwImage.PARENT]
78 imgSeg = img[psfBox, afwImage.PARENT]
79 imgSeg += thisPsfImg
80 meanSigma /= len(coordList)
81
82 img += sky
83
84 # add Poisson noise
85 if (addPoissonNoise):
86 np.random.seed(seed=1) # make results reproducible
87 imgArr = img.getArray()
88 imgArr[:] = np.random.poisson(imgArr)
89
90 # bundle into a maskedimage and an exposure
91 mask = afwImage.Mask(bbox)
92 var = img.convertFloat()
93 img -= sky
94 mimg = afwImage.MaskedImageF(img.convertFloat(), mask, var)
95 exposure = afwImage.makeExposure(mimg)
96
97 # insert an approximate psf
98 psf = SingleGaussianPsf(kwid, kwid, meanSigma)
99 exposure.setPsf(psf)
100
101 return exposure
102
103
104def makeRandomTransmissionCurve(rng, minWavelength=4000.0, maxWavelength=7000.0, nWavelengths=200,
105 maxRadius=80.0, nRadii=30, perturb=0.05):
106 """Create a random TransmissionCurve with nontrivial spatial and
107 wavelength variation.
108
109 Parameters
110 ----------
111 rng : numpy.random.RandomState
112 Random number generator.
113 minWavelength : float
114 Average minimum wavelength for generated TransmissionCurves (will be
115 randomly perturbed).
116 maxWavelength : float
117 Average maximum wavelength for generated TransmissionCurves (will be
118 randomly perturbed).
119 nWavelengths : int
120 Number of samples in the wavelength dimension.
121 maxRadius : float
122 Average maximum radius for spatial variation (will be perturbed).
123 nRadii : int
124 Number of samples in the radial dimension.
125 perturb: float
126 Fraction by which wavelength and radius bounds should be randomly
127 perturbed.
128 """
129 dWavelength = maxWavelength - minWavelength
130
131 def perturbed(x, s=perturb*dWavelength):
132 return x + 2.0*s*(rng.rand() - 0.5)
133
134 wavelengths = np.linspace(perturbed(minWavelength), perturbed(maxWavelength), nWavelengths)
135 radii = np.linspace(0.0, perturbed(maxRadius, perturb*maxRadius), nRadii)
136 throughput = np.zeros(wavelengths.shape + radii.shape, dtype=float)
137 # throughput will be a rectangle in wavelength, shifting to higher wavelengths and shrinking
138 # in height with radius, going to zero at all bounds.
139 peak0 = perturbed(0.9, 0.05)
140 start0 = perturbed(minWavelength + 0.25*dWavelength)
141 stop0 = perturbed(minWavelength + 0.75*dWavelength)
142 for i, r in enumerate(radii):
143 mask = np.logical_and(wavelengths >= start0 + r, wavelengths <= stop0 + r)
144 throughput[mask, i] = peak0*(1.0 - r/1000.0)
145 return afwImage.TransmissionCurve.makeRadial(throughput, wavelengths, radii)
146
147
149 """Create a list of defects that can be used for testing.
150
151 Returns
152 -------
153 defectList = `list` [`lsst.meas.algorithms.Defect`]
154 The list of defects.
155 """
156 defectList = [Defect(lsst.geom.Box2I(lsst.geom.Point2I(962, 0),
157 lsst.geom.Extent2I(2, 4611))),
158 Defect(lsst.geom.Box2I(lsst.geom.Point2I(1316, 0),
159 lsst.geom.Extent2I(2, 4611))),
160 Defect(lsst.geom.Box2I(lsst.geom.Point2I(1576, 0),
161 lsst.geom.Extent2I(4, 4611))),
162 Defect(lsst.geom.Box2I(lsst.geom.Point2I(1626, 0),
163 lsst.geom.Extent2I(2, 4611))),
164 Defect(lsst.geom.Box2I(lsst.geom.Point2I(1994, 252),
165 lsst.geom.Extent2I(2, 4359))),
166 Defect(lsst.geom.Box2I(lsst.geom.Point2I(1426, 702),
167 lsst.geom.Extent2I(2, 3909))),
168 Defect(lsst.geom.Box2I(lsst.geom.Point2I(1526, 1140),
169 lsst.geom.Extent2I(2, 3471))),
170 Defect(lsst.geom.Box2I(lsst.geom.Point2I(856, 2300),
171 lsst.geom.Extent2I(2, 2311))),
172 Defect(lsst.geom.Box2I(lsst.geom.Point2I(858, 2328),
173 lsst.geom.Extent2I(2, 65))),
174 Defect(lsst.geom.Box2I(lsst.geom.Point2I(859, 2328),
175 lsst.geom.Extent2I(1, 56))),
176 Defect(lsst.geom.Box2I(lsst.geom.Point2I(844, 2796),
177 lsst.geom.Extent2I(4, 1814))),
178 Defect(lsst.geom.Box2I(lsst.geom.Point2I(1366, 2804),
179 lsst.geom.Extent2I(2, 1806))),
180 Defect(lsst.geom.Box2I(lsst.geom.Point2I(1766, 3844),
181 lsst.geom.Extent2I(2, 766))),
182 Defect(lsst.geom.Box2I(lsst.geom.Point2I(1872, 4228),
183 lsst.geom.Extent2I(2, 382))),
184 ]
185
186 return defectList
187
188
190 """Mock reference catalog dataId.
191
192 The reference catalog dataId is only used to retrieve a region property.
193
194 Parameters
195 ----------
196 pixelization : `lsst.sphgeom.Pixelization`
197 Pixelization object.
198 index : `int`
199 Pixel index number.
200 """
201 def __init__(self, pixelization, index):
202 self._region = pixelization.pixel(index)
203
204 @property
205 def region(self):
206 return self._region
207
208
210 """Mock reference catalog dataset handle.
211
212 The mock handle needs a get() and a name for logging.
213
214 Parameters
215 ----------
217 Reference catalog.
218 name : `str`
219 Name of reference catalog.
220 """
221 def __init__(self, catalog, name):
222 self._catalog = catalog
223 self._name = name
224
225 def get(self):
226 return self._catalog
227
228 class MockRef:
229 def __init__(self, name):
230 self._name = name
231
233 def __init__(self, name):
234 self._name = name
235
236 @property
237 def name(self):
238 return self._name
239
240 @property
241 def datasetType(self):
242 return self.MockDatasetType(self._name)
243
244 @property
245 def ref(self):
246 return self.MockRef(self._name)
247
248
249class MockReferenceObjectLoaderFromFiles(ReferenceObjectLoader):
250 """A simple mock of ReferenceObjectLoader.
251
252 This mock ReferenceObjectLoader uses a set of files on disk to create
253 mock dataIds and data reference handles that can be accessed
254 without a butler. The files must be afw catalog files in the reference
255 catalog format, sharded with HTM pixelization.
256
257 Parameters
258 ----------
259 filenames : `list` [`str`]
260 Names of files to use.
261 config : `lsst.meas.astrom.LoadReferenceObjectsConfig`, optional
262 Configuration object if necessary to override defaults.
263 htmLevel : `int`, optional
264 HTM level to use for the loader.
265 """
266 def __init__(self, filenames, name='cal_ref_cat', config=None, htmLevel=4):
267 dataIds, refCats = self._createDataIdsAndRefcats(filenames, htmLevel, name)
268
269 super().__init__(dataIds, refCats, name=name, config=config)
270
271 def _createDataIdsAndRefcats(self, filenames, htmLevel, name):
272 """Create mock dataIds and refcat handles.
273
274 Parameters
275 ----------
276 filenames : `list` [`str`]
277 Names of files to use.
278 htmLevel : `int`
279 HTM level to use for the loader.
280 name : `str`
281 Name of reference catalog (for logging).
282
283 Returns
284 -------
285 dataIds : `list` [`MockRefcatDataId`]
286 List of mock dataIds.
287 refCats : `list` [`MockRefcatDeferredDatasetHandle`]
288 List of mock deferred dataset handles.
289
290 Raises
291 ------
292 RuntimeError if any file contains sources that cover more than one HTM
293 pixel at level ``htmLevel``.
294 """
295 pixelization = sphgeom.HtmPixelization(htmLevel)
296 htm = esutil.htm.HTM(htmLevel)
297
298 dataIds = []
299 refCats = []
300
301 for filename in filenames:
302 cat = afwTable.BaseCatalog.readFits(filename)
303
304 ids = htm.lookup_id(np.rad2deg(cat['coord_ra']), np.rad2deg(cat['coord_dec']))
305
306 if len(np.unique(ids)) != 1:
307 raise RuntimeError(f"File {filename} contains more than one pixel at level {htmLevel}")
308
309 dataIds.append(MockRefcatDataId(pixelization, ids[0]))
310 refCats.append(MockRefcatDeferredDatasetHandle(cat, name))
311
312 return dataIds, refCats
table::Key< std::string > name
Definition: Amplifier.cc:116
Represent a 2-dimensional array of bitmask pixels.
Definition: Mask.h:77
An integer coordinate rectangle.
Definition: Box.h:55
Encapsulate information about a bad portion of a detector.
Definition: Interp.h:72
def __init__(self, pixelization, index)
Definition: testUtils.py:201
def _createDataIdsAndRefcats(self, filenames, htmLevel, name)
Definition: testUtils.py:271
def __init__(self, filenames, name='cal_ref_cat', config=None, htmLevel=4)
Definition: testUtils.py:266
HtmPixelization provides HTM indexing of points and regions.
A Pixelization (or partitioning) of the sphere is a mapping between points on the sphere and a set of...
Definition: Pixelization.h:77
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.
Definition: Exposure.h:445
def plantSources(bbox, kwid, sky, coordList, addPoissonNoise=True)
Definition: testUtils.py:40
def makeRandomTransmissionCurve(rng, minWavelength=4000.0, maxWavelength=7000.0, nWavelengths=200, maxRadius=80.0, nRadii=30, perturb=0.05)
Definition: testUtils.py:105