Hide keyboard shortcuts

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 

6 

7 

8__all__ = ["SeeingModel"] 

9 

10 

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) 

15 

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. 

23 

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] 

39 

40 def configure(self, config=None): 

41 """Configure the model. After 'configure' the model config will be frozen. 

42 

43 Also calculates the fwhm_zenith_system, using self._set_fwhm_zenith_system. 

44 

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) 

67 

68 def config_info(self): 

69 """Report configuration parameters and version information. 

70 

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 

81 

82 def _set_fwhm_zenith_system(self): 

83 """Calculate the system contribution to FWHM at zenith. 

84 

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) 

91 

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). 

95 

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). 

99 

100 FWHM_geom(") = 0.822 * FWHM_eff(") + 0.052" 

101 

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) 

105 

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). 

112 

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} 

140 

141 @staticmethod 

142 def fwhmEff_to_fwhmGeom(fwhm_eff): 

143 """Calculate FWHM_geom from FWHM_eff. 

144 

145 Parameters 

146 ---------- 

147 fwhm_eff : float or np.ndarray 

148 

149 Returns 

150 ------- 

151 float or np.ndarray 

152 """ 

153 return (0.822 * fwhm_eff + 0.052) 

154 

155 @staticmethod 

156 def fwhmGeom_to_fwhmEff(fwhm_geom): 

157 """Calculate FWHM_eff from FWHM_geom. 

158 

159 Parameters 

160 ---------- 

161 fwhm_geom : float or np.ndarray 

162 

163 Returns 

164 ------- 

165 float or np.ndarray 

166 """ 

167 return (fwhm_geom - 0.052)/0.822