Coverage for python/lsst/sims/GalSimInterface/galSimPSF.py : 46%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2This file defines the model classes that wrap PSFs from
3galsim into the CatSim interface
4"""
6from builtins import object
7import numpy
8import galsim
10__all__ = ["PSFbase", "DoubleGaussianPSF", "SNRdocumentPSF",
11 "Kolmogorov_and_Gaussian_PSF"]
13class PSFbase(object):
14 """
15 This is the base class for wrappers of GalSim's PSF classes. To apply a PSF to GalSim images
16 using the GalSim Instance Catalog and GalSim Interpreter, the user must define a daughter
17 class of this class and instantiate it as the member variable self.PSF in the GalSim Instance Catalog.
19 Any Daughter class of this class must have a member method _getPSF which accepts the coordinates
20 xPupil and yPupil in arcseconds as kwargs. This method will instantiate a psf object at those
21 coordinates and return it.
23 The method applyPSF is defined in this class and should not be overwritten. It handles the task of actually
24 convolving the PSF returned by _getPSF.
26 Consult GalSim's documentation to see what kinds of PSFs are available.
28 See the classes DoubleGaussianPSF and SNRdocumentPSF below for example implementations.
30 See galSimCompoundGenerator.py and galSimStarGenerator.py for example usages.
31 """
33 def _getPSF(self, xPupil=None, yPupil=None):
34 """
35 If it had been implemented, this would return a GalSim PSF instantiation at the
36 coordinates and wavelength specified and returned it to applyPSF. As it is, this
37 class has not been implemented and is left to the user to implement in Daughter
38 classes of PSFbase.
40 @param [in] xPupil the x coordinate on the pupil in arc seconds
42 @param [in] yPupil the y coordinate on the pupil in arc seconds
43 """
45 raise NotImplementedError("There is not _getPSF for PSFbase; define a daughter class and define your own")
47 def applyPSF(self, xPupil=None, yPupil=None, obj=None, **kwargs):
48 """
49 Apply the PSF to a GalSim GSObject
51 This method accepts the x and y pupil coordinates in arc seconds as well
52 as a GalSim GSObject. The method calculates the PSF parameters based on xPupil
53 and yPupil, constructs a Galsim GSObject corresponding to the PSF function, and convolves
54 the PSF with the GSObject, returning the result of the convolution.
56 In the case of point sources, this object returns the raw PSF, rather than attempting
57 a convolution (since there is nothing to convolve with).
59 @param [in] xPupil the x pupil coordinate in arc seconds
61 @param [in] yPupil the y pupil coordinate in arc seconds
63 @param [in] obj is a GalSim GSObject (an astronomical object) with which
64 to convolve the PSF (optional)
65 """
67 #use the user-defined _getPSF method to calculate the PSF at these specific
68 #coordinates and (optionally) wavelength
69 psf = self._getPSF(xPupil=xPupil, yPupil=yPupil, **kwargs)
71 if obj is None:
72 #if there is no object, use a DeltaFunction as a point source
73 obj = galsim.DeltaFunction()
75 #convolve obj with the psf
76 if isinstance(psf, galsim.Convolution):
77 # If the psf is itself a Convolution object, convolve obj
78 # with the individual components to ensure that the
79 # obj_list of the returned obj lists those components
80 # separately.
81 return galsim.Convolution([obj] + psf.obj_list)
82 else:
83 return galsim.Convolve(obj, psf)
85 def __eq__(self, rhs):
86 """
87 Compare types and underlying galsim ._cached_psf attributes for
88 equality test.
89 """
90 return (type(self) == type(rhs)
91 and self._cached_psf == rhs._cached_psf)
93class DoubleGaussianPSF(PSFbase):
94 """
95 This is an example implementation of a wavelength- and position-independent
96 Double Gaussian PSF. See the documentation in PSFbase to learn how it is used.
97 """
99 def __init__(self, fwhm1=0.6, fwhm2=0.12, wgt1=1.0, wgt2=0.1):
100 """
101 @param [in] fwhm1 is the Full Width at Half Max of the first Gaussian in arcseconds
103 @param [in] fwhm2 is the Full Width at Half Max of the second Gaussian in arcseconds
105 @param [in] wgt1 is the dimensionless coefficient normalizing the first Gaussian
107 @param [in] wgt2 is the dimensionless coefficient normalizing the second Gaussian
109 The total PSF will be
111 (wgt1 * G(sig1) + wgt2 * G(sig2))/(wgt1 + wgt2)
113 where G(sigN) denotes a normalized Gaussian with a standard deviation that gives
114 a Full Width at Half Max of fwhmN. (Integrating a two-dimensional Gaussian, we find
115 that sig = fwhm/2.355)
117 Because this PSF depends on neither position nor wavelength, this __init__ method
118 will instantiate a PSF and cache it. It is this cached psf that will be returned
119 whenever _getPSF is called in this class.
120 """
122 r1 = fwhm1/2.355
123 r2 = fwhm2/2.355
124 norm = 1.0/(wgt1 + wgt2)
126 gaussian1 = galsim.Gaussian(sigma=r1)
127 gaussian2 = galsim.Gaussian(sigma=r2)
129 self._cached_psf = norm*(wgt1*gaussian1 + wgt2*gaussian2)
131 def _getPSF(self, xPupil=None, yPupil=None, **kwargs):
132 """
133 Return a the PSF to be convolved with sources.
135 @param [in] xPupil the x coordinate on the pupil in arc seconds
137 @param [in] yPupil the y coordinate on the pupil in arc seconds
139 Because this specific PSF depends on neither wavelength nor position,
140 it will just return the cached PSF function.
141 """
142 return self._cached_psf
146class SNRdocumentPSF(DoubleGaussianPSF):
147 """
148 This is an example implementation of a wavelength- and position-independent
149 Double Gaussian PSF. See the documentation in PSFbase to learn how it is used.
151 This specific PSF comes from equation(30) of the signal-to-noise document (LSE-40),
152 which can be found at
154 www.astro.washington.edu/users/ivezic/Astr511/LSST_SNRdoc.pdf
155 """
157 def __init__(self, fwhm=0.6, pixel_scale=0.2, gsparams=None):
158 """
159 @param [in] fwhm is the Full Width at Half Max of the total PSF. This is given in
160 arcseconds. The default value of 0.6 comes from a FWHM of 3 pixels with a pixel scale
161 of 0.2 arcseconds per pixel.
163 Because this PSF depends on neither position nor wavelength, this __init__ method
164 will instantiate a PSF and cache it. It is this cached psf that will be returned
165 whenever _getPSF is called in this class.
166 """
168 #the expression below is derived by solving equation (30) of the signal-to-noise
169 #document (www.astro.washington.edu/uses/ivezic/Astr511/LSST_SNRdoc.pdf)
170 #for r at half the maximum of the PSF
171 alpha = fwhm/2.3835
173 eff_pixel_sigma_sq = pixel_scale*pixel_scale/12.0
175 sigma = numpy.sqrt(alpha*alpha - eff_pixel_sigma_sq)
176 gaussian1 = galsim.Gaussian(sigma=sigma, gsparams=gsparams)
178 sigma = numpy.sqrt(4.0*alpha*alpha - eff_pixel_sigma_sq)
179 gaussian2 = galsim.Gaussian(sigma=sigma, gsparams=gsparams)
181 self._cached_psf = 0.909*(gaussian1 + 0.1*gaussian2)
184class Kolmogorov_and_Gaussian_PSF(PSFbase):
185 """
186 This PSF class is based on David Kirkby's presentation to the DESC Survey Simulations
187 working group on 23 March 2017.
189 https://confluence.slac.stanford.edu/pages/viewpage.action?spaceKey=LSSTDESC&title=SSim+2017-03-23
191 (you will need a SLAC Confluence account to access that link)
192 """
194 def __init__(self, airmass=1.2, rawSeeing=0.7, band='r', gsparams=None):
195 """
196 Parameters
197 ----------
198 airmass
200 rawSeeing is the FWHM seeing at zenith at 500 nm in arc seconds
201 (provided by OpSim)
203 band is the bandpass of the observation [u,g,r,i,z,y]
204 """
205 # This code was provided by David Kirkby in a private communication
207 wlen_eff = dict(u=365.49, g=480.03, r=622.20, i=754.06, z=868.21, y=991.66)[band]
208 # wlen_eff is from Table 2 of LSE-40 (y=y2)
210 FWHMatm = rawSeeing * (wlen_eff / 500.) ** -0.3 * airmass ** 0.6
211 # From LSST-20160 eqn (4.1)
213 FWHMsys = numpy.sqrt(0.25**2 + 0.3**2 + 0.08**2) * airmass ** 0.6
214 # From LSST-20160 eqn (4.2)
216 atm = galsim.Kolmogorov(fwhm=FWHMatm, gsparams=gsparams)
217 sys = galsim.Gaussian(fwhm=FWHMsys, gsparams=gsparams)
218 psf = galsim.Convolve((atm, sys))
220 self._cached_psf = psf
222 def _getPSF(self, xPupil=None, yPupil=None, **kwargs):
223 return self._cached_psf