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# LSST Data Management System 

3# Copyright 2008, 2009, 2010, 2011, 2012 LSST Corporation. 

4#  

5# This product includes software developed by the 

6# LSST Project (http://www.lsst.org/). 

7# 

8# This program is free software: you can redistribute it and/or modify 

9# it under the terms of the GNU General Public License as published by 

10# the Free Software Foundation, either version 3 of the License, or 

11# (at your option) any later version. 

12#  

13# This program is distributed in the hope that it will be useful, 

14# but WITHOUT ANY WARRANTY; without even the implied warranty of 

15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

16# GNU General Public License for more details. 

17#  

18# You should have received a copy of the LSST License Statement and  

19# the GNU General Public License along with this program. If not,  

20# see <http://www.lsstcorp.org/LegalNotices/>. 

21# 

22 

23""" 

24 Questions or comments, email : ljones.uw@gmail.com 

25 

26 

27 The point of this class is mostly for convenience when dealing with 

28 sets of Seds and Bandpasses. Often this convenience is needed when 

29 dealing with these sets from the python interpreter (when figuring out 

30 if a group of SEDS looks appropriate, etc.) 

31  

32 So, a lot of these functions actually deal with plotting.  

33 Still, particularly in SedSet.py you may find the methods to calculate 

34 magnitudes or colors of a large group of seds  

35 (with a set of Bandpasses, defined in this class) useful. 

36  

37 Many of the functions defined here are useful for testing the set 

38 of LSST filters (i.e. do they meet the filter leak requirements?) 

39 or plotting the filters (i.e. plotFilters).  

40""" 

41from __future__ import print_function 

42 

43 

44from builtins import zip 

45from builtins import range 

46from builtins import object 

47import os 

48import copy 

49import numpy as np 

50import matplotlib.pyplot as plt 

51from .Bandpass import Bandpass 

52from .Sed import Sed 

53 

54# airmass of standard atmosphere 

55_stdX = 1.2 

56 

57# wavelength range parameters for calculations. 

58WAVELEN_MIN = 300 # minimum wavelength for transmission/source (nm) 

59WAVELEN_MAX = 1200 # maximum wavelength for transmission/source (nm) 

60WAVELEN_STEP = 0.1 # step size in wavelength grid (nm) 

61 

62# figure format to save output figures, if desired. (can choose 'png' or 'eps' or 'pdf' or a few others).  

63figformat = 'png' 

64 

65class BandpassSet(object): 

66 """ Set up a dictionary of a set of bandpasses (multi-filters). 

67 Run various engineering tests or visualizations.""" 

68 

69 def __init__(self): 

70 """Initialize the class but don't do anything yet.""" 

71 return 

72 

73 def setBandpassSet(self, bpDict, bpDictlist=('u', 'g', 'r', 'i', 'z','y'), verbose=True): 

74 """Simply set throughputs from a pre-made dictionary.""" 

75 if len(bpDictlist) != len(list(bpDict.keys())): 

76 bpDictList = list(bpDict.keys()) 

77 self.bandpass = copy.deepcopy(bpDict) 

78 self.filterlist = copy.deepcopy(bpDictlist) 

79 return 

80 

81 def setThroughputs_SingleFiles(self, filterlist=('u', 'g', 'r', 'i', 'z', 'y'), 

82 rootdir="./", rootname="total_", rootsuffix=".dat", verbose=True): 

83 """Read bandpass set with filters in filterlist, from directory rootdir with base name rootname.""" 

84 # Set up dictionary to hold bandpass information. 

85 bandpass = {} 

86 # Loop through filters:  

87 for f in filterlist: 

88 # Build full filename. 

89 filename = os.path.join(rootdir, rootname+f+rootsuffix) 

90 # read filter throughput and set up Sb/Phi and zeropoint 

91 if verbose: 

92 print("Reading throughput file %s" %(filename)) 

93 # Initialize bandpass object. 

94 bandpass[f] = Bandpass() 

95 # Read the throughput curve, sampling onto grid of wavelen min/max/step. 

96 bandpass[f].readThroughput(filename, wavelen_min=WAVELEN_MIN, 

97 wavelen_max=WAVELEN_MAX, 

98 wavelen_step=WAVELEN_STEP) 

99 # Calculate phi as well.  

100 bandpass[f].sbTophi() 

101 # Set data in self. 

102 self.bandpass = bandpass 

103 self.filterlist = filterlist 

104 return 

105 

106 def setThroughputs_ComponentFiles(self, filterlist=('u', 'g', 'r', 'i', 'z', 'y'), 

107 all_filter_complist = ('detector.dat', 'lens1.dat', 'lens2.dat', 

108 'lens3.dat', 'm1.dat', 'm2.dat', 'm3.dat', 

109 'atmos_std.dat'), 

110 rootdir = "./", verbose=True): 

111 """Read and build bandpass set from all_filter_complist, using data from directory rootdir. 

112 Note that with this method, every bandpass will be the same. The point is that then you can 

113 use this method, plus set up a different BandpassSet with values that are different for each filter 

114 and then multiply the two together using multiplyBandpassSets.""" 

115 # Set up dictionary to hold final bandpass information. 

116 bandpass = {} 

117 # Loop through filters. 

118 # Set up full filenames in a list containing all elements of final throughput curve.  

119 complist = [] 

120 # Join all 'all-filter' items. 

121 for cp in all_filter_complist: 

122 complist.append(os.path.join(rootdir, cp)) 

123 for f in filterlist: 

124 if verbose: 

125 print("Reading throughput curves ", complist, " for filter ", f) 

126 # Initialize bandpass object. 

127 bandpass[f] = Bandpass() 

128 bandpass[f].readThroughputList(complist, wavelen_min=WAVELEN_MIN, 

129 wavelen_max=WAVELEN_MAX, wavelen_step=WAVELEN_STEP) 

130 bandpass[f].sbTophi() 

131 self.bandpass = bandpass 

132 self.filterlist = filterlist 

133 return 

134 

135 def multiplyBandpassSets(self, otherBpSet): 

136 """Multiply two bandpass sets together, filter by filter. Filterlists must match! 

137 Returns a new bandpassSet object.""" 

138 if self.filterlist != otherBpSet.filterlist: 

139 raise Exception("The bandpassSet filter lists must match.") 

140 # Set up dictionary to hold new bandpass objects.  

141 newBpDict = {} 

142 for f in self.filterlist: 

143 wavelen, sb = self.bandpass[f].multiplyThroughputs(otherBpSet.bandpass[f].wavelen, 

144 otherBpSet.bandpass[f].sb) 

145 newBpDict[f] = Bandpass(wavelen=wavelen, sb=sb) 

146 newBpSet = BandpassSet() 

147 newBpSet.setBandpassSet(newBpDict, self.filterlist) 

148 return newBpSet 

149 

150 def writePhis(self, filename): 

151 """Write all phi values and wavelength to stdout""" 

152 # This is useful for getting a data file with only phi's, as requested by some science collaborations. 

153 file = open(filename, "w") 

154 # Print header. 

155 headerline = "#Wavelen(nm) " 

156 for filter in self.filterlist: 

157 headerline = headerline + " phi_" + filter 

158 print(headerline, file=file) 

159 # print data 

160 for i in range(0, len(self.bandpass[self.filterlist[0]].wavelen), 1): 

161 outline = "%.2f " %(self.bandpass[self.filterlist[0]].wavelen[i]) 

162 for f in self.filterlist: 

163 outline = outline + " %.6g " %(self.bandpass[f].phi[i]) 

164 print(outline, file=file) 

165 file.close() 

166 return 

167 

168 def writePhotozThroughputs(self, filename): 

169 """Write all throughputs in format AndyC needs for photoz""" 

170 file = open(filename,"w") 

171 for i,filter in enumerate(self.filterlist): 

172 file.write("%d NAME %d\n"%(len(filter.wavelen),i)) 

173 j=0 

174 for lam,thru in zip(filter.wavelen,filter.sb): 

175 file.write("%d %g %g\n"%(j,10.*lam,thru)) 

176 j=j+1 

177 file.close() 

178 return 

179 

180 def calcFilterEffWave(self, verbose=True): 

181 """Calculate the effective wavelengths for all filters.""" 

182 # Set up dictionaries for effective wavelengths, as calculated for Transmission (sb) and Phi (phi). 

183 effsb = {} 

184 effphi = {} 

185 # Calculate values for each filter.  

186 for f in self.filterlist: 

187 effphi[f], effsb[f] = self.bandpass[f].calcEffWavelen() 

188 self.effsb = effsb 

189 self.effphi = effphi 

190 if verbose: 

191 print("Filter Eff_Sb Eff_phi") 

192 for f in self.filterlist: 

193 print(" %s %.3f %.3f" %(f, self.effsb[f], effphi[f])) 

194 return 

195 

196 def calcZeroPoints(self, gain=1.0, verbose=True): 

197 """Calculate the theoretical zeropoints for the bandpass, in AB magnitudes.""" 

198 exptime = 15 # Default exposure time. 

199 effarea = np.pi*(6.5*100/2.0)**2 # Default effective area of primary mirror.  

200 zpt = {} 

201 print("Filter Zeropoint") 

202 for f in self.filterlist: 

203 zpt[f] = self.bandpass[f].calcZP_t(expTime=exptime, effarea=effarea, gain=gain) 

204 print(" %s %.3f" %(f, zpt[f])) 

205 return 

206 

207 def calcFilterEdges(self, drop_peak=0.1, drop_percent=50, verbose=True): 

208 """Calculate the edges of each filter for Sb, at values of 'drop_*'. 

209  

210 Values for drop_peak are X percent of max throughput, drop_percent is where the 

211 filter throughput drops to an absolute X percent value. """ 

212 bandpass = self.bandpass 

213 filterlist = self.filterlist 

214 try: 

215 effsb = self.effsb 

216 effphi = self.effphi 

217 except AttributeError: 

218 self.calcFilterEffWave() 

219 effsb = self.effsb 

220 effphi = self.effphi 

221 # Set up dictionary for effective wavelengths and X% peak_drop wavelengths. 

222 drop_peak_blue = {} 

223 drop_peak_red = {} 

224 drop_perc_blue = {} 

225 drop_perc_red = {} 

226 maxthruput = {} 

227 # Calculate values for each filter. 

228 for f in filterlist: 

229 # Calculate minimum and maximum wavelengths for bandpass. 

230 minwavelen = bandpass[f].wavelen.min() 

231 maxwavelen = bandpass[f].wavelen.max() 

232 # Set defaults for dropoff points. 

233 drop_peak_blue[f] = maxwavelen 

234 drop_peak_red[f] = minwavelen 

235 drop_perc_blue[f] = maxwavelen 

236 drop_perc_red[f] = minwavelen 

237 # Find out what current wavelength grid is being used. 

238 wavelenstep=(bandpass[f].wavelen[1] - bandpass[f].wavelen[0]) 

239 # Find peak throughput. 

240 maxthruput[f] = bandpass[f].sb.max() 

241 # Calculate the values we're looking for (for the threshold for the 'drop') 

242 d_peak = maxthruput[f] * drop_peak/100.0 

243 d_perc = drop_percent/100.0 # given in %, must translate to fraction. 

244 # Find the nearest spot on the wavelength grid used for filter, for edge lookup. 

245 sbindex = np.where(abs(bandpass[f].wavelen - effsb[f]) < wavelenstep/2.0) 

246 sbindex = sbindex[0][0] 

247 # Now find where Sb drops below 'drop_peak_thruput' of max for the first time. 

248 # Calculate wavelength where dropoff X percent of max level. 

249 # Start at effective wavelength, and walk outwards.  

250 for i in range(sbindex, len(bandpass[f].wavelen)): 

251 if bandpass[f].sb[i] <= d_peak: 

252 drop_peak_red[f] = bandpass[f].wavelen[i] 

253 break 

254 for i in range(sbindex, 0, -1): 

255 if bandpass[f].sb[i] <= d_peak: 

256 drop_peak_blue[f] = bandpass[f].wavelen[i] 

257 break 

258 # Calculate wavelength where dropoff X percent, absolute value 

259 for i in range(sbindex, len(bandpass[f].wavelen)): 

260 if bandpass[f].sb[i] <= d_perc: 

261 drop_perc_red[f] = bandpass[f].wavelen[i] 

262 break 

263 for i in range(sbindex, 0, -1): 

264 if bandpass[f].sb[i] <= d_perc: 

265 drop_perc_blue[f] = bandpass[f].wavelen[i] 

266 break 

267 # Print output to screen. 

268 if verbose: 

269 print("Filter MaxThruput EffWavelen %.3f%s_max(blue) %.3f%s_max(red) %.3f%s_abs(blue) %.3f%s_abs(red)" \ 

270 %(drop_peak, "%", drop_peak, "%", drop_percent, "%", drop_percent, "%")) 

271 for f in self.filterlist: 

272 print("%4s %10.4f %10.4f %12.2f %12.2f %12.2f %12.2f" \ 

273 % (f, maxthruput[f], 

274 effsb[f], 

275 drop_peak_blue[f], 

276 drop_peak_red[f], 

277 drop_perc_blue[f], 

278 drop_perc_red[f])) 

279 # Set values (dictionaries keyed by filterlist). 

280 self.drop_peak_red = drop_peak_red 

281 self.drop_peak_blue = drop_peak_blue 

282 self.drop_perc_red = drop_perc_red 

283 self.drop_perc_blue = drop_perc_blue 

284 return 

285 

286 def calcFilterLeaks(self, ten_nm_limit=0.01, out_of_band_limit=0.05, filter_edges=0.1, 

287 extra_title=None, makeplot=True, savefig=False, figroot = "bandpass"): 

288 """ Calculate throughput leaks beyond location where bandpass drops to filter_edges (%) of max throughput. 

289  

290  

291 According to SRD these leaks must be below 0.01% of peak value in any 10nm interval, 

292 and less than 0.05% of total transmission over all wavelengths beyond where thruput<0.1% of peak. 

293 Assumes wavelength is in nanometers! (because of nm requirement). Uses ten_nm_limit and out_of_band_limit 

294 to set specs. Note that the values given here should be in PERCENT (not fractions).  

295 Generates plots for each filter, as well as calculation of fleaks. """ 

296 # Go through each filter, calculate filter leaks. 

297 filterlist = self.filterlist 

298 bandpass = self.bandpass 

299 # Make sure effective wavelengths defined.  

300 self.calcFilterEffWave(verbose=False) 

301 effsb = self.effsb 

302 # Look for the new FWHM definition for the 10nm filter leak definition 

303 if filter_edges == "FWHM": 

304 self.calcFilterEffWave(verbose=False) 

305 self.calcFilterEdges(drop_percent=0.50, verbose=False) 

306 # Calculate FWHM values.  

307 fwhm = {} 

308 for f in filterlist: 

309 fwhm[f] = self.drop_peak_red[f] - self.drop_peak_blue[f] 

310 # Adjust 'drop' edges to account for being FWHM from center. 

311 for f in filterlist: 

312 self.drop_peak_red[f] = self.effsb[f] + fwhm[f] 

313 self.drop_peak_blue[f] = self.effsb[f] - fwhm[f] 

314 # Otherwise, traditional % definition. 

315 else: 

316 self.calcFilterEdges(drop_peak=filter_edges, verbose=False) 

317 drop_peak_red = self.drop_peak_red 

318 drop_peak_blue = self.drop_peak_blue 

319 # Set up plot colors. 

320 colors = ('m', 'b', 'g', 'y', 'r', 'k', 'c') 

321 colorindex = 0 

322 for f in filterlist: 

323 print("=====") 

324 print("Analyzing %s filter" %(f)) 

325 # find wavelength range in use. 

326 minwavelen = bandpass[f].wavelen.min() 

327 maxwavelen = bandpass[f].wavelen.max() 

328 # find out what current wavelength grid is being used 

329 wavelenstep= bandpass[f].wavelen[1] - bandpass[f].wavelen[0] 

330 # find the wavelength in the wavelength grid which is closest to effsb 

331 condition = (abs(bandpass[f].wavelen - effsb[f]) < wavelenstep/2.0) 

332 waveleneffsb = bandpass[f].wavelen[condition] 

333 # calculate peak transmission 

334 peaktrans = bandpass[f].sb.max() 

335 # calculate total transmission withinin proper bandpass 

336 condition = ((bandpass[f].wavelen>drop_peak_blue[f]) & 

337 (bandpass[f].wavelen<drop_peak_red[f])) 

338 temporary = bandpass[f].sb[condition] 

339 totaltrans = temporary.sum() 

340 # calculate total transmission outside drop_peak wavelengths of peak 

341 condition = ((bandpass[f].wavelen>=drop_peak_red[f]) | 

342 (bandpass[f].wavelen<=drop_peak_blue[f])) 

343 temporary = bandpass[f].sb[condition] 

344 sumthruput_outside_bandpass = temporary.sum() 

345 print("Total transmission through filter: %s" %(totaltrans)) 

346 print("Transmission outside of filter edges (drop_peak): %f" %(sumthruput_outside_bandpass)) 

347 # Calculate percentage of out of band transmission to in-band transmission 

348 out_of_band_perc = sumthruput_outside_bandpass / totaltrans * 100.0 

349 print("Ratio of total out-of-band to in-band transmission: %f%s" \ 

350 %(out_of_band_perc, "%")) 

351 infotext = "Out-of-band/in-band transmission %.3f%s" \ 

352 %(out_of_band_perc, '%') 

353 if out_of_band_perc > out_of_band_limit: 

354 print(" Does not meet SRD-This is more than %.4f%s of throughput outside the bandpass %s" \ 

355 %(out_of_band_limit, '%', f)) 

356 else: 

357 print(" Meets SRD - This is less than %.4f%s of total throughput outside bandpass" \ 

358 %(out_of_band_limit, '%')) 

359 # calculate transmission in each 10nm interval. 

360 sb_10nm = np.zeros(len(bandpass[f].sb), dtype='float') 

361 gapsize_10nm = 10.0 # wavelen gap in nm 

362 meet_SRD=True 

363 maxsb_10nm = 0. 

364 maxwavelen_10nm = 0. 

365 # Convert 10nm limit into actual value (and account for %) 

366 ten_nm_limit_value = ten_nm_limit * peaktrans/100.0 

367 for i in range(0, len(sb_10nm), 1): 

368 # calculate 10 nm 'smoothed' transmission 

369 wavelen = bandpass[f].wavelen[i] 

370 condition = ((bandpass[f].wavelen >= wavelen-gapsize_10nm/2.0) & 

371 (bandpass[f].wavelen < wavelen+gapsize_10nm/2.0) & 

372 ((bandpass[f].wavelen <= drop_peak_blue[f]) 

373 | (bandpass[f].wavelen >= drop_peak_red[f]))) 

374 sb_10nm[i] = bandpass[f].sb[condition].mean() 

375 condition = ((bandpass[f].wavelen > drop_peak_blue[f]) & 

376 (bandpass[f].wavelen < drop_peak_red[f])) 

377 sb_10nm[condition] = 0 

378 # now check for violation of SRD 

379 if sb_10nm.max() > ten_nm_limit_value: 

380 meet_SRD = False 

381 maxsb_10nm = sb_10nm.max() 

382 maxwavelen_10nm = bandpass[f].wavelen[np.where(sb_10nm==sb_10nm.max())] 

383 if meet_SRD==False: 

384 print("Does not meet SRD - %s has at least one region not meeting the 10nm SRD filter leak requirement (max is %f%s of peak transmission at %.1f A)" %(f, maxsb_10nm, "%", maxwavelen_10nm)) 

385 else: 

386 print("10nm limit within SRD.") 

387 if makeplot: 

388 # make plot for this filter 

389 plt.figure() 

390 # set colors for filter in plot  

391 color = colors[colorindex] 

392 colorindex = colorindex + 1 

393 if colorindex == len(colors): 

394 colorindex = 0 

395 # Make lines on the plot.  

396 plt.plot(bandpass[f].wavelen, bandpass[f].sb, color=color, linestyle="-") 

397 plt.plot(bandpass[f].wavelen, sb_10nm, 'r-',linewidth=2) 

398 plt.axvline(drop_peak_blue[f], color='b', linestyle=':') 

399 plt.axvline(drop_peak_red[f], color='b', linestyle=':') 

400 plt.axhline(ten_nm_limit_value, color='b', linestyle=':') 

401 legendstring = f + " filter thruput, 10nm average thruput in red\n" 

402 legendstring = legendstring + " Peak throughput is %.1f%s\n" \ 

403 %(peaktrans*100.0, '%') 

404 legendstring = legendstring + " Total throughput (in band) is %.0f%s\n" \ 

405 %(totaltrans*100.0, '%') 

406 legendstring = legendstring + " " + infotext 

407 plt.figtext(0.25, 0.76, legendstring) 

408 plt.xlabel("Wavelength (nm)") 

409 plt.ylabel("Throughput (0-1)") 

410 plt.yscale('log') 

411 if extra_title != None: 

412 titletext = extra_title + " " + f 

413 else: 

414 titletext = f 

415 plt.title(titletext) 

416 plt.ylim(1e-6, 1) 

417 plt.xlim(xmin=300, xmax=1200) 

418 if savefig: 

419 figname = figroot + "_" + f + "_fleak."+ figformat 

420 plt.savefig(figname, format=figformat) 

421 # end of loop through filters 

422 return 

423 

424 def plotFilters(self, rootdir=".", throughput=True, phi=False, atmos=True, 

425 plotdropoffs=False, ploteffsb=True, compare=None, savefig=False, 

426 figroot='bandpass', xlim=(300, 1100), ylimthruput=(0, 1), ylimphi=(0, 0.002), 

427 filter_tags='normal', leg_tag=None, compare_tag=None, title=None, 

428 linestyle='-', linewidth=2, newfig=True): 

429 """ Plot the filter throughputs and phi's, with limits xlim/ylimthruput/ylimphi.  

430  

431 Optionally add comparison (another BandpassSet) throughput and phi curves. 

432 and show lines for % dropoffs ; filter_tags can be side or normal. """ 

433 # check that all self variables are set up if needed 

434 bandpass = self.bandpass 

435 filterlist = self.filterlist 

436 try: 

437 self.effsb 

438 self.effphi 

439 if plotdropoffs: 

440 self.drop_peak_red 

441 self.drop_peak_blue 

442 except AttributeError: 

443 self.calcFilterEffWave(verbose=False) 

444 if plotdropoffs: 

445 self.calcFilterEdges(verbose=False) 

446 effsb = self.effsb 

447 effphi = self.effphi 

448 if plotdropoffs: 

449 drop_peak_red = self.drop_peak_red 

450 drop_peak_blue = self.drop_peak_blue 

451 # read files for atmosphere and optional comparison throughputs 

452 if atmos: 

453 atmosfile = os.path.join(rootdir, 'atmos_std.dat') 

454 atmosphere = Bandpass() 

455 atmosphere.readThroughput(atmosfile) 

456 Xatm=_stdX 

457 # set up colors for plot output 

458 colors = ('k', 'b', 'g', 'y', 'r', 'm', 'burlywood', 'k') 

459 #colors = ('r', 'b', 'r', 'b', 'r', 'b', 'r', 'b') 

460 if (throughput): 

461 if newfig: 

462 plt.figure() 

463 # plot throughputs 

464 colorindex = 0 

465 for f in filterlist: 

466 color = colors[colorindex] 

467 colorindex = colorindex+1 

468 if colorindex == len(colors): 

469 colorindex=0 

470 plt.plot(bandpass[f].wavelen, bandpass[f].sb, 

471 color=color, linestyle=linestyle, linewidth=linewidth) 

472 # add effective wavelengths (optional) 

473 if ploteffsb: 

474 vertline = np.arange(0, 1.2, 0.1) 

475 temp = vertline*0.0 + 1.0 

476 colorindex = 0 

477 for f in filterlist: 

478 color = colors[colorindex] 

479 colorindex = colorindex + 1 

480 if colorindex == len(colors): 

481 colorindex = 0 

482 plt.plot(effsb[f]*temp, vertline, color=color, linestyle='-') 

483 # add dropoff limits if desired (usually only good with reduced x/y limits) (optional) 

484 if (plotdropoffs): 

485 colorindex = 0 

486 for filter in filterlist: 

487 color = colors[colorindex] 

488 colorindex = colorindex+1 

489 if colorindex == len(colors): 

490 colorindex = 0 

491 plt.plot(drop_peak_red[f]*temp, vertline, color=color, linestyle='--') 

492 plt.plot(drop_peak_blue[f]*temp, vertline, color=color, linestyle='--') 

493 # plot atmosphere (optional) 

494 if atmos: 

495 plt.plot(atmosphere.wavelen, atmosphere.sb, 'k:') 

496 # plot comparison throughputs (optional) 

497 if compare!=None: 

498 colorindex = 0 

499 for f in compare.filterlist: 

500 color = colors[colorindex] 

501 colorindex = colorindex + 1 

502 if colorindex == len(colors): 

503 colorindex = 0 

504 plt.plot(compare.bandpass[f].wavelen, compare.bandpass[f].sb, 

505 color=color, linestyle='--') 

506 # add line legend (type of filter curves) 

507 legendtext = "%s = solid" %(leg_tag) 

508 if leg_tag==None: 

509 legendtext = "" 

510 if compare!=None: 

511 if compare_tag!=None: 

512 legendtext= legendtext + "\n%s = dashed" %(compare_tag) 

513 if atmos: 

514 legendtext = legendtext + "\nAirmass %.1f" %(Xatm) 

515 plt.figtext(0.15, 0.8, legendtext) 

516 # add names to filter throughputs 

517 if filter_tags == 'side': 

518 xtags = np.zeros(len(filterlist), dtype=float) 

519 xtags = xtags + 0.15 

520 spacing = (0.8 - 0.1) / len(filterlist) 

521 ytags = np.arange(0.8, 0.1, -1*spacing, dtype=float) 

522 ytags = ytags 

523 else: # 'normal' tagging 

524 xtags = (0.16, 0.27, 0.42, 0.585, 0.68, 0.8, 0.8, 0.8) 

525 ytags = (0.73, 0.73, 0.73, 0.73, 0.73, 0.73, 0.69, 0.65) 

526 index= 0 

527 colorindex = 0 

528 for f in filterlist: 

529 plt.figtext(xtags[index], ytags[index], f, color=colors[colorindex], 

530 va='top', size='x-large') 

531 index = index+1 

532 colorindex = colorindex + 1 

533 if colorindex == len(colors): 

534 colorindex = 0 

535 # set x/y limits 

536 plt.xlim(xmin=xlim[0], xmax=xlim[1]) 

537 plt.ylim(ymin=ylimthruput[0], ymax=ylimthruput[1]) 

538 plt.xlabel("Wavelength (nm)") 

539 plt.ylabel("Throughput (0-1)") 

540 plt.grid() 

541 if title!=None: 

542 plt.title(title) 

543 if savefig: 

544 figname = figroot + "_thruputs." + figformat 

545 plt.savefig(figname, format=figformat) 

546 if (phi): 

547 if newfig: 

548 plt.figure() 

549 # plot LSST 'phi' curves 

550 colorindex = 0 

551 for f in filterlist: 

552 color = colors[colorindex] 

553 colorindex = colorindex+1 

554 if colorindex == len(colors): 

555 colorindex = 0 

556 plt.plot(bandpass[f].wavelen, bandpass[f].phi, color=color, 

557 linestyle=linestyle, linewidth=linewidth) 

558 # add effective wavelengths for main filter set (optional) 

559 if ploteffsb: 

560 vertline = np.arange(0, .1, 0.01) 

561 temp = vertline*0.0 + 1.0 

562 colorindex = 0 

563 for filter in filterlist: 

564 color = colors[colorindex] 

565 colorindex = colorindex + 1 

566 if colorindex == len(colors): 

567 colorindex = 0 

568 plt.plot(effphi[f]*temp, vertline, color=color, linestyle='-') 

569 # plot comparison throughputs (optional) 

570 if compare!=None: 

571 colorindex = 0 

572 for filter in compare.filterlist: 

573 color = colors[colorindex] 

574 colorindex = colorindex + 1 

575 if colorindex == len(colors): 

576 colorindex = 0 

577 plt.plot(compare.bandpass[f].wavelen, compare.bandpass[f].phi, 

578 color=color, linestyle='--') 

579 # add line legend 

580 legendtext = "%s = solid" %(leg_tag) 

581 if leg_tag ==None: 

582 legendtext = " " 

583 if compare!=None: 

584 if compare_tag!=None: 

585 legendtext = legendtext + "\n%s = dashed" %(compare_tag) 

586 plt.figtext(0.15, 0.78, legendtext) 

587 # add name tags to filters 

588 if filter_tags == 'side': 

589 xtags = np.zeros(len(filterlist), dtype=float) 

590 xtags = xtags + 0.15 

591 ytags = np.arange(len(filterlist), 0, -1.0, dtype=float) 

592 ytags = ytags*0.04 + 0.35 

593 else: 

594 xtags = (0.17, 0.27, 0.42, 0.585, 0.677, 0.82, 0.82, 0.82) 

595 ytags = (0.63, 0.63, 0.63, 0.63, 0.63, 0.63, 0.60, 0.57) 

596 index= 0 

597 colorindex = 0 

598 for f in filterlist: 

599 plt.figtext(xtags[index], ytags[index], f, color=colors[colorindex], va='top') 

600 index = index+1 

601 colorindex = colorindex+1 

602 if colorindex==len(colors): 

603 colorindex=0 

604 # set x/y limits 

605 plt.xlim(xmin=xlim[0], xmax=xlim[1]) 

606 plt.ylim(ymin=ylimphi[0], ymax=ylimphi[1]) 

607 plt.xlabel("Wavelength (nm)") 

608 plt.ylabel("Phi") 

609 plt.grid() 

610 if title!=None: 

611 plt.title(title) 

612 if savefig: 

613 figname = figroot + "_phi." + figformat 

614 plt.savefig(figname, format=figformat) 

615 return 

616 

617