Coverage for python/lsst/sims/featureScheduler/detailers/detailer.py : 18%

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 lsst.sims.utils import _raDec2Hpid, _approx_RaDec2AltAz, _angularSeparation
2import numpy as np
3from lsst.sims.featureScheduler.utils import approx_altaz2pa, int_rounded
4import copy
6__all__ = ["Base_detailer", "Zero_rot_detailer", "Comcam_90rot_detailer", "Close_alt_detailer",
7 "Take_as_pairs_detailer", "Twilight_triple_detailer", "Spider_rot_detailer"]
10class Base_detailer(object):
11 """
12 A Detailer is an object that takes a list of proposed observations and adds "details" to them. The
13 primary purpose is that the Markov Decision Process does an excelent job selecting RA,Dec,filter
14 combinations, but we may want to add additional logic such as what to set the camera rotation angle
15 to, or what to use for an exposure time. We could also modify the order of the proposed observations.
16 For Deep Drilling Fields, a detailer could be useful for computing dither positions and modifying
17 the exact RA,Dec positions.
18 """
20 def __init__(self, nside=32):
21 """
22 """
23 # Dict to hold all the features we want to track
24 self.survey_features = {}
25 self.nside = nside
27 def add_observation(self, observation, indx=None):
28 """
29 Parameters
30 ----------
31 observation : np.array
32 An array with information about the input observation
33 indx : np.array
34 The indices of the healpix map that the observation overlaps with
35 """
36 for feature in self.survey_features:
37 self.survey_features[feature].add_observation(observation, indx=indx)
39 def __call__(self, observation_list, conditions):
40 """
41 Parameters
42 ----------
43 observation_list : list of observations
44 The observations to detail.
45 conditions : lsst.sims.featureScheduler.conditions object
47 Returns
48 -------
49 List of observations.
50 """
52 return observation_list
55class Zero_rot_detailer(Base_detailer):
56 """
57 Detailer to set the camera rotation to be apporximately zero in rotTelPos.
58 Because it can never be written too many times:
59 rotSkyPos = rotTelPos - ParallacticAngle
60 But, wait, what? Is it really the other way?
61 """
63 def __call__(self, observation_list, conditions):
65 # XXX--should I convert the list into an array and get rid of this loop?
66 for obs in observation_list:
67 alt, az = _approx_RaDec2AltAz(obs['RA'], obs['dec'], conditions.site.latitude_rad,
68 conditions.site.longitude_rad, conditions.mjd)
69 obs_pa = approx_altaz2pa(alt, az, conditions.site.latitude_rad)
70 obs['rotSkyPos'] = obs_pa
72 return observation_list
75class Spider_rot_detailer(Base_detailer):
76 """
77 Set the camera rotation to +/- 45 degrees so diffraction spikes align along chip rows
78 and columns
79 """
81 def __call__(self, observation_list, conditions):
82 indx = int(conditions.night % 2)
83 rotTelPos = np.radians([45., 315.][indx])
85 for obs in observation_list:
86 obs['rotSkyPos'] = np.nan
87 obs['rotTelPos'] = rotTelPos
89 return observation_list
92class Comcam_90rot_detailer(Base_detailer):
93 """
94 Detailer to set the camera rotation so rotSkyPos is 0, 90, 180, or 270 degrees. Whatever
95 is closest to rotTelPos of zero.
96 """
98 def __call__(self, observation_list, conditions):
99 favored_rotSkyPos = np.radians([0., 90., 180., 270., 360.]).reshape(5, 1)
100 obs_array =np.concatenate(observation_list)
101 alt, az = _approx_RaDec2AltAz(obs_array['RA'], obs_array['dec'], conditions.site.latitude_rad,
102 conditions.site.longitude_rad, conditions.mjd)
103 parallactic_angle = approx_altaz2pa(alt, az, conditions.site.latitude_rad)
104 # If we set rotSkyPos to parallactic angle, rotTelPos will be zero. So, find the
105 # favored rotSkyPos that is closest to PA to keep rotTelPos as close as possible to zero.
106 ang_diff = np.abs(parallactic_angle - favored_rotSkyPos)
107 min_indxs = np.argmin(ang_diff, axis=0)
108 # can swap 360 and zero if needed?
109 final_rotSkyPos = favored_rotSkyPos[min_indxs]
110 # Set all the observations to the proper rotSkyPos
111 for rsp, obs in zip(final_rotSkyPos, observation_list):
112 obs['rotSkyPos'] = rsp
114 return observation_list
117class Close_alt_detailer(Base_detailer):
118 """
119 re-order a list of observations so that the closest in altitude to the current pointing is first.
121 Parameters
122 ----------
123 alt_band : float (10)
124 The altitude band to try and stay in (degrees)
125 """
126 def __init__(self, alt_band=10.):
127 super(Close_alt_detailer, self).__init__()
128 self.alt_band = int_rounded(np.radians(alt_band))
130 def __call__(self, observation_list, conditions):
131 obs_array = np.concatenate(observation_list)
132 alt, az = _approx_RaDec2AltAz(obs_array['RA'], obs_array['dec'], conditions.site.latitude_rad,
133 conditions.site.longitude_rad, conditions.mjd)
134 alt_diff = np.abs(alt - conditions.telAlt)
135 in_band = np.where(int_rounded(alt_diff) <= self.alt_band)[0]
136 if in_band.size == 0:
137 in_band = np.arange(alt.size)
139 # Find the closest in angular distance of the points that are in band
140 ang_dist = _angularSeparation(az[in_band], alt[in_band], conditions.telAz, conditions.telAlt)
141 good = np.min(np.where(ang_dist == ang_dist.min())[0])
142 indx = in_band[good]
143 result = observation_list[indx:] + observation_list[:indx]
144 return result
147class Take_as_pairs_detailer(Base_detailer):
148 def __init__(self, filtername='r'):
149 """
150 """
151 super(Take_as_pairs_detailer, self).__init__()
152 self.filtername = filtername
154 def __call__(self, observation_list, conditions):
155 paired = copy.deepcopy(observation_list)
156 for obs in paired:
157 obs['filter'] = self.filtername
158 if conditions.current_filter == self.filtername:
159 for obs in paired:
160 obs['note'] = obs['note'][0] + ', a'
161 for obs in observation_list:
162 obs['note'] = obs['note'][0] + ', b'
163 result = paired + observation_list
164 else:
165 for obs in paired:
166 obs['note'] = obs['note'][0] + ', b'
167 for obs in observation_list:
168 obs['note'] = obs['note'][0] + ', a'
169 result = observation_list + paired
170 # XXX--maybe a temp debugging thing, label what part of sequence each observation is.
171 for i, obs in enumerate(result):
172 obs['survey_id'] = i
173 return result
176class Twilight_triple_detailer(Base_detailer):
177 def __init__(self, slew_estimate=5.0, n_repeat=3):
178 super(Twilight_triple_detailer, self).__init__()
179 self.slew_estimate = slew_estimate
180 self.n_repeat = n_repeat
182 def __call__(self, observation_list, conditions):
184 obs_array = np.concatenate(observation_list)
186 # Estimate how much time is left in the twilgiht block
187 potential_times = np.array([conditions.sun_n18_setting - conditions.mjd,
188 conditions.sun_n12_rising - conditions.mjd])
190 potential_times = np.min(potential_times[np.where(potential_times > 0)]) * 24.*3600.
192 # How long will observations take?
193 cumulative_slew = np.arange(obs_array.size) * self.slew_estimate
194 cumulative_expt = np.cumsum(obs_array['exptime'])
195 cumulative_time = cumulative_slew + cumulative_expt
196 # If we are way over, truncate the list before doing the triple
197 if np.max(cumulative_time) > potential_times:
198 max_indx = np.where(cumulative_time/self.n_repeat <= potential_times)[0]
199 if np.size(max_indx) == 0:
200 # Very bad magic number fudge
201 max_indx = 3
202 else:
203 max_indx = np.max(max_indx)
204 if max_indx == 0:
205 max_indx += 1
206 observation_list = observation_list[0:max_indx]
208 # Repeat the observations n times
209 out_obs = []
210 for i in range(self.n_repeat):
211 out_obs.extend(copy.deepcopy(observation_list))
213 return out_obs