Coverage for python/lsst/sims/featureScheduler/basis_functions/feasibility_funcs.py : 20%

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
3import matplotlib.pylab as plt
4from lsst.sims.featureScheduler.basis_functions import Base_basis_function
7__all__ = ['Filter_loaded_basis_function', 'Time_to_twilight_basis_function',
8 'Not_twilight_basis_function', 'Force_delay_basis_function',
9 'Hour_Angle_limit_basis_function', 'Moon_down_basis_function',
10 'Fraction_of_obs_basis_function', 'Clouded_out_basis_function',
11 'Rising_more_basis_function', 'Soft_delay_basis_function',
12 'Look_ahead_ddf_basis_function', 'Sun_alt_limit_basis_function',
13 'Time_in_twilight_basis_function', 'Night_modulo_basis_function']
16class Filter_loaded_basis_function(Base_basis_function):
17 """Check that the filter(s) needed are loaded
19 Parameters
20 ----------
21 filternames : str or list of str
22 The filternames that need to be mounted to execute.
23 """
24 def __init__(self, filternames='r'):
25 super(Filter_loaded_basis_function, self).__init__()
26 if type(filternames) is not list:
27 filternames = [filternames]
28 self.filternames = filternames
30 def check_feasibility(self, conditions):
32 for filtername in self.filternames:
33 result = filtername in conditions.mounted_filters
34 if result is False:
35 return result
36 return result
39class Night_modulo_basis_function(Base_basis_function):
40 """Only return true on certain nights
41 """
42 def __init__(self, pattern=None):
43 super(Night_modulo_basis_function, self).__init__()
44 if pattern is None:
45 pattern = [True, False]
46 self.pattern = pattern
47 self.mod_val = len(self.pattern)
49 def check_feasibility(self, conditions):
50 indx = int(conditions.night % self.mod_val)
51 result = self.pattern[indx]
52 return result
55class Time_in_twilight_basis_function(Base_basis_function):
56 """Make sure there is some time left in twilight.
58 Parameters
59 ----------
60 time_needed : float (5)
61 The time needed remaining in twilight (minutes)
62 """
63 def __init__(self, time_needed=5.):
64 super(Time_in_twilight_basis_function, self).__init__()
65 self.time_needed = time_needed/60./24. # To days
67 def check_feasibility(self, conditions):
68 result = False
69 time1 = conditions.sun_n18_setting - conditions.mjd
70 time2 = conditions.sun_n12_rising - conditions.mjd
72 if time1 > self.time_needed:
73 result = True
74 else:
75 if conditions.sunAlt > np.radians(-18.):
76 if time2 > self.time_needed:
77 result = True
78 return result
82class Time_to_twilight_basis_function(Base_basis_function):
83 """Make sure there is enough time before twilight. Useful
84 if you want to check before starting a long sequence of observations.
86 Parameters
87 ----------
88 time_needed : float (30.)
89 The time needed to run a survey (mintues).
90 alt_limit : int (18)
91 The sun altitude limit to use. Must be 12 or 18
92 """
93 def __init__(self, time_needed=30., alt_limit=18):
94 super(Time_to_twilight_basis_function, self).__init__()
95 self.time_needed = time_needed/60./24. # To days
96 self.alt_limit = str(alt_limit)
98 def check_feasibility(self, conditions):
99 available_time = getattr(conditions, 'sun_n' + self.alt_limit + '_rising') - conditions.mjd
100 result = available_time > self.time_needed
101 return result
104class Not_twilight_basis_function(Base_basis_function):
105 def __init__(self, sun_alt_limit=-18):
106 """
107 # Should be -18 or -12
108 """
109 self.sun_alt_limit = str(int(sun_alt_limit)).replace('-', 'n')
110 super(Not_twilight_basis_function, self).__init__()
112 def check_feasibility(self, conditions):
113 result = True
114 if conditions.mjd < getattr(conditions, 'sun_'+self.sun_alt_limit+'_setting'):
115 result = False
116 if conditions.mjd > getattr(conditions, 'sun_'+self.sun_alt_limit+'_rising'):
117 result = False
118 return result
121class Force_delay_basis_function(Base_basis_function):
122 """Keep a survey from executing to rapidly.
124 Parameters
125 ----------
126 days_delay : float (2)
127 The number of days to force a gap on.
128 """
129 def __init__(self, days_delay=2., survey_name=None):
130 super(Force_delay_basis_function, self).__init__()
131 self.days_delay = days_delay
132 self.survey_name = survey_name
133 self.survey_features['last_obs_self'] = features.Last_observation(survey_name=self.survey_name)
135 def check_feasibility(self, conditions):
136 result = True
137 if conditions.mjd - self.survey_features['last_obs_self'].feature['mjd'] < self.days_delay:
138 result = False
139 return result
142class Soft_delay_basis_function(Base_basis_function):
143 """Like Force_delay, but go ahead and let things catch up if they fall far behind.
145 Parameters
146 ----------
148 """
149 def __init__(self, fractions=[0.000, 0.009, 0.017], delays=[0., 0.5, 1.5], survey_name=None):
150 if len(fractions) != len(delays):
151 raise ValueError('fractions and delays must be same length')
152 super(Soft_delay_basis_function, self).__init__()
153 self.delays = delays
154 self.survey_name = survey_name
155 self.survey_features['last_obs_self'] = features.Last_observation(survey_name=self.survey_name)
156 self.fractions = fractions
157 self.survey_features['Ntot'] = features.N_obs_survey()
158 self.survey_features['N_survey'] = features.N_obs_survey(note=self.survey_name)
160 def check_feasibility(self, conditions):
161 result = True
162 current_ratio = self.survey_features['N_survey'].feature / self.survey_features['Ntot'].feature
163 indx = np.searchsorted(self.fractions, current_ratio)
164 if indx == len(self.fractions):
165 indx -= 1
166 delay = self.delays[indx]
167 if conditions.mjd - self.survey_features['last_obs_self'].feature['mjd'] < delay:
168 result = False
169 return result
172class Hour_Angle_limit_basis_function(Base_basis_function):
173 """Only execute a survey in limited hour angle ranges. Useful for
174 limiting Deep Drilling Fields.
176 Parameters
177 ----------
178 RA : float (0.)
179 RA of the target (degrees).
180 ha_limits : list of lists
181 limits for what hour angles are acceptable (hours). e.g.,
182 to give 4 hour window around RA=0, ha_limits=[[22,24], [0,2]]
183 """
184 def __init__(self, RA=0., ha_limits=None):
185 super(Hour_Angle_limit_basis_function, self).__init__()
186 self.ra_hours = RA/360.*24.
187 self.HA_limits = np.array(ha_limits)
189 def check_feasibility(self, conditions):
190 target_HA = (conditions.lmst - self.ra_hours) % 24
191 # Are we in any of the possible windows
192 result = False
193 for limit in self.HA_limits:
194 lres = limit[0] <= target_HA < limit[1]
195 result = result or lres
197 return result
200class Moon_down_basis_function(Base_basis_function):
201 """Demand the moon is down """
202 def check_feasibility(self, conditions):
203 result = True
204 if conditions.moonAlt > 0:
205 result = False
206 return result
209class Fraction_of_obs_basis_function(Base_basis_function):
210 """Limit the fraction of all observations that can be labled a certain
211 survey name. Useful for keeping DDFs from exceeding a given fraction of the
212 total survey.
214 Parameters
215 ----------
216 frac_total : float
217 The fraction of total observations that can be of this survey
218 survey_name : str
219 The name of the survey
220 """
221 def __init__(self, frac_total, survey_name=''):
222 super(Fraction_of_obs_basis_function, self).__init__()
223 self.survey_name = survey_name
224 self.frac_total = frac_total
225 self.survey_features['Ntot'] = features.N_obs_survey()
226 self.survey_features['N_survey'] = features.N_obs_survey(note=self.survey_name)
228 def check_feasibility(self, conditions):
229 # If nothing has been observed, fine to go
230 result = True
231 if self.survey_features['Ntot'].feature == 0:
232 return result
233 ratio = self.survey_features['N_survey'].feature / self.survey_features['Ntot'].feature
234 if ratio > self.frac_total:
235 result = False
236 return result
239class Look_ahead_ddf_basis_function(Base_basis_function):
240 """Look into the future to decide if it's a good time to observe or block.
242 Parameters
243 ----------
244 frac_total : float
245 The fraction of total observations that can be of this survey
246 aggressive_fraction : float
247 If the fraction of observations drops below ths value, be more aggressive in scheduling.
248 e.g., do not wait for conditions to improve, execute as soon as possible.
249 time_needed : float (30.)
250 Estimate of the amount of time needed to execute DDF sequence (minutes).
251 RA : float (0.)
252 The RA of the DDF
253 ha_limits : list of lists (None)
254 limits for what hour angles are acceptable (hours). e.g.,
255 to give 4 hour window around HA=0, ha_limits=[[22,24], [0,2]]
256 survey_name : str ('')
257 The name of the survey
258 time_jump : float (44.)
259 The amount of time to assume will jump ahead if another survey executes (minutes)
260 sun_alt_limit : float (-18.)
261 The limit to assume twilight starts (degrees)
262 """
263 def __init__(self, frac_total, aggressive_fraction, time_needed=30., RA=0.,
264 ha_limits=None, survey_name='', time_jump=44., sun_alt_limit=-18.):
265 super(Look_ahead_ddf_basis_function, self).__init__()
266 if aggressive_fraction > frac_total:
267 raise ValueError('aggressive_fraction should be less than frac_total')
268 self.survey_name = survey_name
269 self.frac_total = frac_total
270 self.ra_hours = RA/360.*24.
271 self.HA_limits = np.array(ha_limits)
272 self.sun_alt_limit = str(int(sun_alt_limit)).replace('-', 'n')
273 self.time_jump = time_jump / 60. # To hours
274 self.time_needed = time_needed / 60. # To hours
275 self.aggressive_fraction = aggressive_fraction
276 self.survey_features['Ntot'] = features.N_obs_survey()
277 self.survey_features['N_survey'] = features.N_obs_survey(note=self.survey_name)
279 def check_feasibility(self, conditions):
280 result = True
281 target_HA = (conditions.lmst - self.ra_hours) % 24
282 ratio = self.survey_features['N_survey'].feature / self.survey_features['Ntot'].feature
283 available_time = getattr(conditions, 'sun_' + self.sun_alt_limit + '_rising') - conditions.mjd
284 # If it's more that self.time_jump to hour angle zero
285 # See if there will be enough time to twilight in the future
286 if (target_HA > 12) & (target_HA < 24.-self.time_jump):
287 if available_time > (self.time_needed + self.time_jump):
288 result = False
289 # If the survey has fallen far behind, be agressive and observe anytime it's up.
290 if ratio < self.aggressive_fraction:
291 result = True
292 return result
295class Clouded_out_basis_function(Base_basis_function):
296 def __init__(self, cloud_limit=0.7):
297 super(Clouded_out_basis_function, self).__init__()
298 self.cloud_limit = cloud_limit
300 def check_feasibility(self, conditions):
301 result = True
302 if conditions.bulk_cloud > self.cloud_limit:
303 result = False
304 return result
307class Rising_more_basis_function(Base_basis_function):
308 """Say a spot is not available if it will rise substatially before twilight.
310 Parameters
311 ----------
312 RA : float
313 The RA of the point in the sky (degrees)
314 pad : float
315 When to start observations if there's plenty of time before twilight (minutes)
316 """
317 def __init__(self, RA, pad=30.):
318 super(Rising_more_basis_function, self).__init__()
319 self.RA_hours = RA * 24 / 360.
320 self.pad = pad/60. # To hours
322 def check_feasibility(self, conditions):
323 result = True
324 hour_angle = conditions.lmst - self.RA_hours
325 # If it's rising, and twilight is well beyond when it crosses the meridian
326 time_to_twi = (conditions.sun_n18_rising - conditions.mjd)*24.
327 if (hour_angle < -self.pad) & (np.abs(hour_angle) < (time_to_twi - self.pad)):
328 result = False
329 return result
332class Sun_alt_limit_basis_function(Base_basis_function):
333 """Don't try unless the sun is below some limit
334 """
336 def __init__(self, alt_limit=-12.1):
337 super(Sun_alt_limit_basis_function, self).__init__()
338 self.alt_limit = np.radians(alt_limit)
340 def check_feasibility(self, conditions):
341 result = True
342 if conditions.sunAlt > self.alt_limit:
343 result = False
344 return result
347## XXX--TODO: Can include checks to see if downtime is coming, clouds are coming, moon rising, or surveys in a higher tier
348# Have observations they want to execute soon.