LSSTApplications  11.0-13-gbb96280,12.1.rc1,12.1.rc1+1,12.1.rc1+2,12.1.rc1+5,12.1.rc1+8,12.1.rc1-1-g06d7636+1,12.1.rc1-1-g253890b+5,12.1.rc1-1-g3d31b68+7,12.1.rc1-1-g3db6b75+1,12.1.rc1-1-g5c1385a+3,12.1.rc1-1-g83b2247,12.1.rc1-1-g90cb4cf+6,12.1.rc1-1-g91da24b+3,12.1.rc1-2-g3521f8a,12.1.rc1-2-g39433dd+4,12.1.rc1-2-g486411b+2,12.1.rc1-2-g4c2be76,12.1.rc1-2-gc9c0491,12.1.rc1-2-gda2cd4f+6,12.1.rc1-3-g3391c73+2,12.1.rc1-3-g8c1bd6c+1,12.1.rc1-3-gcf4b6cb+2,12.1.rc1-4-g057223e+1,12.1.rc1-4-g19ed13b+2,12.1.rc1-4-g30492a7
LSSTDataManagementBasePackage
mockObservation.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008, 2009, 2010, 2011, 2012 LSST Corporation.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <http://www.lsstcorp.org/LegalNotices/>.
21 #
22 
23 import numpy
24 
25 import lsst.pex.config
26 import lsst.afw.table
27 import lsst.afw.geom
28 from lsst.afw.cameraGeom import PIXELS, FOCAL_PLANE
29 import lsst.afw.image
30 import lsst.afw.math
31 import lsst.afw.detection
32 import lsst.pipe.base
33 
34 class MockObservationConfig(lsst.pex.config.Config):
35  pixelScale = lsst.pex.config.Field(
36  dtype=float, default=0.2, optional=False,
37  doc="Pixel scale for mock WCSs in arcseconds/pixel"
38  )
39  doRotate = lsst.pex.config.Field(
40  dtype=bool, default=True, optional=False,
41  doc="Whether to randomly rotate observations relative to the tract Wcs"
42  )
43  fluxMag0 = lsst.pex.config.Field(
44  dtype=float, default=1E11, optional=False,
45  doc="Flux at zero magnitude used to define Calibs."
46  )
47  fluxMag0Sigma = lsst.pex.config.Field(
48  dtype=float, default=100.0, optional=False,
49  doc="Error on flux at zero magnitude used to define Calibs; used to add scatter as well."
50  )
51  expTime = lsst.pex.config.Field(
52  dtype=float, default=60.0, optional=False,
53  doc="Exposure time set in generated Calibs (does not affect flux or noise level)"
54  )
55  psfImageSize = lsst.pex.config.Field(
56  dtype=int, default=21, optional=False,
57  doc="Image width and height of generated Psfs."
58  )
59  psfMinSigma = lsst.pex.config.Field(
60  dtype=float, default=1.5, optional=False,
61  doc="Minimum radius for generated Psfs."
62  )
63  psfMaxSigma = lsst.pex.config.Field(
64  dtype=float, default=3.0, optional=False,
65  doc="Maximum radius for generated Psfs."
66  )
67  seed = lsst.pex.config.Field(dtype=int, default=1, doc="Seed for numpy random number generator")
68 
69 class MockObservationTask(lsst.pipe.base.Task):
70  """Task to generate mock Exposure parameters (Wcs, Psf, Calib), intended for use as a subtask
71  of MockCoaddTask.
72 
73  @todo:
74  - document "pa" in detail; angle of what to what?
75  - document the catalog parameter of the run method
76  """
77 
78  ConfigClass = MockObservationConfig
79 
80  def __init__(self, **kwds):
81  lsst.pipe.base.Task.__init__(self, **kwds)
83  self.ccdKey = self.schema.addField("ccd", type=int, doc="CCD number")
84  self.visitKey = self.schema.addField("visit", type=int, doc="visit number")
85  self.pointingKey = lsst.afw.table.CoordKey.addFields(self.schema, "pointing", "center of visit")
86  self.rng = numpy.random.RandomState(self.config.seed)
87 
88  def run(self, butler, n, tractInfo, camera, catalog=None):
89  """Driver that generates an ExposureCatalog of mock observations.
90 
91  @param[in] butler: a data butler
92  @param[in] n: number of pointings
93  @param[in] camera: camera geometry (an lsst.afw.cameraGeom.Camera)
94  @param[in] catalog: catalog to which to add observations (an ExposureCatalog);
95  if None then a new catalog is created.
96  """
97  if catalog is None:
99  else:
100  if not catalog.getSchema().contains(self.schema):
101  raise ValueError("Catalog schema does not match Task schema")
102  visit = 1
103  for position, pa in self.makePointings(n, tractInfo):
104  for detector in camera:
105  calib = self.buildCalib()
106  record = catalog.addNew()
107  record.setI(self.ccdKey, detector.getId())
108  record.setI(self.visitKey, visit)
109  record.set(self.pointingKey, position)
110  record.setWcs(self.buildWcs(position, pa, detector))
111  record.setCalib(calib)
112  record.setPsf(self.buildPsf(detector))
113  record.setBBox(detector.getBBox())
114  detectorId = detector.getId()
115  obj = butler.get("ccdExposureId", visit=visit, ccd=detectorId, immediate=True)
116  record.setId(obj)
117  visit += 1
118  return catalog
119 
120  def makePointings(self, n, tractInfo):
121  """Generate (celestial) positions and rotation angles that define field locations.
122 
123  Default implementation draws random pointings that are uniform in the tract's image
124  coordinate system.
125 
126  @param[in] n: number of pointings
127  @param[in] tractInfo: skymap tract (a lsst.skymap.TractInfo)
128  @return a Python iterable over (coord, angle) pairs:
129  - coord is an object position (an lsst.afw.coord.Coord)
130  - angle is a position angle (???) (an lsst.afw.geom.Angle)
131 
132  The default implementation returns an iterator (i.e. the function is a "generator"),
133  but derived-class overrides may return any iterable.
134  """
135  wcs = tractInfo.getWcs()
136  bbox = lsst.afw.geom.Box2D(tractInfo.getBBox())
137  bbox.grow(lsst.afw.geom.Extent2D(-0.1 * bbox.getWidth(), -0.1 * bbox.getHeight()))
138  for i in xrange(n):
139  x = self.rng.rand() * bbox.getWidth() + bbox.getMinX()
140  y = self.rng.rand() * bbox.getHeight() + bbox.getMinY()
141  pa = 0.0 * lsst.afw.geom.radians
142  if self.config.doRotate:
143  pa = self.rng.rand() * 2.0 * numpy.pi * lsst.afw.geom.radians
144  yield wcs.pixelToSky(x, y), pa
145 
146  def buildWcs(self, position, pa, detector):
147  """Build a simple TAN Wcs with no distortion and exactly-aligned CCDs.
148 
149  @param[in] position: object position on sky (an lsst.afw.coord.Coord)
150  @param[in] pa: position angle (an lsst.afw.geom.Angle)
151  @param[in] detector: detector information (an lsst.afw.cameraGeom.Detector)
152  """
153  crval = position
154  pixelScale = (self.config.pixelScale * lsst.afw.geom.arcseconds).asDegrees()
157  fpCtr = detector.makeCameraPoint(lsst.afw.geom.Point2D(0, 0), FOCAL_PLANE)
158  crpix = detector.transform(fpCtr, PIXELS).getPoint()
159 
160  wcs = lsst.afw.image.makeWcs(crval, crpix, *cd.getMatrix().flatten())
161  return wcs
162 
163  def buildCalib(self):
164  """Build a simple Calib object with exposure time fixed by config, fluxMag0 drawn from
165  a Gaussian defined by config, and mid-time set to DateTime.now().
166  """
167  calib = lsst.afw.image.Calib()
168  calib.setMidTime(lsst.daf.base.DateTime.now())
169  calib.setExptime(self.config.expTime)
170  calib.setFluxMag0(
171  self.rng.randn() * self.config.fluxMag0Sigma + self.config.fluxMag0,
172  self.config.fluxMag0Sigma
173  )
174  return calib
175 
176  def buildPsf(self, detector):
177  """Build a simple Gaussian Psf with linearly-varying ellipticity and size.
178 
179  The Psf pattern increases sigma_x linearly along the x direction, and sigma_y
180  linearly along the y direction.
181 
182  @param[in] detector: detector information (an lsst.afw.cameraGeom.Detector)
183  @return a psf (an instance of lsst.meas.algorithms.KernelPsf)
184  """
185  bbox = detector.getBBox()
186  dx = (self.config.psfMaxSigma - self.config.psfMinSigma) / bbox.getWidth()
187  dy = (self.config.psfMaxSigma - self.config.psfMinSigma) / bbox.getHeight()
188  sigmaXFunc = lsst.afw.math.PolynomialFunction2D(1)
189  sigmaXFunc.setParameter(0, self.config.psfMinSigma - dx * bbox.getMinX() - dy * bbox.getMinY())
190  sigmaXFunc.setParameter(1, dx)
191  sigmaXFunc.setParameter(2, 0.0)
192  sigmaYFunc = lsst.afw.math.PolynomialFunction2D(1)
193  sigmaYFunc.setParameter(0, self.config.psfMinSigma)
194  sigmaYFunc.setParameter(1, 0.0)
195  sigmaYFunc.setParameter(2, dy)
196  angleFunc = lsst.afw.math.PolynomialFunction2D(0)
197  spatialFuncList = lsst.afw.math.Function2DList()
198  spatialFuncList.append(sigmaXFunc)
199  spatialFuncList.append(sigmaYFunc)
200  spatialFuncList.append(angleFunc)
202  self.config.psfImageSize, self.config.psfImageSize,
203  lsst.afw.math.GaussianFunction2D(self.config.psfMinSigma, self.config.psfMinSigma),
204  spatialFuncList
205  )
206  return lsst.meas.algorithms.KernelPsf(kernel)
static DateTime now(void)
Definition: DateTime.cc:601
A Psf defined by a Kernel.
Definition: KernelPsf.h:33
static LinearTransform makeScaling(double s)
static Schema makeMinimalSchema()
Return a minimal schema for Exposure tables and records.
Definition: Exposure.h:164
static CoordKey addFields(afw::table::Schema &schema, std::string const &name, std::string const &doc)
static LinearTransform makeRotation(Angle t)
boost::shared_ptr< Wcs > makeWcs(coord::Coord const &crval, geom::Point2D const &crpix, double CD11, double CD12, double CD21, double CD22)
Create a Wcs object from crval, crpix, CD, using CD elements (useful from python) ...
Definition: makeWcs.cc:138
Custom catalog class for ExposureRecord/Table.
Definition: Exposure.h:55
A floating-point coordinate rectangle geometry.
Definition: Box.h:271
A kernel described by a function.
Definition: Kernel.h:625