def source_plot(ax, hdu, j): telescope = telescopes.KeckTelescope() fobos_fratio = 3. fobos_platescale = telescope.platescale*fobos_fratio/telescope.fratio fiber_diameter_limits = numpy.array([48., 252.]) ax.set_xlim(fiber_diameter_limits) ax.set_ylim([0.0, 6.]) ax.minorticks_on() ax.tick_params(which='major', length=8, direction='in') ax.tick_params(which='minor', length=4, direction='in') ax.grid(True, which='major', color='0.9', linestyle=':') for i in range(4): ax.plot(hdu['DIAM'].data[i,j,:]*1000, hdu['TOTAL'].data[i,j,:], color='C{0}'.format(i), linestyle='-', zorder=2) ax.plot(hdu['DIAM'].data[i,j,:]*1000, hdu['SKYSUB'].data[i,j,:], color='C{0}'.format(i), linestyle='--', zorder=2) m = numpy.array([numpy.argmax(hdu['TOTAL'].data[i,j,:]), numpy.argmax(hdu['SKYSUB'].data[i,j,:])]) maxsnr = numpy.array([hdu['TOTAL'].data[i,j,m[0]], hdu['SKYSUB'].data[i,j,m[1]]]) ax.scatter(hdu['DIAM'].data[i,j,m]*1000, maxsnr, marker='.', color='C{0}'.format(i), lw=0, s=100, zorder=3) ax2 = ax.twiny() ax2.minorticks_on() ax2.tick_params(which='major', length=8, direction='in') ax2.tick_params(which='minor', length=4, direction='in') ax2.set_xlim(fiber_diameter_limits/fobos_platescale/1000) return ax, ax2
def test(mag, seeing, on_sky_fiber_diameter, sersic=None, exptime=1.): telescope = telescopes.KeckTelescope() detector_noise_ratio = 0. #1/3. re, sersicn = (None,None) if sersic is None else sersic size = 5.0 if re is None else max(5.0, 8*re) sampling = 0.05 if re is None else min(0.05, re/20) # arcsec/pixel return onsky_aperture_snr(telescope, mag, seeing, on_sky_fiber_diameter, detector_noise_ratio=detector_noise_ratio, sampling=sampling, size=size, re=re, sersicn=sersicn, exptime=exptime, quiet=True)
def fiddles_data_table(seeing, exptime, airmass, dmag_sky, data_file): mag = numpy.linspace(15, 22, 15, dtype=float)[::-1] apf_snr_pix = numpy.zeros_like(mag) apf_snr = numpy.zeros_like(mag) apf_saturated = numpy.zeros_like(mag, dtype=bool) keck_snr_pix = numpy.zeros_like(mag) keck_snr = numpy.zeros_like(mag) keck_saturated = numpy.zeros_like(mag, dtype=bool) keck = telescopes.KeckTelescope() apf = telescopes.APFTelescope() for i in range(mag.size): apf_snr_pix[i], apf_snr[i], apf_saturated[i], sky_mag \ = fiddles_snr(mag[i], apf, seeing, exptime, airmass=airmass, dmag_sky=dmag_sky) keck_snr_pix[i], keck_snr[i], keck_saturated[i], sky_mag \ = fiddles_snr(mag[i], keck, seeing, exptime, airmass=airmass, dmag_sky=dmag_sky) numpy.savetxt( data_file, numpy.transpose([ mag, apf_snr_pix, apf_snr, apf_saturated.astype(int), keck_snr_pix, keck_snr, keck_saturated.astype(int) ]), fmt=['%5.1f', '%14.7e', '%14.7e', '%2d', '%14.7e', '%14.7e', '%2d'], header='Sky surface brightness: {0:.2f}'.format(sky_mag) + 'Exposure time: {0:.1f}\n'.format(exptime) + 'Airmass: {0:.1f}\n'.format(airmass) + 'Seeing: {0:.1f}\n'.format(seeing) + '{0:>3s} {1:^32} {2:^32}\n'.format('', 'APF', 'Keck') + '{0:>3s} {1:^32} {2:^32}\n'.format('', '-' * 32, '-' * 32) + '{0:>3s} {1:>14s} {2:>14s} {3:>2s} {4:>14s} {5:>14s} {6:>2s}'.format( 'Mg', 'SNRp', 'SNRt', 'S', 'SNRp', 'SNRt', 'S'))
def fiddles_realization_plot(plot_file=None): telescope = telescopes.KeckTelescope() on, off = fiddles_realization(19., telescope, 0.8, 60, airmass=2., dmag_sky=-3) vmin = -100 vmax = 3500 font = {'size': 10} rc('font', **font) w, h = pyplot.figaspect(1) fig = pyplot.figure(figsize=(1.5 * w, 1.5 * h)) ax = fig.add_axes([0.07, 0.35, 0.3, 0.3]) cax = fig.add_axes([0.07, 0.72, 0.2, 0.01]) ax.minorticks_on() ax.tick_params(which='major', length=6, direction='in', top=True, right=True) ax.tick_params(which='minor', length=3, direction='in', top=True, right=True) # ax.grid(True, which='major', color='0.9', zorder=0, linestyle='-') im = ax.imshow(on, origin='lower', interpolation='nearest', vmin=vmin, vmax=vmax) pyplot.colorbar(im, cax=cax, orientation='horizontal') cax.text(1.1, 0.5, r'Counts (e$-$)', ha='left', va='center', transform=cax.transAxes) ax.text(-0.15, 0.5, r'Y (pixels)', ha='center', va='center', rotation='vertical', transform=ax.transAxes) ax.text(0.5, -0.14, r'X (pixels)', ha='center', va='center', transform=ax.transAxes) ax.text(0.5, 1.05, r'On Source', ha='center', va='center', transform=ax.transAxes) ax = fig.add_axes([0.37, 0.35, 0.3, 0.3]) ax.minorticks_on() ax.tick_params(which='major', length=6, direction='in', top=True, right=True) ax.tick_params(which='minor', length=3, direction='in', top=True, right=True) # ax.grid(True, which='major', color='0.9', zorder=0, linestyle='-') ax.yaxis.set_major_formatter(ticker.NullFormatter()) ax.imshow(off, origin='lower', interpolation='nearest', vmin=vmin, vmax=vmax) ax.text(0.5, -0.14, r'X (pixels)', ha='center', va='center', transform=ax.transAxes) ax.text(0.5, 1.05, r'Off Source', ha='center', va='center', transform=ax.transAxes) ax = fig.add_axes([0.67, 0.35, 0.3, 0.3]) ax.minorticks_on() ax.tick_params(which='major', length=6, direction='in', top=True, right=True) ax.tick_params(which='minor', length=3, direction='in', top=True, right=True) # ax.grid(True, which='major', color='0.9', zorder=0, linestyle='-') ax.yaxis.set_major_formatter(ticker.NullFormatter()) ax.imshow(on - off, origin='lower', interpolation='nearest', vmin=vmin, vmax=vmax) ax.text(0.5, -0.14, r'X (pixels)', ha='center', va='center', transform=ax.transAxes) ax.text(0.5, 1.05, r'On-Off', ha='center', va='center', transform=ax.transAxes) if plot_file is None: pyplot.show() else: fig.canvas.print_figure(plot_file, bbox_inches='tight') fig.clear() pyplot.close(fig)
def main(args): t = time.perf_counter() # Constants: resolution = 3500. # lambda/dlambda fiber_diameter = 0.8 # Arcsec rn = 2. # Detector readnoise (e-) dark = 0.0 # Detector dark-current (e-/s) # Temporary numbers that assume a given spectrograph PSF and LSF. # Assume 3 pixels per spectral and spatial FWHM. spatial_fwhm = 3.0 spectral_fwhm = 3.0 mags = numpy.arange(args.mag[0], args.mag[1] + args.mag[2], args.mag[2]) times = numpy.arange(args.time[0], args.time[1] + args.time[2], args.time[2]) # Get source spectrum in 1e-17 erg/s/cm^2/angstrom. Currently, the # source spectrum is assumed to be # - normalized by the total integral of the source flux # - independent of position within the source wave = get_wavelength_vector(args.wavelengths[0], args.wavelengths[1], args.wavelengths[2]) spec = get_spectrum(wave, mags[0], resolution=resolution) # Get the source distribution. If the source is uniform, onsky is None. onsky = None # Get the sky spectrum sky_spectrum = spectrum.MaunakeaSkySpectrum() # Overplot the source and sky spectrum # ax = spec.plot() # ax = sky_spectrum.plot(ax=ax, show=True) # Get the atmospheric throughput atmospheric_throughput = efficiency.AtmosphericThroughput( airmass=args.airmass) # Set the telescope. Defines the aperture area and throughput # (nominally 3 aluminum reflections for Keck) telescope = telescopes.KeckTelescope() # Define the observing aperture; fiber diameter is in arcseconds, # center is 0,0 to put the fiber on the target center. "resolution" # sets the resolution of the fiber rendering; it has nothing to do # with spatial or spectral resolution of the instrument fiber = aperture.FiberAperture(0, 0, fiber_diameter, resolution=100) # Get the spectrograph throughput (circa June 2018; TODO: needs to # be updated). Includes fibers + foreoptics + FRD + spectrograph + # detector QE (not sure about ADC). Because this is the total # throughput, define a generic efficiency object. thru_db = numpy.genfromtxt( os.path.join(os.environ['ENYO_DIR'], 'data/efficiency', 'fobos_throughput.db')) spectrograph_throughput = efficiency.Efficiency(thru_db[:, 1], wave=thru_db[:, 0]) # System efficiency combines the spectrograph and the telescope system_throughput = efficiency.SystemThroughput( wave=spec.wave, spectrograph=spectrograph_throughput, telescope=telescope.throughput) # Instantiate the detector; really just a container for the rn and # dark current for now. QE is included in fobos_throughput.db file, # so I set it to 1 here. det = detector.Detector(rn=rn, dark=dark, qe=1.0) # Extraction: makes simple assumptions about the detector PSF for # each fiber spectrum and mimics a "perfect" extraction, including # an assumption of no cross-talk between fibers. Ignore the # "spectral extraction". extraction = extract.Extraction(det, spatial_fwhm=spatial_fwhm, spatial_width=1.5 * spatial_fwhm, spectral_fwhm=spectral_fwhm, spectral_width=spectral_fwhm) snr_label = 'S/N per {0}'.format('R element' if args.snr_units == 'resolution' else args.snr_units) # SNR g = efficiency.FilterResponse() r = efficiency.FilterResponse(band='r') snr_g = numpy.empty((mags.size, times.size), dtype=float) snr_r = numpy.empty((mags.size, times.size), dtype=float) for i in range(mags.size): spec.rescale_magnitude(mags[i], band=g) for j in range(times.size): print('{0}/{1} ; {2}/{3}'.format(i + 1, mags.size, j + 1, times.size), end='\r') # Perform the observation obs = Observation(telescope, sky_spectrum, fiber, times[j], det, system_throughput=system_throughput, atmospheric_throughput=atmospheric_throughput, airmass=args.airmass, onsky_source_distribution=onsky, source_spectrum=spec, extraction=extraction, snr_units=args.snr_units) # Construct the S/N spectrum snr_spec = obs.snr(sky_sub=True) snr_g[i, j] = numpy.sum(g(snr_spec.wave) * snr_spec.flux) / numpy.sum( g(snr_spec.wave)) snr_r[i, j] = numpy.sum(r(snr_spec.wave) * snr_spec.flux) / numpy.sum( r(snr_spec.wave)) print('{0}/{1} ; {2}/{3}'.format(i + 1, mags.size, j + 1, times.size)) extent = [ args.time[0] - args.time[2] / 2, args.time[1] + args.time[2] / 2, args.mag[0] - args.mag[2] / 2, args.mag[1] + args.mag[2] / 2 ] w, h = pyplot.figaspect(1) fig = pyplot.figure(figsize=(1.5 * w, 1.5 * h)) ax = fig.add_axes([0.15, 0.2, 0.7, 0.7]) img = ax.imshow(snr_g, origin='lower', interpolation='nearest', extent=extent, aspect='auto', norm=colors.LogNorm(vmin=snr_g.min(), vmax=snr_g.max())) cax = fig.add_axes([0.86, 0.2, 0.02, 0.7]) pyplot.colorbar(img, cax=cax) cax.text(4, 0.5, snr_label, ha='center', va='center', transform=cax.transAxes, rotation='vertical') ax.text(0.5, -0.08, 'Exposure Time [s]', ha='center', va='center', transform=ax.transAxes) ax.text(-0.12, 0.5, r'Surface Brightness [AB mag/arcsec$^2$]', ha='center', va='center', transform=ax.transAxes, rotation='vertical') ax.text(0.5, 1.03, r'$g$-band S/N', ha='center', va='center', transform=ax.transAxes, fontsize=12) pyplot.show()
def main(args): if args.sky_err < 0 or args.sky_err > 1: raise ValueError( '--sky_err option must provide a value between 0 and 1.') t = time.perf_counter() # Constants: resolution = 3500. # lambda/dlambda fiber_diameter = 0.8 # Arcsec rn = 2. # Detector readnoise (e-) dark = 0.0 # Detector dark-current (e-/s) # Temporary numbers that assume a given spectrograph PSF and LSF. # Assume 3 pixels per spectral and spatial FWHM. spatial_fwhm = args.spot_fwhm spectral_fwhm = args.spot_fwhm # Get source spectrum in 1e-17 erg/s/cm^2/angstrom. Currently, the # source spectrum is assumed to be # - normalized by the total integral of the source flux # - independent of position within the source dw = 1 / spectral_fwhm / resolution / numpy.log(10) wavelengths = [3100, 10000, dw] wave = get_wavelength_vector(wavelengths[0], wavelengths[1], wavelengths[2]) emline_db = None if args.emline is None else read_emission_line_database( args.emline) spec = get_spectrum(wave, args.mag, mag_band=args.mag_band, mag_system=args.mag_system, spec_file=args.spec_file, spec_wave=args.spec_wave, spec_wave_units=args.spec_wave_units, spec_flux=args.spec_flux, spec_flux_units=args.spec_flux_units, spec_res_indx=args.spec_res_indx, spec_res_value=args.spec_res_value, spec_table=args.spec_table, emline_db=emline_db, redshift=args.redshift, resolution=resolution) # Get the source distribution. If the source is uniform, onsky is None. onsky = get_source_distribution(args.fwhm, args.uniform, args.sersic) # Show the rendered source # if onsky is not None: # pyplot.imshow(onsky.data, origin='lower', interpolation='nearest') # pyplot.show() # Get the sky spectrum sky_spectrum = get_sky_spectrum(args.sky_mag, mag_band=args.sky_mag_band, mag_system=args.sky_mag_system) # Overplot the source and sky spectrum # ax = spec.plot() # ax = sky_spectrum.plot(ax=ax, show=True) # Get the atmospheric throughput atmospheric_throughput = efficiency.AtmosphericThroughput( airmass=args.airmass) # Set the telescope. Defines the aperture area and throughput # (nominally 3 aluminum reflections for Keck) telescope = telescopes.KeckTelescope() # Define the observing aperture; fiber diameter is in arcseconds, # center is 0,0 to put the fiber on the target center. "resolution" # sets the resolution of the fiber rendering; it has nothing to do # with spatial or spectral resolution of the instrument fiber = aperture.FiberAperture(0, 0, fiber_diameter, resolution=100) # Get the spectrograph throughput (circa June 2018; TODO: needs to # be updated). Includes fibers + foreoptics + FRD + spectrograph + # detector QE (not sure about ADC). Because this is the total # throughput, define a generic efficiency object. thru_db = numpy.genfromtxt( os.path.join(os.environ['ENYO_DIR'], 'data/efficiency', 'fobos_throughput.db')) spectrograph_throughput = efficiency.Efficiency(thru_db[:, 1], wave=thru_db[:, 0]) # System efficiency combines the spectrograph and the telescope system_throughput = efficiency.SystemThroughput( wave=spec.wave, spectrograph=spectrograph_throughput, telescope=telescope.throughput) # Instantiate the detector; really just a container for the rn and # dark current for now. QE is included in fobos_throughput.db file, # so I set it to 1 here. det = detector.Detector(rn=rn, dark=dark, qe=1.0) # Extraction: makes simple assumptions about the detector PSF for # each fiber spectrum and mimics a "perfect" extraction, including # an assumption of no cross-talk between fibers. Ignore the # "spectral extraction". extraction = extract.Extraction(det, spatial_fwhm=spatial_fwhm, spatial_width=1.5 * spatial_fwhm, spectral_fwhm=spectral_fwhm, spectral_width=spectral_fwhm) # Perform the observation obs = Observation(telescope, sky_spectrum, fiber, args.time, det, system_throughput=system_throughput, atmospheric_throughput=atmospheric_throughput, airmass=args.airmass, onsky_source_distribution=onsky, source_spectrum=spec, extraction=extraction, snr_units=args.snr_units) # Construct the S/N spectrum snr = obs.snr(sky_sub=True, sky_err=args.sky_err) if args.ipython: embed() snr_label = 'S/N per {0}'.format('R element' if args.snr_units == 'resolution' else args.snr_units) if args.plot: w, h = pyplot.figaspect(1) fig = pyplot.figure(figsize=(1.5 * w, 1.5 * h)) ax = fig.add_axes([0.1, 0.5, 0.8, 0.4]) ax.set_xlim([wave[0], wave[-1]]) ax.minorticks_on() ax.tick_params(which='major', length=8, direction='in', top=True, right=True) ax.tick_params(which='minor', length=4, direction='in', top=True, right=True) ax.grid(True, which='major', color='0.8', zorder=0, linestyle='-') ax.xaxis.set_major_formatter(ticker.NullFormatter()) ax.set_yscale('log') ax = spec.plot(ax=ax, label='Object') ax = sky_spectrum.plot(ax=ax, label='Sky') ax.legend() ax.text(-0.1, 0.5, r'Flux [10$^{-17}$ erg/s/cm$^2$/${\rm \AA}$]', ha='center', va='center', transform=ax.transAxes, rotation='vertical') ax = fig.add_axes([0.1, 0.1, 0.8, 0.4]) ax.set_xlim([wave[0], wave[-1]]) ax.minorticks_on() ax.tick_params(which='major', length=8, direction='in', top=True, right=True) ax.tick_params(which='minor', length=4, direction='in', top=True, right=True) ax.grid(True, which='major', color='0.8', zorder=0, linestyle='-') ax = snr.plot(ax=ax) ax.text(0.5, -0.1, r'Wavelength [${\rm \AA}$]', ha='center', va='center', transform=ax.transAxes) ax.text(-0.1, 0.5, snr_label, ha='center', va='center', transform=ax.transAxes, rotation='vertical') pyplot.show() # Report g = efficiency.FilterResponse(band='g') r = efficiency.FilterResponse(band='r') iband = efficiency.FilterResponse(band='i') print('-' * 70) print('{0:^70}'.format('FOBOS S/N Calculation (v0.2)')) print('-' * 70) print('Compute time: {0} seconds'.format(time.perf_counter() - t)) print('Object g- and r-band AB magnitude: {0:.1f} {1:.1f}'.format( spec.magnitude(band=g), spec.magnitude(band=r))) print('Sky g- and r-band AB surface brightness: {0:.1f} {1:.1f}'.format( sky_spectrum.magnitude(band=g), sky_spectrum.magnitude(band=r))) print('Exposure time: {0:.1f} (s)'.format(args.time)) if not args.uniform: print('Aperture Loss: {0:.1f}%'.format( (1 - obs.aperture_factor) * 100)) print('Extraction Loss: {0:.1f}%'.format( (1 - obs.extraction.spatial_efficiency) * 100)) print('Median {0}: {1:.1f}'.format(snr_label, numpy.median(snr.flux))) print('g-band weighted mean {0} {1:.1f}'.format( snr_label, numpy.sum(g(snr.wave) * snr.flux) / numpy.sum(g(snr.wave)))) print('r-band weighted mean {0} {1:.1f}'.format( snr_label, numpy.sum(r(snr.wave) * snr.flux) / numpy.sum(r(snr.wave)))) print('i-band weighted mean {0} {1:.1f}'.format( snr_label, numpy.sum(iband(snr.wave) * snr.flux) / numpy.sum(iband(snr.wave))))
def build_data(mag, ofile): # dmag_sky = None # Dark time # # dmag_sky = -3.0 # Bright time... seeing = [0.4, 0.6, 0.8, 1.0] sersic = [None, (0.1,1), (0.1,4), (0.2,1), (0.2,4), (0.4,1), (0.4,4), (0.8,1), (0.8,4)] # seeing = [0.4, 0.6] # sersic = [None] telescope = telescopes.KeckTelescope() dmag_sky = None fobos_fratio = 3. detector_noise_ratio = 0. #1/3. fiber_diameter = numpy.linspace(0.05, 0.25, 41) fobos_platescale = telescope.platescale*fobos_fratio/telescope.fratio outshape = (len(seeing), len(sersic), len(fiber_diameter)) atmseeing = numpy.zeros(outshape, dtype=float) profile = numpy.zeros(outshape, dtype=int) fibdiam = numpy.zeros(outshape, dtype=float) total_snr = numpy.zeros(outshape, dtype=float) skysub_snr = numpy.zeros(outshape, dtype=float) for i in range(len(seeing)): atmseeing[i,:,:] = seeing[i] for j in range(len(sersic)): print('Seeing {0}/{1}'.format(i+1, len(seeing))) print('Profile {0}/{1}'.format(j+1, len(sersic))) profile[i,j,:] = j if sersic[j] is None: re = None sersicn = None else: re, sersicn = sersic[j] size = 5.0 if re is None else max(5.0, 8*re) sampling = 0.05 if re is None else min(0.05, re/20) # arcsec/pixel for k in range(len(fiber_diameter)): fibdiam[i,j,k] = fiber_diameter[k] print('Calculating {0}/{1}'.format(k+1, fiber_diameter.size), end='\r') total_snr[i,j,k], skysub_snr[i,j,k] = \ aperture_snr(telescope, mag, seeing[i], fiber_diameter[k], dmag_sky=dmag_sky, fobos_fratio=fobos_fratio, detector_noise_ratio=detector_noise_ratio, sampling=sampling, size=size, re=re, sersicn=sersicn, quiet=True) print('Calculating {0}/{0}'.format(fiber_diameter.size)) fits.HDUList([ fits.PrimaryHDU(), fits.ImageHDU(data=atmseeing, name='SEEING'), fits.ImageHDU(data=profile, name='PROF'), fits.ImageHDU(data=fibdiam, name='DIAM'), fits.ImageHDU(data=total_snr, name='TOTAL'), fits.ImageHDU(data=skysub_snr, name='SKYSUB') ]).writeto(ofile, overwrite=True) return i = numpy.argmax(total_snr) print('Max total: {0:.3f} {1:.3f}'.format(fiber_diameter[i], total_snr[i])) i = numpy.argmax(skysub_snr) print('Max skysub: {0:.3f} {1:.3f}'.format(fiber_diameter[i], skysub_snr[i])) print('Calculating {0}/{0}'.format(fiber_diameter.size)) pyplot.plot(fiber_diameter*1000, total_snr) pyplot.plot(fiber_diameter*1000, skysub_snr) pyplot.xlabel('Fiber Diameter (micron)') pyplot.ylabel(r'S/N [(photons/s) / $\sqrt{{\rm photons}^2/{\rm s}^2}$]') pyplot.show()