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 __future__ import print_function 

2# Collection of utilities for MAF that relate to Opsim specifically. 

3 

4import os 

5import numpy as np 

6from .outputUtils import printDict 

7 

8__all__ = ['writeConfigs', 'getFieldData', 'getSimData', 

9 'scaleBenchmarks', 'calcCoaddedDepth'] 

10 

11 

12def writeConfigs(opsimDb, outDir): 

13 """ 

14 Convenience function to get the configuration information from the opsim database and write 

15 this information to text files 'configSummary.txt' and 'configDetails.txt'. 

16 

17 Parameters 

18 ---------- 

19 opsimDb : OpsimDatabase 

20 The opsim database from which to pull the opsim configuration information. 

21 Opsim SQLite databases save this configuration information in their config table. 

22 outputDir : str 

23 The path to the output directory, where to write the config*.txt files. 

24 """ 

25 configSummary, configDetails = opsimDb.fetchConfig() 

26 outfile = os.path.join(outDir, 'configSummary.txt') 

27 f = open(outfile, 'w') 

28 printDict(configSummary, 'Summary', f) 

29 f.close() 

30 outfile = os.path.join(outDir, 'configDetails.txt') 

31 f = open(outfile, 'w') 

32 printDict(configDetails, 'Details', f) 

33 f.close() 

34 

35 

36def getFieldData(opsimDb, sqlconstraint): 

37 """ 

38 Find the fields (ra/dec/fieldID) relevant for a given sql constraint. 

39 If the opsimDb contains a Fields table, it uses 

40 :meth:`OpsimDatabase.fetchFieldsFromFieldTable()` 

41 to get the fields. If the opsimDb contains only a Summary, it uses 

42 :meth:`OpsimDatabase.fetchFieldsFromSummaryTable()`. 

43 

44 Parameters 

45 ---------- 

46 opsimDb : OpsimDatabase 

47 An opsim database to use to query for field information. 

48 sqlconstraint : str 

49 A SQL constraint to apply to the query (i.e. find all fields for DD proposal) 

50 

51 Returns 

52 ------- 

53 numpy.ndarray 

54 A numpy structured array containing the field information. This data will ALWAYS be in radians. 

55 """ 

56 # Get all fields used for all proposals. 

57 if 'proposalId' not in sqlconstraint: 

58 propids, propTags = opsimDb.fetchPropInfo() 

59 propids = list(propids.keys()) 

60 else: 

61 # Parse the propID out of the sqlconstraint. 

62 # example: sqlconstraint: filter = r and (propid = 219 or propid = 155) and propid!= 90 

63 sqlconstraint = sqlconstraint.replace('=', ' = ').replace('(', '').replace(')', '') 

64 sqlconstraint = sqlconstraint.replace("'", '').replace('"', '') 

65 # Allow for choosing all but a particular proposal. 

66 sqlconstraint = sqlconstraint.replace('! =', ' !=') 

67 sqlconstraint = sqlconstraint.replace(' ', ' ') 

68 sqllist = sqlconstraint.split(' ') 

69 propids = [] 

70 nonpropids = [] 

71 i = 0 

72 while i < len(sqllist): 

73 if sqllist[i].lower() == 'proposalid': 

74 i += 1 

75 if sqllist[i] == "=": 

76 i += 1 

77 propids.append(int(sqllist[i])) 

78 elif sqllist[i] == '!=': 

79 i += 1 

80 nonpropids.append(int(sqllist[i])) 

81 i += 1 

82 if len(propids) == 0: 

83 propids, propTags = opsimDb.fetchPropInfo() 

84 propids = list(propids.keys()) 

85 if len(nonpropids) > 0: 

86 for nonpropid in nonpropids: 

87 if nonpropid in propids: 

88 propids.remove(nonpropid) 

89 # And query the field Table. 

90 if 'Field' in opsimDb.tables: 

91 # The field table is always in degrees. 

92 fieldData = opsimDb.fetchFieldsFromFieldTable(propids, degreesToRadians=True) 

93 # Or give up and query the summary table. 

94 else: 

95 fieldData = opsimDb.fetchFieldsFromSummaryTable(sqlconstraint) 

96 return fieldData 

97 

98 

99def getSimData(opsimDb, sqlconstraint, dbcols, stackers=None, groupBy='default', tableName=None): 

100 """ 

101 Query an opsim database for the needed data columns and run any required stackers. 

102 

103 Parameters 

104 ---------- 

105 opsimDb : OpsimDatabase 

106 sqlconstraint : str 

107 SQL constraint to apply to query for observations. 

108 dbcols : list of str 

109 Columns required from the database. 

110 stackers : list of Stackers 

111 Stackers to be used to generate additional columns. 

112 tableName : str 

113 Name of the table to query. 

114 distinctExpMJD : bool 

115 Only select observations with a distinct expMJD value. This is overriden if groupBy is not expMJD. 

116 groupBy : str 

117 Column name to group SQL results by. 

118 

119 Returns 

120 ------- 

121 numpy.ndarray 

122 A numpy structured array with columns resulting from dbcols + stackers, for observations matching 

123 the SQLconstraint. 

124 """ 

125 # Get data from database. 

126 simData = opsimDb.fetchMetricData(dbcols, sqlconstraint, groupBy=groupBy, tableName=tableName) 

127 if len(simData) == 0: 

128 raise UserWarning('No data found matching sqlconstraint %s' % (sqlconstraint)) 

129 # Now add the stacker columns. 

130 if stackers is not None: 

131 for s in stackers: 

132 simData = s.run(simData) 

133 return simData 

134 

135 

136def scaleBenchmarks(runLength, benchmark='design'): 

137 """ 

138 Set the design and stretch values of the number of visits, area of the footprint, 

139 seeing values, FWHMeff values, skybrightness, and single visit depth (based on SRD values). 

140 Scales number of visits for the length of the run, relative to 10 years. 

141 

142 Parameters 

143 ---------- 

144 runLength : float 

145 The length (in years) of the run. 

146 benchmark : str 

147 design or stretch - which version of the SRD values to return. 

148 requested is another option, in which case the values of the number of visits requested 

149 by the OpSim run (recorded in the Config table) is returned. 

150 

151 Returns 

152 ------- 

153 dict of floats 

154 A dictionary containing the number of visits, area of footprint, seeing and FWHMeff values, 

155 skybrightness and single visit depth for either the design or stretch SRD values. 

156 """ 

157 # Set baseline (default) numbers for the baseline survey length (10 years). 

158 baseline = 10. 

159 

160 design = {} 

161 stretch = {} 

162 

163 design['nvisitsTotal'] = 825 

164 stretch['nvisitsTotal'] = 1000 

165 design['Area'] = 18000 

166 stretch['Area'] = 20000 

167 

168 design['nvisits']={'u':56,'g':80, 'r':184, 'i':184, 'z':160, 'y':160} 

169 stretch['nvisits']={'u':70,'g':100, 'r':230, 'i':230, 'z':200, 'y':200} 

170 

171 design['skybrightness'] = {'u':21.8, 'g':22., 'r':21.3, 'i':20.0, 'z':19.1, 'y':17.5} # mag/sq arcsec 

172 stretch['skybrightness'] = {'u':21.8, 'g':22., 'r':21.3, 'i':20.0, 'z':19.1, 'y':17.5} 

173 

174 design['seeing'] = {'u':0.77, 'g':0.73, 'r':0.7, 'i':0.67, 'z':0.65, 'y':0.63} # arcsec - old seeing values 

175 stretch['seeing'] = {'u':0.77, 'g':0.73, 'r':0.7, 'i':0.67, 'z':0.65, 'y':0.63} 

176 

177 design['FWHMeff'] = {'u':0.92, 'g':0.87, 'r':0.83, 'i':0.80, 'z':0.78, 'y':0.76} # arcsec - new FWHMeff values (scaled from old seeing) 

178 stretch['FWHMeff'] = {'u':0.92, 'g':0.87, 'r':0.83, 'i':0.80, 'z':0.78, 'y':0.76} 

179 

180 design['singleVisitDepth'] = {'u':23.9,'g':25.0, 'r':24.7, 'i':24.0, 'z':23.3, 'y':22.1} 

181 stretch['singleVisitDepth'] = {'u':24.0,'g':25.1, 'r':24.8, 'i':24.1, 'z':23.4, 'y':22.2} 

182 

183 # Scale the number of visits. 

184 if runLength != baseline: 

185 scalefactor = float(runLength) / float(baseline) 

186 # Calculate scaled value for design and stretch values of nvisits, per filter. 

187 for f in design['nvisits']: 

188 design['nvisits'][f] = int(np.floor(design['nvisits'][f] * scalefactor)) 

189 stretch['nvisits'][f] = int(np.floor(stretch['nvisits'][f] * scalefactor)) 

190 

191 if benchmark == 'design': 

192 return design 

193 elif benchmark == 'stretch': 

194 return stretch 

195 else: 

196 raise ValueError("Benchmark value %s not understood: use 'design' or 'stretch'" % (benchmark)) 

197 

198 

199def calcCoaddedDepth(nvisits, singleVisitDepth): 

200 """ 

201 Calculate the coadded depth expected for a given number of visits and single visit depth. 

202 

203 Parameters 

204 ---------- 

205 nvisits : dict of ints or floats 

206 Dictionary (per filter) of number of visits 

207 singleVisitDepth : dict of floats 

208 Dictionary (per filter) of the single visit depth 

209 

210 Returns 

211 ------- 

212 dict of floats 

213 Dictionary of coadded depths per filter. 

214 """ 

215 coaddedDepth = {} 

216 for f in nvisits: 

217 if f not in singleVisitDepth: 

218 raise ValueError('Filter keys in nvisits and singleVisitDepth must match') 

219 coaddedDepth[f] = float(1.25 * np.log10(nvisits[f] * 10**(0.8*singleVisitDepth[f]))) 

220 if not np.isfinite(coaddedDepth[f]): 

221 coaddedDepth[f] = singleVisitDepth[f] 

222 return coaddedDepth