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

1from builtins import object 

2from collections import OrderedDict 

3import numpy as np 

4from astropy.time import Time, TimeDelta 

5import random 

6 

7 

8__all__ = ['UnscheduledDowntimeData'] 

9 

10 

11class UnscheduledDowntimeData(object): 

12 """Handle (and create) the unscheduled downtime information. 

13 

14 Parameters 

15 ---------- 

16 start_time : astropy.time.Time 

17 The time of the start of the simulation. 

18 The cloud database will be assumed to start on Jan 01 of the same year. 

19 seed : int, opt 

20 The random seed for creating the random nights of unscheduled downtime. Default 1516231120. 

21 start_of_night_offset : float, opt 

22 The fraction of a day to offset from MJD.0 to reach the defined start of a night ('noon' works). 

23 Default 0.16 (UTC midnight in Chile) - 0.5 (minus half a day) = -0.34 

24 survey_length : int, opt 

25 The number of nights in the total survey. Default 3650*2. 

26 """ 

27 

28 MINOR_EVENT = {'P': 0.0137, 'length': 1, 'level': "minor event"} 

29 INTERMEDIATE_EVENT = {'P': 0.00548, 'length': 3, 'level': "intermediate event"} 

30 MAJOR_EVENT = {'P': 0.00137, 'length': 7, 'level': "major event"} 

31 CATASTROPHIC_EVENT = {'P': 0.000274, 'length': 14, 'level': "catastrophic event"} 

32 

33 def __init__(self, start_time, seed=1516231120, start_of_night_offset=-0.34, survey_length=3650*2): 

34 self.seed = seed 

35 self.survey_length = survey_length 

36 year_start = start_time.datetime.year 

37 self.night0 = Time('%d-01-01' % year_start, format='isot', scale='tai') + start_of_night_offset 

38 

39 # Scheduled downtime data is a np.ndarray of start / end / activity for each scheduled downtime. 

40 self.downtime = None 

41 self.make_data() 

42 

43 def __call__(self): 

44 """Return the array of unscheduled downtimes. 

45 

46 Parameters 

47 ---------- 

48 time : astropy.time.Time 

49 Time in the simulation for which to find the current downtime. 

50 

51 Returns 

52 ------- 

53 np.ndarray 

54 The array of all unscheduled downtimes, with keys for 'start', 'end', 'activity', 

55 corresponding to astropy.time.Time, astropy.time.Time, and str. 

56 """ 

57 return self.downtime 

58 

59 def _downtimeStatus(self, time): 

60 """Look behind the scenes at the downtime status/next values 

61 """ 

62 next_start = self.downtime['start'].searchsorted(time, side='right') 

63 next_end = self.downtime['end'].searchsorted(time, side='right') 

64 if next_start > next_end: 

65 current = self.downtime[next_end] 

66 else: 

67 current = None 

68 future = self.downtime[next_start:] 

69 return current, future 

70 

71 def make_data(self): 

72 """Configure the set of unscheduled downtimes. 

73 

74 This function creates the unscheduled downtimes based on a set of probabilities 

75 of the downtime type occurance. 

76 

77 The random downtime is calculated using the following probabilities: 

78 

79 minor event 

80 remainder of night and next day = 5/365 days e.g. power supply failure 

81 intermediate 

82 3 nights = 2/365 days e.g. repair filter mechanism, rotator, hexapod, or shutter 

83 major event 

84 7 nights = 1/2*365 days 

85 catastrophic event 

86 14 nights = 1/3650 days e.g. replace a raft 

87 """ 

88 random.seed(self.seed) 

89 

90 starts = [] 

91 ends = [] 

92 acts = [] 

93 night = 0 

94 while night < self.survey_length: 

95 prob = random.random() 

96 if prob < self.CATASTROPHIC_EVENT['P']: 

97 start_night = self.night0 + TimeDelta(night, format='jd') 

98 starts.append(start_night) 

99 end_night = start_night + TimeDelta(self.CATASTROPHIC_EVENT['length'], format='jd') 

100 ends.append(end_night) 

101 acts.append(self.CATASTROPHIC_EVENT['level']) 

102 night += self.CATASTROPHIC_EVENT['length'] + 1 

103 continue 

104 else: 

105 prob = random.random() 

106 if prob < self.MAJOR_EVENT['P']: 

107 start_night = self.night0 + TimeDelta(night, format='jd') 

108 starts.append(start_night) 

109 end_night = start_night + TimeDelta(self.MAJOR_EVENT['length'], format='jd') 

110 ends.append(end_night) 

111 acts.append(self.MAJOR_EVENT['level']) 

112 night += self.MAJOR_EVENT['length'] + 1 

113 continue 

114 else: 

115 prob = random.random() 

116 if prob < self.INTERMEDIATE_EVENT['P']: 

117 start_night = self.night0 + TimeDelta(night, format='jd') 

118 starts.append(start_night) 

119 end_night = start_night + TimeDelta(self.INTERMEDIATE_EVENT['length'], format='jd') 

120 ends.append(end_night) 

121 acts.append(self.INTERMEDIATE_EVENT['level']) 

122 night += self.INTERMEDIATE_EVENT['length'] + 1 

123 continue 

124 else: 

125 prob = random.random() 

126 if prob < self.MINOR_EVENT['P']: 

127 start_night = self.night0 + TimeDelta(night, format='jd') 

128 starts.append(start_night) 

129 end_night = start_night + TimeDelta(self.MINOR_EVENT['length'], format='jd') 

130 ends.append(end_night) 

131 acts.append(self.MINOR_EVENT['level']) 

132 night += self.MINOR_EVENT['length'] + 1 

133 night += 1 

134 self.downtime = np.array(list(zip(starts, ends, acts)), 

135 dtype=[('start', 'O'), ('end', 'O'), ('activity', 'O')]) 

136 

137 def config_info(self): 

138 """Report information about configuration of this data. 

139 

140 Returns 

141 ------- 

142 OrderedDict 

143 """ 

144 config_info = OrderedDict() 

145 config_info['Survey start'] = self.night0.isot 

146 config_info['Survey end'] = (self.night0 + TimeDelta(self.survey_length)).isot 

147 config_info['Total unscheduled downtime (days)'] = self.total_downtime() 

148 config_info['Random seed'] = self.seed 

149 config_info['Unscheduled Downtimes'] = self.downtime 

150 return config_info 

151 

152 def total_downtime(self): 

153 """Return total downtime (in days). 

154 

155 Returns 

156 ------- 

157 int 

158 Total number of downtime days. 

159 """ 

160 total = 0 

161 for td in (self.downtime['end'] - self.downtime['start']): 

162 total += td.jd 

163 return total