Coverage for tests/surveyPropertyMapsTestUtils.py: 17%
61 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-08-12 10:21 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-08-12 10:21 +0000
1# This file is part of pipe_tasks.
2#
3# LSST Data Management System
4# This product includes software developed by the
5# LSST Project (http://www.lsst.org/).
6# See COPYRIGHT file at the top of the source tree.
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 <https://www.lsstcorp.org/LegalNotices/>.
21#
22"""Utilities for HealSparsePropertyMapTask and others."""
23import numpy as np
25import lsst.geom as geom
26from lsst.daf.base import DateTime
27from lsst.afw.coord import Observatory
28from lsst.pipe.tasks.postprocess import ConsolidateVisitSummaryTask
29import lsst.afw.table as afwTable
30import lsst.afw.image as afwImage
31import lsst.afw.geom as afwGeom
32from lsst.afw.detection import GaussianPsf
35__all__ = ['makeMockVisitSummary']
38def makeMockVisitSummary(visit,
39 ra_center=0.0,
40 dec_center=-45.0,
41 physical_filter='TEST-I',
42 band='i',
43 mjd=59234.7083333334,
44 psf_sigma=3.0,
45 zenith_distance=45.0,
46 zero_point=30.0,
47 sky_background=100.0,
48 sky_noise=10.0,
49 mean_var=100.0,
50 exposure_time=100.0,
51 detector_size=200,
52 pixel_scale=0.2):
53 """Make a mock visit summary catalog.
55 This will contain two square detectors with the same metadata,
56 with a small (20 pixel) gap between the detectors. There is no
57 rotation, as each detector is simply offset in RA from the
58 specified boresight.
60 Parameters
61 ----------
62 visit : `int`
63 Visit number.
64 ra_center : `float`
65 Right ascension of the center of the "camera" boresight (degrees).
66 dec_center : `float`
67 Declination of the center of the "camera" boresight (degrees).
68 physical_filter : `str`
69 Arbitrary name for the physical filter.
70 band : `str`
71 Name of the associated band.
72 mjd : `float`
73 Modified Julian Date.
74 psf_sigma : `float`
75 Sigma width of Gaussian psf.
76 zenith_distance : `float`
77 Distance from zenith of the visit (degrees).
78 zero_point : `float`
79 Constant zero point for the visit (magnitudes).
80 sky_background : `float`
81 Background level for the visit (counts).
82 sky_noise : `float`
83 Noise level for the background of the visit (counts).
84 mean_var : `float`
85 Mean of the variance plane of the visit (counts).
86 exposure_time : `float`
87 Exposure time of the visit (seconds).
88 detector_size : `int`
89 Size of each square detector in the visit (pixels).
90 pixel_scale : `float`
91 Size of the pixel in arcseconds per pixel.
93 Returns
94 -------
95 visit_summary : `lsst.afw.table.ExposureCatalog`
96 """
97 # We are making a 2 detector "camera"
98 n_detector = 2
100 schema = ConsolidateVisitSummaryTask().schema
101 visit_summary = afwTable.ExposureCatalog(schema)
102 visit_summary.resize(n_detector)
104 bbox = geom.Box2I(x=geom.IntervalI(min=0, max=detector_size - 1),
105 y=geom.IntervalI(min=0, max=detector_size - 1))
107 for detector_id in range(n_detector):
108 row = visit_summary[detector_id]
110 row['id'] = detector_id
111 row.setBBox(bbox)
112 row['visit'] = visit
113 row['physical_filter'] = physical_filter
114 row['band'] = band
115 row['zenithDistance'] = zenith_distance
116 row['zeroPoint'] = zero_point
117 row['skyBg'] = sky_background
118 row['skyNoise'] = sky_noise
119 row['meanVar'] = mean_var
121 # Generate a photocalib
122 instFluxMag0 = 10.**(zero_point/2.5)
123 row.setPhotoCalib(afwImage.makePhotoCalibFromCalibZeroPoint(instFluxMag0))
125 # Generate a WCS and set values accordingly
126 crpix = geom.Point2D(detector_size/2., detector_size/2.)
127 # Create a 20 pixel gap between the two detectors (each offset 10 pixels).
128 if detector_id == 0:
129 delta_ra = -1.0*((detector_size + 10)*pixel_scale/3600.)/np.cos(np.deg2rad(dec_center))
130 delta_dec = 0.0
131 elif detector_id == 1:
132 delta_ra = ((detector_size + 10)*pixel_scale/3600.)/np.cos(np.deg2rad(dec_center))
133 delta_dec = 0.0
134 crval = geom.SpherePoint(ra_center + delta_ra, dec_center + delta_dec, geom.degrees)
135 cd_matrix = afwGeom.makeCdMatrix(scale=pixel_scale*geom.arcseconds, orientation=0.0*geom.degrees)
136 wcs = afwGeom.makeSkyWcs(crpix=crpix, crval=crval, cdMatrix=cd_matrix)
137 row.setWcs(wcs)
139 sph_pts = wcs.pixelToSky(geom.Box2D(bbox).getCorners())
140 row['raCorners'] = np.array([float(sph.getRa().asDegrees()) for sph in sph_pts])
141 row['decCorners'] = np.array([float(sph.getDec().asDegrees()) for sph in sph_pts])
142 sph_pt = wcs.pixelToSky(bbox.getCenter())
143 row['ra'] = sph_pt.getRa().asDegrees()
144 row['dec'] = sph_pt.getDec().asDegrees()
146 # Generate a visitInfo.
147 # This does not need to be consistent with the zenith angle in the table,
148 # it just needs to be valid and have sufficient information to compute
149 # exposure time and parallactic angle.
150 date = DateTime(date=mjd, system=DateTime.DateSystem.MJD)
151 visit_info = afwImage.VisitInfo(exposureTime=exposure_time,
152 date=date,
153 darkTime=0.0,
154 boresightRaDec=geom.SpherePoint(ra_center,
155 dec_center,
156 geom.degrees),
157 era=45.1*geom.degrees,
158 observatory=Observatory(
159 11.1*geom.degrees,
160 0.0*geom.degrees,
161 0.333),
162 boresightRotAngle=0.0*geom.degrees,
163 rotType=afwImage.RotType.SKY)
164 row.setVisitInfo(visit_info)
166 # Generate a PSF and set values accordingly
167 psf = GaussianPsf(15, 15, psf_sigma)
168 row.setPsf(psf)
169 psfAvgPos = psf.getAveragePosition()
170 shape = psf.computeShape(psfAvgPos)
171 row['psfSigma'] = psf.getSigma()
172 row['psfIxx'] = shape.getIxx()
173 row['psfIyy'] = shape.getIyy()
174 row['psfIxy'] = shape.getIxy()
175 row['psfArea'] = shape.getArea()
177 return visit_summary