24 import lsst.pex.config
38 pixelScale = lsst.pex.config.Field(
39 dtype=float, default=0.2, optional=
False,
40 doc=
"Pixel scale for mock WCSs in arcseconds/pixel"
42 doRotate = lsst.pex.config.Field(
43 dtype=bool, default=
True, optional=
False,
44 doc=
"Whether to randomly rotate observations relative to the tract Wcs"
46 fluxMag0 = lsst.pex.config.Field(
47 dtype=float, default=1E11, optional=
False,
48 doc=
"Flux at zero magnitude used to define PhotoCalibs."
50 fluxMag0Err = lsst.pex.config.Field(
51 dtype=float, default=100.0, optional=
False,
52 doc=
"Error on flux at zero magnitude used to define PhotoCalibs; used to add scatter as well."
54 expTime = lsst.pex.config.Field(
55 dtype=float, default=60.0, optional=
False,
56 doc=
"Exposure time set in visitInfo (does not affect flux or noise level)"
58 psfImageSize = lsst.pex.config.Field(
59 dtype=int, default=21, optional=
False,
60 doc=
"Image width and height of generated Psfs."
62 psfMinSigma = lsst.pex.config.Field(
63 dtype=float, default=1.5, optional=
False,
64 doc=
"Minimum radius for generated Psfs."
66 psfMaxSigma = lsst.pex.config.Field(
67 dtype=float, default=3.0, optional=
False,
68 doc=
"Maximum radius for generated Psfs."
70 apCorrOrder = lsst.pex.config.Field(
71 dtype=int, default=1, optional=
False,
72 doc=
"Polynomial order for aperture correction fields"
74 seed = lsst.pex.config.Field(dtype=int, default=1, doc=
"Seed for numpy random number generator")
78 """Task to generate mock Exposure parameters (Wcs, Psf, PhotoCalib), intended for use as a subtask
82 - document "pa" in detail; angle of what to what?
83 - document the catalog parameter of the run method
86 ConfigClass = MockObservationConfig
89 lsst.pipe.base.Task.__init__(self, **kwds)
91 self.
ccdKey = self.
schema.addField(
"ccd", type=np.int32, doc=
"CCD number")
92 self.
visitKey = self.
schema.addField(
"visit", type=np.int32, doc=
"visit number")
94 self.
filterKey = self.
schema.addField(
"filter", type=str, doc=
"Bandpass filter name", size=16)
97 def run(self, butler, n, tractInfo, camera, catalog=None):
98 """Driver that generates an ExposureCatalog of mock observations.
100 @param[in] butler: a data butler
101 @param[in] n: number of pointings
102 @param[in] camera: camera geometry (an lsst.afw.cameraGeom.Camera)
103 @param[in] catalog: catalog to which to add observations (an ExposureCatalog);
104 if None then a new catalog is created.
106 @todo figure out what `pa` is and use that knowledge to set `boresightRotAng` and `rotType`
112 raise ValueError(
"Catalog schema does not match Task schema")
117 exposureTime=self.
config.expTime,
119 boresightRaDec=position,
121 for detector
in camera:
123 record = catalog.addNew()
124 record.setI(self.
ccdKey, detector.getId())
128 record.setWcs(self.
buildWcs(position, pa, detector))
129 record.setPhotoCalib(photoCalib)
130 record.setVisitInfo(visitInfo)
131 record.setPsf(self.
buildPsf(detector))
134 record.setBBox(detector.getBBox())
135 detectorId = detector.getId()
136 obj = butler.get(
"ccdExposureId", visit=visit, ccd=detectorId, immediate=
True)
142 """Generate (celestial) positions and rotation angles that define field locations.
144 Default implementation draws random pointings that are uniform in the tract's image
147 @param[in] n: number of pointings
148 @param[in] tractInfo: skymap tract (a lsst.skymap.TractInfo)
149 @return a Python iterable over (coord, angle) pairs:
150 - coord is an ICRS object position (an lsst.geom.SpherePoint)
151 - angle is a position angle (???) (an lsst.geom.Angle)
153 The default implementation returns an iterator (i.e. the function is a "generator"),
154 but derived-class overrides may return any iterable.
156 wcs = tractInfo.getWcs()
160 x = self.
rng.rand() * bbox.getWidth() + bbox.getMinX()
161 y = self.
rng.rand() * bbox.getHeight() + bbox.getMinY()
162 pa = 0.0 * lsst.geom.radians
164 pa = self.
rng.rand() * 2.0 * np.pi * lsst.geom.radians
165 yield wcs.pixelToSky(x, y), pa
168 """Build a simple TAN Wcs with no distortion and exactly-aligned CCDs.
170 @param[in] position: ICRS object position on sky (on lsst.geom.SpherePoint)
171 @param[in] pa: position angle (an lsst.geom.Angle)
172 @param[in] detector: detector information (an lsst.afw.cameraGeom.Detector)
175 pixelScale = (self.
config.pixelScale * lsst.geom.arcseconds).asDegrees()
183 """Build a simple PhotoCalib object with the calibration factor
184 drawn from a Gaussian defined by config.
193 """Build a simple Gaussian Psf with linearly-varying ellipticity and size.
195 The Psf pattern increases sigma_x linearly along the x direction, and sigma_y
196 linearly along the y direction.
198 @param[in] detector: detector information (an lsst.afw.cameraGeom.Detector)
199 @return a psf (an instance of lsst.meas.algorithms.KernelPsf)
201 bbox = detector.getBBox()
202 dx = (self.
config.psfMaxSigma - self.
config.psfMinSigma) / bbox.getWidth()
203 dy = (self.
config.psfMaxSigma - self.
config.psfMinSigma) / bbox.getHeight()
204 sigmaXFunc = lsst.afw.math.PolynomialFunction2D(1)
205 sigmaXFunc.setParameter(0, self.
config.psfMinSigma - dx * bbox.getMinX() - dy * bbox.getMinY())
206 sigmaXFunc.setParameter(1, dx)
207 sigmaXFunc.setParameter(2, 0.0)
208 sigmaYFunc = lsst.afw.math.PolynomialFunction2D(1)
209 sigmaYFunc.setParameter(0, self.
config.psfMinSigma)
210 sigmaYFunc.setParameter(1, 0.0)
211 sigmaYFunc.setParameter(2, dy)
212 angleFunc = lsst.afw.math.PolynomialFunction2D(0)
214 spatialFuncList.append(sigmaXFunc)
215 spatialFuncList.append(sigmaYFunc)
216 spatialFuncList.append(angleFunc)
219 lsst.afw.math.GaussianFunction2D(self.
config.psfMinSigma, self.
config.psfMinSigma),
225 """Build an ApCorrMap with random linearly-varying fields for all
226 flux fields registered for aperture correction.
228 These flux field names are used only as strings; there is no
229 connection to any actual algorithms with those names or the PSF model.
231 order = self.
config.apCorrOrder
233 def makeRandomBoundedField():
234 """Make an upper-left triangular coefficient array appropriate
235 for a 2-d polynomial."""
236 array = np.zeros((order + 1, order + 1), dtype=float)
237 for n
in range(order + 1):
238 array[n, 0:order + 1 - n] = self.
rng.randn(order + 1 - n)
241 bbox = detector.getBBox()
244 apCorrMap.set(name +
"_instFlux", makeRandomBoundedField())
245 apCorrMap.set(name +
"_instFluxErr", makeRandomBoundedField())
249 """Build a random spacially-varying TransmissionCurve."""
250 bbox = detector.getBBox()