Coverage for python/lsst/sims/featureScheduler/basis_functions/mask_basis_funcs.py : 21%

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
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 'Solar_elongation_mask_basis_function', 'Area_check_mask_basis_function']
16class Area_check_mask_basis_function(Base_basis_function):
17 """Take a list of other mask basis functions, and do an additional check for area available
18 """
19 def __init__(self, bf_list, nside=32, min_area=1000.):
20 super(Area_check_mask_basis_function, self).__init__(nside=nside)
21 self.bf_list = bf_list
22 self.result = np.zeros(hp.nside2npix(self.nside), dtype=float)
23 self.min_area = min_area
25 def check_feasibility(self, conditions):
26 result = True
27 for bf in self.bf_list:
28 if not bf.check_feasibility(conditions):
29 return False
31 area_map = self.result.copy()
32 for bf in self.bf_list:
33 area_map *= bf(conditions)
35 good_pix = np.where(area_map == 0)[0]
36 if hp.nside2pixarea(self.nside, degrees=True)*good_pix.size < self.min_area:
37 result = False
38 return result
40 def _calc_value(self, conditions, **kwargs):
41 result = self.result.copy()
42 for bf in self.bf_list:
43 result *= bf(conditions)
44 return result
48class Solar_elongation_mask_basis_function(Base_basis_function):
49 """Mask things at various solar elongations
51 Parameters
52 ----------
53 min_elong : float (0)
54 The minimum solar elongation to consider (degrees).
55 max_elong : float (60.)
56 The maximum solar elongation to consider (degrees).
57 """
59 def __init__(self, min_elong=0., max_elong=60., nside=None, penalty=np.nan):
60 super(Solar_elongation_mask_basis_function, self).__init__(nside=nside)
61 self.min_elong = np.radians(min_elong)
62 self.max_elong = np.radians(max_elong)
63 self.penalty = penalty
64 self.result = np.empty(hp.nside2npix(self.nside), dtype=float)
65 self.result.fill(self.penalty)
67 def _calc_value(self, conditions, indx=None):
68 result = self.result.copy()
69 in_range = np.where((int_rounded(conditions.solar_elongation) >= int_rounded(self.min_elong)) &
70 (int_rounded(conditions.solar_elongation) <= int_rounded(self.max_elong)))[0]
71 result[in_range] = 1
72 return result
75class Zenith_mask_basis_function(Base_basis_function):
76 """Just remove the area near zenith.
78 Parameters
79 ----------
80 min_alt : float (20.)
81 The minimum possible altitude (degrees)
82 max_alt : float (82.)
83 The maximum allowed altitude (degrees)
84 """
85 def __init__(self, min_alt=20., max_alt=82., nside=None):
86 super(Zenith_mask_basis_function, self).__init__(nside=nside)
87 self.update_on_newobs = False
88 self.min_alt = np.radians(min_alt)
89 self.max_alt = np.radians(max_alt)
90 self.result = np.empty(hp.nside2npix(self.nside), dtype=float).fill(self.penalty)
92 def _calc_value(self, conditions, indx=None):
94 result = self.result.copy()
95 alt_limit = np.where((int_rounded(conditions.alt) > int_rounded(self.min_alt)) &
96 (int_rounded(conditions.alt) < int_rounded(self.max_alt)))[0]
97 result[alt_limit] = 1
98 return result
101class Planet_mask_basis_function(Base_basis_function):
102 """Mask the bright planets
104 Parameters
105 ----------
106 mask_radius : float (3.5)
107 The radius to mask around a planet (degrees).
108 planets : list of str (None)
109 A list of planet names to mask. Defaults to ['venus', 'mars', 'jupiter']. Not including
110 Saturn because it moves really slow and has average apparent mag of ~0.4, so fainter than Vega.
112 """
113 def __init__(self, mask_radius=3.5, planets=None, nside=None, scale=1e5):
114 super(Planet_mask_basis_function, self).__init__(nside=nside)
115 if planets is None:
116 planets = ['venus', 'mars', 'jupiter']
117 self.planets = planets
118 self.mask_radius = np.radians(mask_radius)
119 self.result = np.zeros(hp.nside2npix(nside))
120 # set up a kdtree. Could maybe use healpy.query_disc instead.
121 self.in_fov = hp_in_lsst_fov(nside=nside, fov_radius=mask_radius, scale=scale)
123 def _calc_value(self, conditions, indx=None):
124 result = self.result.copy()
125 for pn in self.planets:
126 indices = self.in_fov(conditions.planet_positions[pn+'_RA'], conditions.planet_positions[pn+'_dec'])
127 result[indices] = np.nan
129 return result
132class Zenith_shadow_mask_basis_function(Base_basis_function):
133 """Mask the zenith, and things that will soon pass near zenith. Useful for making sure
134 observations will not be too close to zenith when they need to be observed again (e.g. for a pair).
136 Parameters
137 ----------
138 min_alt : float (20.)
139 The minimum alititude to alow. Everything lower is masked. (degrees)
140 max_alt : float (82.)
141 The maximum altitude to alow. Everything higher is masked. (degrees)
142 shadow_minutes : float (40.)
143 Mask anything that will pass through the max alt in the next shadow_minutes time. (minutes)
144 """
145 def __init__(self, nside=None, min_alt=20., max_alt=82.,
146 shadow_minutes=40., penalty=np.nan, site='LSST'):
147 super(Zenith_shadow_mask_basis_function, self).__init__(nside=nside)
148 self.update_on_newobs = False
150 self.penalty = penalty
152 self.min_alt = np.radians(min_alt)
153 self.max_alt = np.radians(max_alt)
154 self.ra, self.dec = _hpid2RaDec(nside, np.arange(hp.nside2npix(nside)))
155 self.shadow_minutes = np.radians(shadow_minutes/60. * 360./24.)
156 # Compute the declination band where things could drift into zenith
157 self.decband = np.zeros(self.dec.size, dtype=float)
158 self.zenith_radius = np.radians(90.-max_alt)/2.
159 site = Site(name=site)
160 self.lat_rad = site.latitude_rad
161 self.lon_rad = site.longitude_rad
162 self.decband[np.where((int_rounded(self.dec) < int_rounded(self.lat_rad+self.zenith_radius)) &
163 (int_rounded(self.dec) > int_rounded(self.lat_rad-self.zenith_radius)))] = 1
165 self.result = np.empty(hp.nside2npix(self.nside), dtype=float)
166 self.result.fill(self.penalty)
168 def _calc_value(self, conditions, indx=None):
170 result = self.result.copy()
171 alt_limit = np.where((int_rounded(conditions.alt) > int_rounded(self.min_alt)) &
172 (int_rounded(conditions.alt) < int_rounded(self.max_alt)))[0]
173 result[alt_limit] = 1
174 to_mask = np.where((int_rounded(conditions.HA) > int_rounded(2.*np.pi-self.shadow_minutes-self.zenith_radius)) &
175 (self.decband == 1))
176 result[to_mask] = np.nan
177 return result
180class Moon_avoidance_basis_function(Base_basis_function):
181 """Avoid looking too close to the moon.
183 Parameters
184 ----------
185 moon_distance: float (30.)
186 Minimum allowed moon distance. (degrees)
188 XXX--TODO: This could be a more complicated function of filter and moon phase.
189 """
190 def __init__(self, nside=None, moon_distance=30.):
191 super(Moon_avoidance_basis_function, self).__init__(nside=nside)
192 self.update_on_newobs = False
194 self.moon_distance = int_rounded(np.radians(moon_distance))
195 self.result = np.ones(hp.nside2npix(self.nside), dtype=float)
197 def _calc_value(self, conditions, indx=None):
198 result = self.result.copy()
200 angular_distance = _angularSeparation(conditions.az, conditions.alt,
201 conditions.moonAz,
202 conditions.moonAlt)
204 result[int_rounded(angular_distance) < self.moon_distance] = np.nan
206 return result
209class Bulk_cloud_basis_function(Base_basis_function):
210 """Mark healpixels on a map if their cloud values are greater than
211 the same healpixels on a maximum cloud map.
213 Parameters
214 ----------
215 nside: int (default_nside)
216 The healpix resolution.
217 max_cloud_map : numpy array (None)
218 A healpix map showing the maximum allowed cloud values for all points on the sky
219 out_of_bounds_val : float (10.)
220 Point value to give regions where there are no observations requested
221 """
223 def __init__(self, nside=None, max_cloud_map=None, max_val=0.7,
224 out_of_bounds_val=np.nan):
225 super(Bulk_cloud_basis_function, self).__init__(nside=nside)
226 self.update_on_newobs = False
228 if max_cloud_map is None:
229 self.max_cloud_map = np.zeros(hp.nside2npix(nside), dtype=float) + max_val
230 else:
231 self.max_cloud_map = max_cloud_map
232 self.out_of_bounds_area = np.where(self.max_cloud_map > 1.)[0]
233 self.out_of_bounds_val = out_of_bounds_val
234 self.result = np.ones(hp.nside2npix(self.nside))
236 def _calc_value(self, conditions, indx=None):
237 """
238 Parameters
239 ----------
240 indx : list (None)
241 Index values to compute, if None, full map is computed
242 Returns
243 -------
244 Healpix map where pixels with a cloud value greater than the max_cloud_map
245 value are marked as unseen.
246 """
248 result = self.result.copy()
250 clouded = np.where(self.max_cloud_map <= conditions.bulk_cloud)
251 result[clouded] = self.out_of_bounds_val
253 return result
256class Map_cloud_basis_function(Base_basis_function):
257 """Mark healpixels on a map if their cloud values are greater than
258 the same healpixels on a maximum cloud map. Currently a placeholder for
259 when the telemetry stream can include a full sky cloud map.
261 Parameters
262 ----------
263 nside: int (default_nside)
264 The healpix resolution.
265 max_cloud_map : numpy array (None)
266 A healpix map showing the maximum allowed cloud values for all points on the sky
267 out_of_bounds_val : float (10.)
268 Point value to give regions where there are no observations requested
269 """
271 def __init__(self, nside=None, max_cloud_map=None, max_val=0.7,
272 out_of_bounds_val=np.nan):
273 super(Bulk_cloud_basis_function, self).__init__(nside=nside)
274 self.update_on_newobs = False
276 if max_cloud_map is None:
277 self.max_cloud_map = np.zeros(hp.nside2npix(nside), dtype=float) + max_val
278 else:
279 self.max_cloud_map = max_cloud_map
280 self.out_of_bounds_area = np.where(self.max_cloud_map > 1.)[0]
281 self.out_of_bounds_val = out_of_bounds_val
282 self.result = np.ones(hp.nside2npix(self.nside))
284 def _calc_value(self, conditions, indx=None):
285 """
286 Parameters
287 ----------
288 indx : list (None)
289 Index values to compute, if None, full map is computed
290 Returns
291 -------
292 Healpix map where pixels with a cloud value greater than the max_cloud_map
293 value are marked as unseen.
294 """
296 result = self.result.copy()
298 clouded = np.where(self.max_cloud_map <= conditions.bulk_cloud)
299 result[clouded] = self.out_of_bounds_val
301 return result
304class Mask_azimuth_basis_function(Base_basis_function):
305 """Mask pixels based on azimuth
306 """
307 def __init__(self, nside=None, out_of_bounds_val=np.nan, az_min=0., az_max=180.):
308 super(Mask_azimuth_basis_function, self).__init__(nside=nside)
309 self.az_min = int_rounded(np.radians(az_min))
310 self.az_max = int_rounded(np.radians(az_max))
311 self.out_of_bounds_val = out_of_bounds_val
312 self.result = np.ones(hp.nside2npix(self.nside))
314 def _calc_value(self, conditions, indx=None):
315 to_mask = np.where((int_rounded(conditions.az) > self.az_min) & (int_rounded(conditions.az) < self.az_max))[0]
316 result = self.result.copy()
317 result[to_mask] = self.out_of_bounds_val
319 return result