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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

from builtins import object 

from collections import OrderedDict 

import numpy as np 

from astropy.time import Time, TimeDelta 

import random 

 

 

__all__ = ['UnscheduledDowntimeData'] 

 

 

class UnscheduledDowntimeData(object): 

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

 

Parameters 

---------- 

start_time : astropy.time.Time 

The time of the start of the simulation. 

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

seed : int, opt 

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

start_of_night_offset : float, opt 

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

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

survey_length : int, opt 

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

""" 

 

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

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

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

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

 

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

self.seed = seed 

self.survey_length = survey_length 

year_start = start_time.datetime.year 

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

 

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

self.downtime = None 

self.make_data() 

 

def __call__(self): 

"""Return the array of unscheduled downtimes. 

 

Parameters 

---------- 

time : astropy.time.Time 

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

 

Returns 

------- 

np.ndarray 

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

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

""" 

return self.downtime 

 

def _downtimeStatus(self, time): 

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

""" 

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

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

if next_start > next_end: 

current = self.downtime[next_end] 

else: 

current = None 

future = self.downtime[next_start:] 

return current, future 

 

def make_data(self): 

"""Configure the set of unscheduled downtimes. 

 

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

of the downtime type occurance. 

 

The random downtime is calculated using the following probabilities: 

 

minor event 

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

intermediate 

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

major event 

7 nights = 1/2*365 days 

catastrophic event 

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

""" 

random.seed(self.seed) 

 

starts = [] 

ends = [] 

acts = [] 

night = 0 

while night < self.survey_length: 

prob = random.random() 

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

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

starts.append(start_night) 

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

ends.append(end_night) 

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

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

continue 

else: 

prob = random.random() 

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

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

starts.append(start_night) 

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

ends.append(end_night) 

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

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

continue 

else: 

prob = random.random() 

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

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

starts.append(start_night) 

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

ends.append(end_night) 

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

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

continue 

else: 

prob = random.random() 

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

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

starts.append(start_night) 

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

ends.append(end_night) 

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

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

night += 1 

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

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

 

def config_info(self): 

"""Report information about configuration of this data. 

 

Returns 

------- 

OrderedDict 

""" 

config_info = OrderedDict() 

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

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

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

config_info['Random seed'] = self.seed 

config_info['Unscheduled Downtimes'] = self.downtime 

return config_info 

 

def total_downtime(self): 

"""Return total downtime (in days). 

 

Returns 

------- 

int 

Total number of downtime days. 

""" 

total = 0 

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

total += td.jd 

return total