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()