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

1import numpy as np 

2import healpy as hp 

3from lsst.sims.utils import _hpid2RaDec, Site, _angularSeparation, _xyz_from_ra_dec 

4import matplotlib.pylab as plt 

5from lsst.sims.featureScheduler.basis_functions import Base_basis_function 

6from lsst.sims.featureScheduler.utils import hp_in_lsst_fov, int_rounded 

7 

8 

9__all__ = ['Zenith_mask_basis_function', 'Zenith_shadow_mask_basis_function', 

10 'Moon_avoidance_basis_function', 'Map_cloud_basis_function', 

11 'Planet_mask_basis_function', 'Mask_azimuth_basis_function'] 

12 

13 

14class Zenith_mask_basis_function(Base_basis_function): 

15 """Just remove the area near zenith. 

16 

17 Parameters 

18 ---------- 

19 min_alt : float (20.) 

20 The minimum possible altitude (degrees) 

21 max_alt : float (82.) 

22 The maximum allowed altitude (degrees) 

23 """ 

24 def __init__(self, min_alt=20., max_alt=82.): 

25 super(Zenith_mask_basis_function, self).__init__() 

26 self.update_on_newobs = False 

27 self.min_alt = np.radians(min_alt) 

28 self.max_alt = np.radians(max_alt) 

29 self.result = np.empty(hp.nside2npix(self.nside), dtype=float).fill(self.penalty) 

30 

31 def _calc_value(self, conditions, indx=None): 

32 

33 result = self.result.copy() 

34 alt_limit = np.where((int_rounded(conditions.alt) > int_rounded(self.min_alt)) & 

35 (int_rounded(conditions.alt) < int_rounded(self.max_alt)))[0] 

36 result[alt_limit] = 1 

37 return result 

38 

39 

40class Planet_mask_basis_function(Base_basis_function): 

41 """Mask the bright planets 

42 

43 Parameters 

44 ---------- 

45 mask_radius : float (3.5) 

46 The radius to mask around a planet (degrees). 

47 planets : list of str (None) 

48 A list of planet names to mask. Defaults to ['venus', 'mars', 'jupiter']. Not including 

49 Saturn because it moves really slow and has average apparent mag of ~0.4, so fainter than Vega. 

50 

51 """ 

52 def __init__(self, mask_radius=3.5, planets=None, nside=None, scale=1e5): 

53 super(Planet_mask_basis_function, self).__init__(nside=nside) 

54 if planets is None: 

55 planets = ['venus', 'mars', 'jupiter'] 

56 self.planets = planets 

57 self.mask_radius = np.radians(mask_radius) 

58 self.result = np.zeros(hp.nside2npix(nside)) 

59 # set up a kdtree. Could maybe use healpy.query_disc instead. 

60 self.in_fov = hp_in_lsst_fov(nside=nside, fov_radius=mask_radius, scale=scale) 

61 

62 def _calc_value(self, conditions, indx=None): 

63 result = self.result.copy() 

64 for pn in self.planets: 

65 indices = self.in_fov(conditions.planet_positions[pn+'_RA'], conditions.planet_positions[pn+'_dec']) 

66 result[indices] = np.nan 

67 

68 return result 

69 

70 

71class Zenith_shadow_mask_basis_function(Base_basis_function): 

72 """Mask the zenith, and things that will soon pass near zenith. Useful for making sure 

73 observations will not be too close to zenith when they need to be observed again (e.g. for a pair). 

74 

75 Parameters 

76 ---------- 

77 min_alt : float (20.) 

78 The minimum alititude to alow. Everything lower is masked. (degrees) 

79 max_alt : float (82.) 

80 The maximum altitude to alow. Everything higher is masked. (degrees) 

81 shadow_minutes : float (40.) 

82 Mask anything that will pass through the max alt in the next shadow_minutes time. (minutes) 

83 """ 

84 def __init__(self, nside=None, min_alt=20., max_alt=82., 

85 shadow_minutes=40., penalty=np.nan, site='LSST'): 

86 super(Zenith_shadow_mask_basis_function, self).__init__(nside=nside) 

87 self.update_on_newobs = False 

88 

89 self.penalty = penalty 

90 

91 self.min_alt = np.radians(min_alt) 

92 self.max_alt = np.radians(max_alt) 

93 self.ra, self.dec = _hpid2RaDec(nside, np.arange(hp.nside2npix(nside))) 

94 self.shadow_minutes = np.radians(shadow_minutes/60. * 360./24.) 

95 # Compute the declination band where things could drift into zenith 

96 self.decband = np.zeros(self.dec.size, dtype=float) 

97 self.zenith_radius = np.radians(90.-max_alt)/2. 

98 site = Site(name=site) 

99 self.lat_rad = site.latitude_rad 

100 self.lon_rad = site.longitude_rad 

101 self.decband[np.where((int_rounded(self.dec) < int_rounded(self.lat_rad+self.zenith_radius)) & 

102 (int_rounded(self.dec) > int_rounded(self.lat_rad-self.zenith_radius)))] = 1 

103 

104 self.result = np.empty(hp.nside2npix(self.nside), dtype=float) 

105 self.result.fill(self.penalty) 

106 

107 def _calc_value(self, conditions, indx=None): 

108 

109 result = self.result.copy() 

110 alt_limit = np.where((int_rounded(conditions.alt) > int_rounded(self.min_alt)) & 

111 (int_rounded(conditions.alt) < int_rounded(self.max_alt)))[0] 

112 result[alt_limit] = 1 

113 to_mask = np.where((int_rounded(conditions.HA) > int_rounded(2.*np.pi-self.shadow_minutes-self.zenith_radius)) & 

114 (self.decband == 1)) 

115 result[to_mask] = np.nan 

116 return result 

117 

118 

119class Moon_avoidance_basis_function(Base_basis_function): 

120 """Avoid looking too close to the moon. 

121 

122 Parameters 

123 ---------- 

124 moon_distance: float (30.) 

125 Minimum allowed moon distance. (degrees) 

126 

127 XXX--TODO: This could be a more complicated function of filter and moon phase. 

128 """ 

129 def __init__(self, nside=None, moon_distance=30.): 

130 super(Moon_avoidance_basis_function, self).__init__(nside=nside) 

131 self.update_on_newobs = False 

132 

133 self.moon_distance = int_rounded(np.radians(moon_distance)) 

134 self.result = np.ones(hp.nside2npix(self.nside), dtype=float) 

135 

136 def _calc_value(self, conditions, indx=None): 

137 result = self.result.copy() 

138 

139 angular_distance = _angularSeparation(conditions.az, conditions.alt, 

140 conditions.moonAz, 

141 conditions.moonAlt) 

142 

143 result[int_rounded(angular_distance) < self.moon_distance] = np.nan 

144 

145 return result 

146 

147 

148class Bulk_cloud_basis_function(Base_basis_function): 

149 """Mark healpixels on a map if their cloud values are greater than 

150 the same healpixels on a maximum cloud map. 

151 

152 Parameters 

153 ---------- 

154 nside: int (default_nside) 

155 The healpix resolution. 

156 max_cloud_map : numpy array (None) 

157 A healpix map showing the maximum allowed cloud values for all points on the sky 

158 out_of_bounds_val : float (10.) 

159 Point value to give regions where there are no observations requested 

160 """ 

161 

162 def __init__(self, nside=None, max_cloud_map=None, max_val=0.7, 

163 out_of_bounds_val=np.nan): 

164 super(Bulk_cloud_basis_function, self).__init__(nside=nside) 

165 self.update_on_newobs = False 

166 

167 if max_cloud_map is None: 

168 self.max_cloud_map = np.zeros(hp.nside2npix(nside), dtype=float) + max_val 

169 else: 

170 self.max_cloud_map = max_cloud_map 

171 self.out_of_bounds_area = np.where(self.max_cloud_map > 1.)[0] 

172 self.out_of_bounds_val = out_of_bounds_val 

173 self.result = np.ones(hp.nside2npix(self.nside)) 

174 

175 def _calc_value(self, conditions, indx=None): 

176 """ 

177 Parameters 

178 ---------- 

179 indx : list (None) 

180 Index values to compute, if None, full map is computed 

181 Returns 

182 ------- 

183 Healpix map where pixels with a cloud value greater than the max_cloud_map 

184 value are marked as unseen. 

185 """ 

186 

187 result = self.result.copy() 

188 

189 clouded = np.where(self.max_cloud_map <= conditions.bulk_cloud) 

190 result[clouded] = self.out_of_bounds_val 

191 

192 return result 

193 

194 

195class Map_cloud_basis_function(Base_basis_function): 

196 """Mark healpixels on a map if their cloud values are greater than 

197 the same healpixels on a maximum cloud map. Currently a placeholder for 

198 when the telemetry stream can include a full sky cloud map. 

199 

200 Parameters 

201 ---------- 

202 nside: int (default_nside) 

203 The healpix resolution. 

204 max_cloud_map : numpy array (None) 

205 A healpix map showing the maximum allowed cloud values for all points on the sky 

206 out_of_bounds_val : float (10.) 

207 Point value to give regions where there are no observations requested 

208 """ 

209 

210 def __init__(self, nside=None, max_cloud_map=None, max_val=0.7, 

211 out_of_bounds_val=np.nan): 

212 super(Bulk_cloud_basis_function, self).__init__(nside=nside) 

213 self.update_on_newobs = False 

214 

215 if max_cloud_map is None: 

216 self.max_cloud_map = np.zeros(hp.nside2npix(nside), dtype=float) + max_val 

217 else: 

218 self.max_cloud_map = max_cloud_map 

219 self.out_of_bounds_area = np.where(self.max_cloud_map > 1.)[0] 

220 self.out_of_bounds_val = out_of_bounds_val 

221 self.result = np.ones(hp.nside2npix(self.nside)) 

222 

223 def _calc_value(self, conditions, indx=None): 

224 """ 

225 Parameters 

226 ---------- 

227 indx : list (None) 

228 Index values to compute, if None, full map is computed 

229 Returns 

230 ------- 

231 Healpix map where pixels with a cloud value greater than the max_cloud_map 

232 value are marked as unseen. 

233 """ 

234 

235 result = self.result.copy() 

236 

237 clouded = np.where(self.max_cloud_map <= conditions.bulk_cloud) 

238 result[clouded] = self.out_of_bounds_val 

239 

240 return result 

241 

242 

243class Mask_azimuth_basis_function(Base_basis_function): 

244 """Mask pixels based on azimuth 

245 """ 

246 def __init__(self, nside=None, out_of_bounds_val=np.nan, az_min=0., az_max=180.): 

247 super(Mask_azimuth_basis_function, self).__init__(nside=nside) 

248 self.az_min = int_rounded(np.radians(az_min)) 

249 self.az_max = int_rounded(np.radians(az_max)) 

250 self.out_of_bounds_val = out_of_bounds_val 

251 self.result = np.ones(hp.nside2npix(self.nside)) 

252 

253 def _calc_value(self, conditions, indx=None): 

254 to_mask = np.where((int_rounded(conditions.az) > self.az_min) & (int_rounded(conditions.az) < self.az_max))[0] 

255 result = self.result.copy() 

256 result[to_mask] = self.out_of_bounds_val 

257 

258 return result 

259