Coverage for python/lsst/sims/featureScheduler/basis_functions/rolling_funcs.py : 13%

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
2from lsst.sims.featureScheduler import features
3from lsst.sims.featureScheduler import utils
4import healpy as hp
5import matplotlib.pylab as plt
6import warnings
7from lsst.sims.featureScheduler.basis_functions import Base_basis_function
10__all__ = ["Target_map_modulo_basis_function", "Footprint_basis_function", "Footprint_rolling_basis_function"]
13class Footprint_basis_function(Base_basis_function):
14 """Basis function that tries to maintain a uniformly covered footprint
16 Parameters
17 ----------
18 filtername : str ('r')
19 The filter for this footprint
20 footprint : HEALpix np.array
21 The desired footprint. Assumed normalized.
22 all_footprints_sum : float (None)
23 If using multiple filters, the sum of all the footprints. Needed to make sure basis functions are
24 normalized properly across all fitlers.
25 out_of_bounds_val : float (-10)
26 The value to set the basis function for regions that are not in the footprint (default -10, np.nan is
27 another good value to use)
29 """
30 def __init__(self, filtername='r', nside=None, footprint=None, all_footprints_sum=None,
31 out_of_bounds_val=-10.):
33 super(Footprint_basis_function, self).__init__(nside=nside, filtername=filtername)
34 self.footprint = footprint
36 if all_footprints_sum is None:
37 # Assume the footprints are similar in weight
38 self.all_footprints_sum = np.sum(footprint)*6
39 else:
40 self.all_footprints_sum = all_footprints_sum
42 self.footprint_sum = np.sum(footprint)
44 self.survey_features = {}
45 # All the observations in all filters
46 self.survey_features['N_obs_all'] = features.N_observations(nside=nside, filtername=None)
47 self.survey_features['N_obs'] = features.N_observations(nside=nside, filtername=filtername)
49 # should probably actually loop over all the target maps?
50 self.out_of_bounds_area = np.where(footprint <= 0)[0]
51 self.out_of_bounds_val = out_of_bounds_val
53 def _calc_value(self, conditions, indx=None):
55 # Compute how many observations we should have on the sky
56 desired = self.footprint / self.all_footprints_sum * np.sum(self.survey_features['N_obs_all'].feature)
57 result = desired - self.survey_features['N_obs'].feature
58 result[self.out_of_bounds_area] = self.out_of_bounds_val
59 return result
62class Footprint_rolling_basis_function(Base_basis_function):
63 """Let's get the rolling really right.
65 Parameters
66 ----------
67 footprints : list of np.array
68 List of HEALpix arrays. The footprints should all have matching sums and have the same nside.
69 all_footprints_sum : float
70 The sum of footprints over all filters.
71 all_rolling_sum : float
72 The sum (over all filters) of the region of the maps that changs.
73 season_modulo : int (2)
74 The modulo to pass to utils.season_calc.
75 season_length : float (365.25)
76 How long a season should be (days).
77 max_season : int (None)
78 If set, the season calc will return -1 for values greater than max_season
79 day_offset : np.array (None)
80 Offset to pass to utils.season_calc (days).
82 """
84 def __init__(self, filtername='r', nside=None, footprints=None, all_footprints_sum=None, all_rolling_sum=None, out_of_bounds_val=-10,
85 season_modulo=2, season_length=365.25, max_season=None, day_offset=None):
86 super(Footprint_rolling_basis_function, self).__init__(nside=nside, filtername=filtername)
88 # OK, going to find the parts of the map that are the same everywhere, and compute the
89 # basis function the same as usual for those.
90 same_footprint = np.ones(footprints[0].size, dtype=bool)
91 for footprint in footprints[0:-1]:
92 same_footprint = same_footprint & (footprint == footprints[-1])
94 sum_footprints = footprints[0]*0
95 for footprint in footprints:
96 sum_footprints += footprint
97 self.constant_footprint_indx = np.where((same_footprint == True) & (sum_footprints > 0))[0]
98 self.rolling_footprint_indx = np.where((same_footprint == False) & (sum_footprints > 0))[0]
100 self.season_modulo = season_modulo
101 self.season_length = season_length
102 self.max_season = max_season
103 self.day_offset = day_offset
104 self.footprints = footprints
106 self.all_footprints_sum = all_footprints_sum
107 self.all_rolling_sum = all_rolling_sum
109 self.survey_features = {}
110 # Set a season for -1 (for before rolling or after max_season)
111 # All the observations in the given filter
112 self.survey_features['N_obs_%i' % -1] = features.N_observations(nside=nside, filtername=filtername)
113 # All the observations in all filters
114 self.survey_features['N_obs_all_%i' % -1] = features.N_observations(nside=nside, filtername=None)
116 for i, temp in enumerate(footprints[0:-1]):
117 # Observation in a season, in filtername
118 self.survey_features['N_obs_%i' % i] = features.N_observations_season(i, filtername=filtername,
119 nside=self.nside,
120 modulo=season_modulo,
121 offset=day_offset,
122 max_season=max_season,
123 season_length=season_length)
124 # Count of all the observations taken in a season
125 self.survey_features['N_obs_all_%i' % i] = features.N_observations_season(i, filtername=None,
126 modulo=season_modulo,
127 offset=day_offset,
128 nside=self.nside,
129 max_season=max_season,
130 season_length=season_length)
132 # Now I need to track the observations taken in each season.
133 self.out_of_bounds_area = np.where(footprint <= 0)[0]
134 self.out_of_bounds_val = out_of_bounds_val
136 self.result = np.zeros(hp.nside2npix(nside), dtype=float)
138 def _calc_value(self, conditions, indx=None):
139 result = self.result.copy()
141 # Compute what season it is at each pixel
142 seasons = utils.season_calc(conditions.night, offset=self.day_offset,
143 modulo=self.season_modulo, max_season=self.max_season,
144 season_length=self.season_length)
146 # Compute the constant parts of the footprint like before
147 desired = self.footprints[-1] / self.all_footprints_sum * np.sum(self.survey_features['N_obs_all_-1'].feature)
148 result[self.constant_footprint_indx] = desired[self.constant_footprint_indx] - self.survey_features['N_obs_-1'].feature[self.constant_footprint_indx]
150 # Now for the rolling sections
151 for season in np.unique(seasons[self.rolling_footprint_indx]):
152 season_indx = np.where(seasons[self.rolling_footprint_indx] == season)[0]
153 desired = self.footprints[season][self.rolling_footprint_indx][season_indx] / self.all_rolling_sum * np.sum(self.survey_features['N_obs_all_%i' % season].feature[self.rolling_footprint_indx])
154 result[self.rolling_footprint_indx[season_indx]] = desired - self.survey_features['N_obs_%i' % season].feature[self.rolling_footprint_indx][season_indx]
156 result[self.out_of_bounds_area] = self.out_of_bounds_val
157 return result
160class Target_map_modulo_basis_function(Base_basis_function):
161 """Basis function that tracks number of observations and tries to match a specified spatial distribution
162 can enter multiple maps that will be used at different times in the survey
164 Parameters
165 ----------
166 day_offset : np.array
167 Healpix map that has the offset to be applied to each pixel when computing what season it is on.
168 filtername : (string 'r')
169 The name of the filter for this target map.
170 nside: int (default_nside)
171 The healpix resolution.
172 target_maps : list of numpy array (None)
173 healpix maps showing the ratio of observations desired for all points on the sky. Last map will be used
174 for season -1. Probably shouldn't support going to season less than -1.
175 norm_factor : float (0.00010519)
176 for converting target map to number of observations. Should be the area of the camera
177 divided by the area of a healpixel divided by the sum of all your goal maps. Default
178 value assumes LSST foV has 1.75 degree radius and the standard goal maps. If using
179 mulitple filters, see lsst.sims.featureScheduler.utils.calc_norm_factor for a utility
180 that computes norm_factor.
181 out_of_bounds_val : float (-10.)
182 Reward value to give regions where there are no observations requested (unitless).
183 season_modulo : int (2)
184 The value to modulate the season by (years).
185 max_season : int (None)
186 For seasons higher than this value (pre-modulo), the final target map is used.
188 """
189 def __init__(self, day_offset=None, filtername='r', nside=None, target_maps=None,
190 norm_factor=None, out_of_bounds_val=-10., season_modulo=2, max_season=None,
191 season_length=365.25):
193 super(Target_map_modulo_basis_function, self).__init__(nside=nside, filtername=filtername)
195 if norm_factor is None:
196 warnings.warn('No norm_factor set, use utils.calc_norm_factor if using multiple filters.')
197 self.norm_factor = 0.00010519
198 else:
199 self.norm_factor = norm_factor
201 self.survey_features = {}
202 # Map of the number of observations in filter
204 for i, temp in enumerate(target_maps[0:-1]):
205 self.survey_features['N_obs_%i' % i] = features.N_observations_season(i, filtername=filtername,
206 nside=self.nside,
207 modulo=season_modulo,
208 offset=day_offset,
209 max_season=max_season,
210 season_length=season_length)
211 # Count of all the observations taken in a season
212 self.survey_features['N_obs_count_all_%i' % i] = features.N_obs_count_season(i, filtername=None,
213 season_modulo=season_modulo,
214 offset=day_offset,
215 nside=self.nside,
216 max_season=max_season,
217 season_length=season_length)
218 # Set the final one to be -1
219 self.survey_features['N_obs_%i' % -1] = features.N_observations_season(-1, filtername=filtername,
220 nside=self.nside,
221 modulo=season_modulo,
222 offset=day_offset,
223 max_season=max_season,
224 season_length=season_length)
225 self.survey_features['N_obs_count_all_%i' % -1] = features.N_obs_count_season(-1, filtername=None,
226 season_modulo=season_modulo,
227 offset=day_offset,
228 nside=self.nside,
229 max_season=max_season,
230 season_length=season_length)
231 if target_maps is None:
232 self.target_maps = utils.generate_goal_map(filtername=filtername, nside=self.nside)
233 else:
234 self.target_maps = target_maps
235 # should probably actually loop over all the target maps?
236 self.out_of_bounds_area = np.where(self.target_maps[0] == 0)[0]
237 self.out_of_bounds_val = out_of_bounds_val
238 self.result = np.zeros(hp.nside2npix(self.nside), dtype=float)
239 self.all_indx = np.arange(self.result.size)
241 # For computing what day each healpix is on
242 if day_offset is None:
243 self.day_offset = np.zeros(hp.nside2npix(self.nside), dtype=float)
244 else:
245 self.day_offset = day_offset
247 self.season_modulo = season_modulo
248 self.max_season = max_season
249 self.season_length = season_length
251 def _calc_value(self, conditions, indx=None):
252 """
253 Parameters
254 ----------
255 indx : list (None)
256 Index values to compute, if None, full map is computed
257 Returns
258 -------
259 Healpix reward map
260 """
262 result = self.result.copy()
263 if indx is None:
264 indx = self.all_indx
266 # Compute what season it is at each pixel
267 seasons = utils.season_calc(conditions.night, offset=self.day_offset,
268 modulo=self.season_modulo, max_season=self.max_season,
269 season_length=self.season_length)
271 composite_target = self.result.copy()[indx]
272 composite_nobs = self.result.copy()[indx]
274 composite_goal_N = self.result.copy()[indx]
276 for season in np.unique(seasons):
277 season_indx = np.where(seasons == season)[0]
278 composite_target[season_indx] = self.target_maps[season][season_indx]
279 composite_nobs[season_indx] = self.survey_features['N_obs_%i' % season].feature[season_indx]
280 composite_goal_N[season_indx] = composite_target[season_indx] * self.survey_features['N_obs_count_all_%i' % season].feature * self.norm_factor
282 result[indx] = composite_goal_N - composite_nobs[indx]
283 result[self.out_of_bounds_area] = self.out_of_bounds_val
285 return result