LSST Applications  21.0.0+04719a4bac,21.0.0-1-ga51b5d4+f5e6047307,21.0.0-11-g2b59f77+a9c1acf22d,21.0.0-11-ga42c5b2+86977b0b17,21.0.0-12-gf4ce030+76814010d2,21.0.0-13-g1721dae+760e7a6536,21.0.0-13-g3a573fe+768d78a30a,21.0.0-15-g5a7caf0+f21cbc5713,21.0.0-16-g0fb55c1+b60e2d390c,21.0.0-19-g4cded4ca+71a93a33c0,21.0.0-2-g103fe59+bb20972958,21.0.0-2-g45278ab+04719a4bac,21.0.0-2-g5242d73+3ad5d60fb1,21.0.0-2-g7f82c8f+8babb168e8,21.0.0-2-g8f08a60+06509c8b61,21.0.0-2-g8faa9b5+616205b9df,21.0.0-2-ga326454+8babb168e8,21.0.0-2-gde069b7+5e4aea9c2f,21.0.0-2-gecfae73+1d3a86e577,21.0.0-2-gfc62afb+3ad5d60fb1,21.0.0-25-g1d57be3cd+e73869a214,21.0.0-3-g357aad2+ed88757d29,21.0.0-3-g4a4ce7f+3ad5d60fb1,21.0.0-3-g4be5c26+3ad5d60fb1,21.0.0-3-g65f322c+e0b24896a3,21.0.0-3-g7d9da8d+616205b9df,21.0.0-3-ge02ed75+a9c1acf22d,21.0.0-4-g591bb35+a9c1acf22d,21.0.0-4-g65b4814+b60e2d390c,21.0.0-4-gccdca77+0de219a2bc,21.0.0-4-ge8a399c+6c55c39e83,21.0.0-5-gd00fb1e+05fce91b99,21.0.0-6-gc675373+3ad5d60fb1,21.0.0-64-g1122c245+4fb2b8f86e,21.0.0-7-g04766d7+cd19d05db2,21.0.0-7-gdf92d54+04719a4bac,21.0.0-8-g5674e7b+d1bd76f71f,master-gac4afde19b+a9c1acf22d,w.2021.13
LSST Data Management Base Package
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 import numpy as np
23 
24 import lsst.pex.config
25 import lsst.geom
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 from lsst.meas.base.apCorrRegistry import getApCorrNameSet
34 from lsst.meas.algorithms.testUtils import makeRandomTransmissionCurve
35 
36 
38  pixelScale = lsst.pex.config.Field(
39  dtype=float, default=0.2, optional=False,
40  doc="Pixel scale for mock WCSs in arcseconds/pixel"
41  )
43  dtype=bool, default=True, optional=False,
44  doc="Whether to randomly rotate observations relative to the tract Wcs"
45  )
47  dtype=float, default=1E11, optional=False,
48  doc="Flux at zero magnitude used to define PhotoCalibs."
49  )
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."
53  )
55  dtype=float, default=60.0, optional=False,
56  doc="Exposure time set in visitInfo (does not affect flux or noise level)"
57  )
58  psfImageSize = lsst.pex.config.Field(
59  dtype=int, default=21, optional=False,
60  doc="Image width and height of generated Psfs."
61  )
62  psfMinSigma = lsst.pex.config.Field(
63  dtype=float, default=1.5, optional=False,
64  doc="Minimum radius for generated Psfs."
65  )
66  psfMaxSigma = lsst.pex.config.Field(
67  dtype=float, default=3.0, optional=False,
68  doc="Maximum radius for generated Psfs."
69  )
70  apCorrOrder = lsst.pex.config.Field(
71  dtype=int, default=1, optional=False,
72  doc="Polynomial order for aperture correction fields"
73  )
74  seed = lsst.pex.config.Field(dtype=int, default=1, doc="Seed for numpy random number generator")
75 
76 
78  """Task to generate mock Exposure parameters (Wcs, Psf, PhotoCalib), intended for use as a subtask
79  of MockCoaddTask.
80 
81  @todo:
82  - document "pa" in detail; angle of what to what?
83  - document the catalog parameter of the run method
84  """
85 
86  ConfigClass = MockObservationConfig
87 
88  def __init__(self, **kwds):
89  lsst.pipe.base.Task.__init__(self, **kwds)
91  self.ccdKeyccdKey = self.schemaschema.addField("ccd", type=np.int32, doc="CCD number")
92  self.visitKeyvisitKey = self.schemaschema.addField("visit", type=np.int32, doc="visit number")
93  self.pointingKeypointingKey = lsst.afw.table.CoordKey.addFields(self.schemaschema, "pointing", "center of visit")
94  self.filterKeyfilterKey = self.schemaschema.addField("filter", type=str, doc="Bandpass filter name", size=16)
95  self.rngrng = np.random.RandomState(self.configconfig.seed)
96 
97  def run(self, butler, n, tractInfo, camera, catalog=None):
98  """Driver that generates an ExposureCatalog of mock observations.
99 
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.
105 
106  @todo figure out what `pa` is and use that knowledge to set `boresightRotAng` and `rotType`
107  """
108  if catalog is None:
109  catalog = lsst.afw.table.ExposureCatalog(self.schemaschema)
110  else:
111  if not catalog.getSchema().contains(self.schemaschema):
112  raise ValueError("Catalog schema does not match Task schema")
113  visit = 1
114 
115  for position, pa in self.makePointingsmakePointings(n, tractInfo):
116  visitInfo = lsst.afw.image.VisitInfo(
117  exposureTime=self.configconfig.expTime,
119  boresightRaDec=position,
120  )
121  for detector in camera:
122  photoCalib = self.buildPhotoCalibbuildPhotoCalib()
123  record = catalog.addNew()
124  record.setI(self.ccdKeyccdKey, detector.getId())
125  record.setI(self.visitKeyvisitKey, visit)
126  record.set(self.filterKeyfilterKey, 'r')
127  record.set(self.pointingKeypointingKey, position)
128  record.setWcs(self.buildWcsbuildWcs(position, pa, detector))
129  record.setPhotoCalib(photoCalib)
130  record.setVisitInfo(visitInfo)
131  record.setPsf(self.buildPsfbuildPsf(detector))
132  record.setApCorrMap(self.buildApCorrMapbuildApCorrMap(detector))
133  record.setTransmissionCurve(self.buildTransmissionCurvebuildTransmissionCurve(detector))
134  record.setBBox(detector.getBBox())
135  detectorId = detector.getId()
136  obj = butler.get("ccdExposureId", visit=visit, ccd=detectorId, immediate=True)
137  record.setId(obj)
138  visit += 1
139  return catalog
140 
141  def makePointings(self, n, tractInfo):
142  """Generate (celestial) positions and rotation angles that define field locations.
143 
144  Default implementation draws random pointings that are uniform in the tract's image
145  coordinate system.
146 
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)
152 
153  The default implementation returns an iterator (i.e. the function is a "generator"),
154  but derived-class overrides may return any iterable.
155  """
156  wcs = tractInfo.getWcs()
157  bbox = lsst.geom.Box2D(tractInfo.getBBox())
158  bbox.grow(lsst.geom.Extent2D(-0.1 * bbox.getWidth(), -0.1 * bbox.getHeight()))
159  for i in range(n):
160  x = self.rngrng.rand() * bbox.getWidth() + bbox.getMinX()
161  y = self.rngrng.rand() * bbox.getHeight() + bbox.getMinY()
162  pa = 0.0 * lsst.geom.radians
163  if self.configconfig.doRotate:
164  pa = self.rngrng.rand() * 2.0 * np.pi * lsst.geom.radians
165  yield wcs.pixelToSky(x, y), pa
166 
167  def buildWcs(self, position, pa, detector):
168  """Build a simple TAN Wcs with no distortion and exactly-aligned CCDs.
169 
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)
173  """
174  crval = position
175  pixelScale = (self.configconfig.pixelScale * lsst.geom.arcseconds).asDegrees()
176  cd = (lsst.geom.LinearTransform.makeScaling(pixelScale)
178  crpix = detector.transform(lsst.geom.Point2D(0, 0), FOCAL_PLANE, PIXELS)
179  wcs = lsst.afw.geom.makeSkyWcs(crpix=crpix, crval=crval, cdMatrix=cd.getMatrix())
180  return wcs
181 
182  def buildPhotoCalib(self):
183  """Build a simple PhotoCalib object with the calibration factor
184  drawn from a Gaussian defined by config.
185  """
187  self.rngrng.randn() * self.configconfig.fluxMag0Err + self.configconfig.fluxMag0,
188  self.configconfig.fluxMag0Err
189  )
190  return photoCalib
191 
192  def buildPsf(self, detector):
193  """Build a simple Gaussian Psf with linearly-varying ellipticity and size.
194 
195  The Psf pattern increases sigma_x linearly along the x direction, and sigma_y
196  linearly along the y direction.
197 
198  @param[in] detector: detector information (an lsst.afw.cameraGeom.Detector)
199  @return a psf (an instance of lsst.meas.algorithms.KernelPsf)
200  """
201  bbox = detector.getBBox()
202  dx = (self.configconfig.psfMaxSigma - self.configconfig.psfMinSigma) / bbox.getWidth()
203  dy = (self.configconfig.psfMaxSigma - self.configconfig.psfMinSigma) / bbox.getHeight()
204  sigmaXFunc = lsst.afw.math.PolynomialFunction2D(1)
205  sigmaXFunc.setParameter(0, self.configconfig.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.configconfig.psfMinSigma)
210  sigmaYFunc.setParameter(1, 0.0)
211  sigmaYFunc.setParameter(2, dy)
212  angleFunc = lsst.afw.math.PolynomialFunction2D(0)
213  spatialFuncList = []
214  spatialFuncList.append(sigmaXFunc)
215  spatialFuncList.append(sigmaYFunc)
216  spatialFuncList.append(angleFunc)
218  self.configconfig.psfImageSize, self.configconfig.psfImageSize,
219  lsst.afw.math.GaussianFunction2D(self.configconfig.psfMinSigma, self.configconfig.psfMinSigma),
220  spatialFuncList
221  )
222  return lsst.meas.algorithms.KernelPsf(kernel)
223 
224  def buildApCorrMap(self, detector):
225  """Build an ApCorrMap with random linearly-varying fields for all
226  flux fields registered for aperture correction.
227 
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.
230  """
231  order = self.configconfig.apCorrOrder
232 
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.rngrng.randn(order + 1 - n)
239  return lsst.afw.math.ChebyshevBoundedField(bbox, array)
240 
241  bbox = detector.getBBox()
242  apCorrMap = lsst.afw.image.ApCorrMap()
243  for name in getApCorrNameSet():
244  apCorrMap.set(name + "_instFlux", makeRandomBoundedField())
245  apCorrMap.set(name + "_instFluxErr", makeRandomBoundedField())
246  return apCorrMap
247 
248  def buildTransmissionCurve(self, detector):
249  """Build a random spacially-varying TransmissionCurve."""
250  bbox = detector.getBBox()
251  return makeRandomTransmissionCurve(rng=self.rngrng, maxRadius=max(bbox.getWidth(), bbox.getHeight()))
int max
A thin wrapper around std::map to allow aperture corrections to be attached to Exposures.
Definition: ApCorrMap.h:45
Information about a single exposure of an imaging camera.
Definition: VisitInfo.h:68
A kernel described by a function.
Definition: Kernel.h:536
A BoundedField based on 2-d Chebyshev polynomials of the first kind.
static CoordKey addFields(afw::table::Schema &schema, std::string const &name, std::string const &doc)
Add a pair of _ra, _dec fields to a Schema, and return a CoordKey that points to them.
Definition: aggregates.cc:83
Custom catalog class for ExposureRecord/Table.
Definition: Exposure.h:311
static Schema makeMinimalSchema()
Return a minimal schema for Exposure tables and records.
Definition: Exposure.h:216
static DateTime now(void)
Return current time as a DateTime.
Definition: DateTime.cc:517
A floating-point coordinate rectangle geometry.
Definition: Box.h:413
static LinearTransform makeRotation(Angle t) noexcept
static LinearTransform makeScaling(double s) noexcept
A Psf defined by a Kernel.
Definition: KernelPsf.h:36
def run(self, butler, n, tractInfo, camera, catalog=None)
std::shared_ptr< SkyWcs > makeSkyWcs(daf::base::PropertySet &metadata, bool strip=false)
Construct a SkyWcs from FITS keywords.
Definition: SkyWcs.cc:526
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
std::shared_ptr< PhotoCalib > makePhotoCalibFromCalibZeroPoint(double instFluxMag0, double instFluxMag0Err)
Construct a PhotoCalib from the deprecated Calib-style instFluxMag0/instFluxMag0Err values.
Definition: PhotoCalib.cc:614
def makeRandomTransmissionCurve(rng, minWavelength=4000.0, maxWavelength=7000.0, nWavelengths=200, maxRadius=80.0, nRadii=30, perturb=0.05)
Definition: testUtils.py:90