Coverage for python/lsst/sims/seeingModel/seeingModel.py : 19%

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
1from builtins import object
2from collections import OrderedDict
3import numpy as np
4from .seeingModelConfig import SeeingModelConfig
5from lsst.sims.seeingModel import version
8__all__ = ["SeeingModel"]
11class SeeingModel(object):
12 """LSST FWHM calculations for FWHM_effective and FWHM_geometric.
13 Calculations of the delivered values are based on equations in Document-20160
14 ("Atmospheric and Delivered Image Quality in OpSim" by Bo Xin, George Angeli, Zeljko Ivezic)
16 Parameters
17 ----------
18 config: SeeingModelConfig, opt
19 A configuration class for the seeing model.
20 This can be None, in which case the default SeeingModelConfig is used.
21 The user should set any non-default values for SeeingModelConfig before
22 configuration of the actual SeeingModel.
24 self.efd_requirements and self.map_requirements are also set.
25 efd_requirements is a tuple: (list of str, float).
26 This corresponds to the data columns required from the EFD and the amount of time history required.
27 target_requirements is a list of str.
28 This corresponds to the data columns required in the target map dictionary passed when calculating the
29 processed telemetry values.
30 """
31 def __init__(self, config=None):
32 self._config = None
33 self.filter_list = None
34 self.eff_wavelens = None
35 self.configure(config=config)
36 self.efd_requirements = (self._config.efd_columns, self._config.efd_delta_time)
37 self.target_requirements = self._config.target_columns
38 self.efd_seeing = self._config.efd_columns[0]
40 def configure(self, config=None):
41 """Configure the model. After 'configure' the model config will be frozen.
43 Also calculates the fwhm_zenith_system, using self._set_fwhm_zenith_system.
45 Parameters
46 ----------
47 config: SeeingModelConfig, opt
48 A configuration class for the seeing model.
49 This can be None, in which case the default values are used.
50 """
51 if config is None:
52 self._config = SeeingModelConfig()
53 elif isinstance(config, dict):
54 self._config = SeeingModelConfig()
55 for key in config:
56 setattr(self._config, key, config[key])
57 elif isinstance(config, SeeingModelConfig):
58 self._config = config
59 else:
60 raise RuntimeError(f'Expecting `None`, dictionary or `SeeingModelConfig`, '
61 f'got {type(config)}: {config!r}.')
62 self._config.validate()
63 self._config.freeze()
64 self._set_fwhm_zenith_system()
65 self.filter_list = tuple(self._config.filter_list)
66 self.eff_wavelens = np.array(self._config.filter_effwavelens)
68 def config_info(self):
69 """Report configuration parameters and version information.
71 Returns
72 -------
73 OrderedDict
74 """
75 config_info = OrderedDict()
76 config_info['SeeingModel_version'] = '%s' % version.__version__
77 config_info['SeeingModel_sha'] = '%s' % version.__fingerprint__
78 for k, v in self._config.iteritems():
79 config_info[k] = v
80 return config_info
82 def _set_fwhm_zenith_system(self):
83 """Calculate the system contribution to FWHM at zenith.
85 This is simply the individual telescope, optics, and camera contributions
86 combined in quadrature.
87 """
88 self.fwhm_system_zenith = np.sqrt(self._config.telescope_seeing**2 +
89 self._config.optical_design_seeing**2 +
90 self._config.camera_seeing**2)
92 def __call__(self, fwhm_z, airmass):
93 """Calculate the seeing values FWHM_eff and FWHM_geom at the given airmasses,
94 for the specified effective wavelengths, given FWHM_zenith (typically FWHM_500).
96 FWHM_geom represents the geometric size of the PSF; FWHM_eff represents the FWHM of a
97 single gaussian which encloses the same number of pixels as N_eff (the number of pixels
98 enclosed in the actual PSF -- this is the value to use when calculating SNR).
100 FWHM_geom(") = 0.822 * FWHM_eff(") + 0.052"
102 The FWHM_eff includes a contribution from the system and from the atmosphere.
103 Both of these are expected to scale with airmass^0.6 and with (500(nm)/wavelength(nm))^0.3.
104 FWHM_eff = 1.16 * sqrt(FWHM_sys**2 + 1.04*FWHM_atm**2)
106 Parameters
107 ----------
108 fwhm_z: float, or efdData dict
109 FWHM at zenith (arcsec).
110 airmass: float, np.array, or targetDict
111 Airmass (unitless).
113 Returns
114 -------
115 dict of numpy.ndarray, numpy.ndarray
116 FWHMeff, FWHMgeom: both are the same shape numpy.ndarray.
117 If airmass is a single value, FWHMeff & FWHMgeom are 1-d arrays,
118 with the same order as eff_wavelen (i.e. eff_wavelen[0] = u, then FWHMeff[0] = u).
119 If airmass is a numpy array, FWHMeff and FWHMgeom are 2-d arrays,
120 in the order of <filter><airmass> (i.e. eff_wavelen[0] = u, 1-d array over airmass range).
121 """
122 if isinstance(fwhm_z, dict):
123 fwhm_z = fwhm_z[self.efd_seeing]
124 if isinstance(airmass, dict):
125 airmass = airmass['airmass']
126 airmass_correction = np.power(airmass, 0.6)
127 wavelen_correction = np.power(self._config.raw_seeing_wavelength / self.eff_wavelens, 0.3)
128 if isinstance(airmass, np.ndarray):
129 fwhm_system = self.fwhm_system_zenith * np.outer(np.ones(len(wavelen_correction)),
130 airmass_correction)
131 fwhm_atmo = fwhm_z * np.outer(wavelen_correction, airmass_correction)
132 else:
133 fwhm_system = self.fwhm_system_zenith * airmass_correction
134 fwhm_atmo = fwhm_z * wavelen_correction * airmass_correction
135 # Calculate combined FWHMeff.
136 fwhm_eff = 1.16 * np.sqrt(fwhm_system ** 2 + 1.04 * fwhm_atmo ** 2)
137 # Translate to FWHMgeom.
138 fwhm_geom = self.fwhmEff_to_fwhmGeom(fwhm_eff)
139 return {'fwhmEff': fwhm_eff, 'fwhmGeom': fwhm_geom}
141 @staticmethod
142 def fwhmEff_to_fwhmGeom(fwhm_eff):
143 """Calculate FWHM_geom from FWHM_eff.
145 Parameters
146 ----------
147 fwhm_eff : float or np.ndarray
149 Returns
150 -------
151 float or np.ndarray
152 """
153 return (0.822 * fwhm_eff + 0.052)
155 @staticmethod
156 def fwhmGeom_to_fwhmEff(fwhm_geom):
157 """Calculate FWHM_eff from FWHM_geom.
159 Parameters
160 ----------
161 fwhm_geom : float or np.ndarray
163 Returns
164 -------
165 float or np.ndarray
166 """
167 return (fwhm_geom - 0.052)/0.822