def get_sky_spectrum(mag=None, mag_band='g', mag_system='AB'): sky_spec = spectrum.MaunakeaSkySpectrum() if mag is None: return sky_spec broadband = efficiency.FilterResponse(band=mag_band) sky_spec.rescale_magnitude(mag, band=broadband, system=mag_system) return sky_spec
def main(): # Assume slit is perfectly aligned with center of object # TODO: Introduce wavelength dependent seeing? mag = 22. # g-band magnitude seeing = 0.6 # arcsec slitwidth = 0.75 # arcsec slitlength = 5. # arcsec slitrotation = 0. # degrees # Define the slit aperture slit = aperture.SlitAperture(0., 0., slitwidth, slitlength, rotation=slitrotation) # Sample the source at least 5 times per seeing disk FWHM sampling = seeing / 5 # Set the size of the map to be the maximum of 3 seeing disks, or # 1.5 times the size of the slit in the cartesian coordinates. # First, find the width of the slit shape in x and y (including any # rotation). dslitx, dslity = numpy.diff(numpy.asarray(slit.bounds).reshape(2, -1), axis=0).ravel() size = max(seeing * 3, 1.5 * dslitx, 1.5 * dslity) # Use a point source with unity integral (integral of mapped # profile will not necessarily be unity) star = source.OnSkySource(seeing, 1.0, sampling=sampling, size=size) print('Fraction of star flux in mapped image: {0:.3f}'.format( numpy.sum(star.data) * numpy.square(sampling))) slit_img = slit.response(star.x, star.y) print('Slit area: {0:.2f} (arcsec^2)'.format(slit.area)) print('Aperture efficiency: {0:.2f}'.format( numpy.sum(slit_img * star.data) * numpy.square(sampling))) # Use a reference spectrum that's constant; units are 1e-17 erg/s/cm^2/angstrom wave = numpy.linspace(3000., 10000., num=7001) spec = spectrum.ABReferenceSpectrum(wave) g = efficiency.FilterResponse() # Rescale to a specified magnitude spec.rescale_magnitude(mag, band=g) print('Star Magnitude (AB mag): {0:.2f}'.format(spec.magnitude(g))) # Sky Spectrum; units are 1e-17 erg/s/cm^2/angstrom/arcsec^2 sky = spectrum.MaunakeaSkySpectrum() sky_mag = sky.magnitude(g) print('Sky Surface Brightness (AB mag/arcsec^2): {0:.2f}'.format(sky_mag))
def test_mag(): wave = numpy.linspace(3000., 10000., num=7001) spec = spectrum.ABReferenceSpectrum(wave) g = efficiency.FilterResponse() print(spec.magnitude(g)) spec.rescale_magnitude(10, band=g) print(spec.magnitude(g)) # pyplot.plot(spec.wave, spec.flux) # pyplot.plot(spec.wave, spec.flux) # pyplot.yscale('log') # pyplot.show() pyplot.plot(spec.wave, spec.photon_flux()) pyplot.show() print(spec.photon_flux(band=g))
def get_spectrum(wave, mag, emline_db=None, redshift=0.0, resolution=3500): """ """ spec = spectrum.ABReferenceSpectrum(wave, resolution=resolution, log=True) g = efficiency.FilterResponse() spec.rescale_magnitude(mag, band=g) if emline_db is None: return spec spec = spectrum.EmissionLineSpectrum(wave, emline_db['flux'], emline_db['restwave'], emline_db['fwhm'], units=emline_db['fwhmu'], redshift=redshift, resolution=resolution, log=True, continuum=spec.flux) warnings.warn( 'Including emission lines, spectrum g-band magnitude changed ' 'from {0} to {1}.'.format(mag, spec.magnitude(band=g))) return spec
def get_spectrum(wave, mag, mag_band='g', mag_system='AB', spec_file=None, spec_wave=None, spec_wave_units=None, spec_flux=None, spec_flux_units=None, spec_res_indx=None, spec_res_value=None, spec_table=None, emline_db=None, redshift=0.0, resolution=3500): """ """ spec = spectrum.ABReferenceSpectrum(wave, resolution=resolution, log=True) \ if spec_file is None \ else read_spectrum(spec_file, spec_wave, spec_wave_units, spec_flux, spec_flux_units, spec_res_indx, spec_res_value, spec_table, wave, resolution) broadband = efficiency.FilterResponse(band=mag_band) spec.rescale_magnitude(mag, band=broadband, system=mag_system) if emline_db is None: return spec spec = spectrum.EmissionLineSpectrum(wave, emline_db['flux'], emline_db['restwave'], emline_db['fwhm'], units=emline_db['fwhmu'], redshift=redshift, resolution=resolution, log=True, continuum=spec.flux) warnings.warn( 'Including emission lines, spectrum broadband magnitude changed ' 'from {0} to {1}.'.format(mag, spec.magnitude(band=broadband))) return spec
def twod_spec(): mag = 23. # g-band AB magnitude seeing = 0.6 # arcsec wave_ref = 5500 # Reference wavelength in angstrom slitwidth = 0.75 # arcsec slitlength = 5. # arcsec slitrotation = 0. # degrees # wave = numpy.linspace(3100., 10000., num=6901, dtype=float) # star_spec = spectrum.ABReferenceSpectrum(wave) star_spec = spectrum.BlueGalaxySpectrum() star_spec.show() g = efficiency.FilterResponse() star_spec.rescale_magnitude(mag, band=g) sky_spec = spectrum.MaunakeaSkySpectrum() psf = source.OnSkyGaussian(seeing) star_reference_flux = star_spec.interp([wave_ref])[0] star = source.OnSkySource(psf, star_reference_flux) sky_reference_flux = sky_spec.interp([wave_ref])[0] sky = source.OnSkySource(psf, source.OnSkyConstant(sky_reference_flux)) slit = aperture.SlitAperture(0., 0., slitwidth, slitlength, rotation=slitrotation) lowres_wfos = spectrographs.TMTWFOS(setting='lowres') wave_lim = star_spec.wave[[0, -1]] arm = 'red' print('Building off-slit rectilinear spectrum') off_slit_spectrum = lowres_wfos.twod_spectrum(sky_spec, slit, wave_lim=wave_lim, arm=arm, rectilinear=True) # print(numpy.median(off_slit_spectrum)) # pyplot.imshow(off_slit_spectrum, origin='lower', interpolation='nearest', aspect='auto', # vmax=10) # pyplot.show() print('Building on-slit rectilinear spectrum') on_slit_spectrum = lowres_wfos.twod_spectrum(sky_spec, slit, source_distribution=star, source_spectrum=star_spec, wave_lim=wave_lim, arm=arm, rectilinear=True) # pyplot.imshow(on_slit_spectrum, origin='lower', interpolation='nearest', aspect='auto', # vmax=10) # pyplot.show() print('Writing rectilinear spectra to disk.') fits.HDUList([fits.PrimaryHDU(data=off_slit_spectrum) ]).writeto('offslit_test.fits', overwrite=True) fits.HDUList([fits.PrimaryHDU(data=on_slit_spectrum) ]).writeto('onslit_test.fits', overwrite=True) # Field coordinates in arcsec field_coo = numpy.array([-4., 1.2]) * 60. print('Building off-slit projected spectrum.') off_slit_projected_spectrum, spec0, spat0 \ = lowres_wfos.twod_spectrum(sky_spec, slit, wave_lim=wave_lim, arm=arm, field_coo=field_coo) # from IPython import embed # embed() print('Building on-slit projected spectrum.') on_slit_projected_spectrum, spec0, spat0 \ = lowres_wfos.twod_spectrum(sky_spec, slit, source_distribution=star, source_spectrum=star_spec, wave_lim=wave_lim, arm=arm, field_coo=field_coo) # embed() print(on_slit_projected_spectrum.shape) fits.HDUList([fits.PrimaryHDU(data=off_slit_projected_spectrum) ]).writeto('offslit_projected_test.fits', overwrite=True) fits.HDUList([fits.PrimaryHDU(data=on_slit_projected_spectrum) ]).writeto('onslit_projected_test.fits', overwrite=True)
def gal_set_image(): seeing = 0.6 # arcsec wave_ref = 5500 # Reference wavelength in angstrom slitwidth = 0.75 # arcsec slitlength = 5. # arcsec slitrotation = 0. # degrees slit_pos_file = 'slits.db' if not os.path.isfile(slit_pos_file): slit_x = numpy.arange(-4.2 * 60 + slitlength, 4.2 * 60, slitlength + slitlength * 0.2) nslits = slit_x.size slit_y = numpy.random.uniform(size=nslits) * 3 - 1.5 mag = 23 - numpy.random.uniform(size=nslits) * 2 z = 0.1 + numpy.random.uniform(size=nslits) * 0.4 numpy.savetxt(slit_pos_file, numpy.array([slit_x, slit_y, mag, z]).T, header='{0:>7} {1:>7} {2:>5} {3:>7}'.format( 'SLITX', 'SLITY', 'GMAG', 'Z'), fmt=['%9.2f', '%7.2f', '%5.2f', '%7.5f']) else: slit_x, slit_y, mag, z = numpy.genfromtxt(slit_pos_file).T nslits = slit_x.size nspec = 18000 nspat = 10800 image = { 'blue': numpy.zeros((nspec, nspat), dtype=numpy.float32), 'red': numpy.zeros((nspec, nspat), dtype=numpy.float32) } psf = source.OnSkyGaussian(seeing) g = efficiency.FilterResponse() sky_spec = spectrum.MaunakeaSkySpectrum() sky_reference_flux = sky_spec.interp([wave_ref])[0] sky = source.OnSkyConstant(sky_reference_flux) slit = aperture.SlitAperture(0., 0., slitwidth, slitlength, rotation=slitrotation) lowres_wfos = spectrographs.TMTWFOS(setting='lowres') wave_lim = numpy.array([3100, 10000]) gal = source.OnSkySource(psf, source.OnSkySersic(1.0, 0.1, 1, unity_integral=True), sampling=lowres_wfos.arms['blue'].pixelscale) # pyplot.imshow(gal.data, origin='lower', interpolation='nearest') # pyplot.show() for i in range(nslits): gal_spec = spectrum.BlueGalaxySpectrum() gal_spec.redshift(z[i]) gal_spec.rescale_magnitude(mag[i], band=g) field_coo = numpy.array([slit_x[i], slit_y[i]]) for arm in ['blue', 'red']: print('Building on-slit projected spectrum.') slit_spectrum, spec0, spat0 \ = lowres_wfos.twod_spectrum(sky_spec, slit, source_distribution=gal, source_spectrum=gal_spec, wave_lim=wave_lim, arm=arm, field_coo=field_coo) sspec = int(spec0) + nspec // 2 sspat = int(spat0) + nspat // 2 image[arm][sspec:sspec+slit_spectrum.shape[0], sspat:sspat+slit_spectrum.shape[1]] \ += slit_spectrum fits.HDUList([fits.PrimaryHDU(data=image['blue']) ]).writeto('gal_set_blue.fits', overwrite=True) fits.HDUList([fits.PrimaryHDU(data=image['red']) ]).writeto('gal_set_red.fits', overwrite=True)
def spec_eff_plot(plot_file=None): mag = 19. airmass = 2. dmag_sky = -3. # Spectra; units are 1e-17 erg/s/cm^2/angstrom/arcsec^2 wave = numpy.linspace(3000., 10000., num=7001) spec = spectrum.ABReferenceSpectrum(wave) g = efficiency.FilterResponse() spec.rescale_magnitude(mag, band=g) sky = spectrum.MaunakeaSkySpectrum() sky_mag = sky.magnitude(g) if dmag_sky is not None: sky_mag += dmag_sky sky.rescale_magnitude(sky_mag, band=g) # Efficiencies atm = efficiency.AtmosphericThroughput(airmass=airmass) fiber_throughput = efficiency.FiberThroughput() qe_file = os.path.join(os.environ['ENYO_DIR'], 'data', 'efficiency', 'detectors', 'thor_labs_monochrome_qe.db') fiddles_qe = efficiency.Efficiency.from_file(qe_file, wave_units='nm') xlim = [3700, 5600] 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.1, 0.5, 0.85, 0.2]) 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.xaxis.set_major_formatter(ticker.NullFormatter()) ax.set_xlim(xlim) ax.set_ylim([3, 35]) ax.plot(spec.wave, spec.flux, color='k', zorder=3) ax.plot(sky.wave, sky.flux, color='C3', zorder=3, lw=0.8) ax.text(-0.08, 0.5, r'$F_\lambda$ (erg/s/cm$^2$/${\rm \AA}$)', ha='center', va='center', rotation='vertical', transform=ax.transAxes) ax.text(-0.05, 0.5, r'$\mathcal{I}_\lambda$ (erg/s/cm$^2$/${\rm \AA}$/")', ha='center', va='center', rotation='vertical', transform=ax.transAxes, color='C3') ax.text(0.05, 0.55, 'Source', ha='left', va='center', transform=ax.transAxes) ax.text(0.2, 0.1, 'Sky', color='C3', ha='left', va='center', transform=ax.transAxes) ax = fig.add_axes([0.1, 0.3, 0.85, 0.2]) 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.set_xlim(xlim) ax.set_ylim([0, 1.1]) ax.plot(atm.wave, atm.eta, color='C3', zorder=3) ax.plot(fiber_throughput.wave, fiber_throughput.eta, color='C0', zorder=3) ax.plot(fiddles_qe.wave, fiddles_qe.eta, color='C1', zorder=3) ax.plot(g.wave, g.eta, color='k', zorder=3) ax.text(0.5, -0.2, r'Wavelength (${\rm \AA}$)', ha='center', va='center', transform=ax.transAxes) ax.text(-0.08, 0.5, r'$\eta$', ha='center', va='center', rotation='vertical', transform=ax.transAxes) ax.text(0.01, 0.87, 'Fiber', color='C0', ha='left', va='center', transform=ax.transAxes) ax.text(0.01, 0.57, 'Sky', color='C3', ha='left', va='center', transform=ax.transAxes) ax.text(0.01, 0.4, 'Detector', color='C1', ha='left', va='center', transform=ax.transAxes) ax.text(0.01, 0.1, r'$g$ Filter', color='k', ha='left', 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 fiddles_model(mag, telescope, seeing, exposure_time, airmass=1.0, fiddles_fratio=3., fiber_diameter=0.15, dmag_sky=None): # Use a reference spectrum that's constant; units are 1e-17 erg/s/cm^2/angstrom wave = numpy.linspace(3000., 10000., num=7001) spec = spectrum.ABReferenceSpectrum(wave) g = efficiency.FilterResponse() # Rescale to a specified magnitude spec.rescale_magnitude(mag, band=g) print('Star Magnitude (AB mag): {0:.2f}'.format(spec.magnitude(g))) # Sky Spectrum; units are 1e-17 erg/s/cm^2/angstrom/arcsec^2 sky = spectrum.MaunakeaSkySpectrum() sky_mag = sky.magnitude(g) print('Sky Surface Brightness (AB mag/arcsec^2): {0:.2f}'.format(sky_mag)) if dmag_sky is not None: sky_mag += dmag_sky sky.rescale_magnitude(sky_mag, band=g) # Fiddles detector fiddles_pixel_size = 7.4e-3 # mm fiddles_fullwell = 18133 # Electrons (NOT ADU!) fiddles_platescale = telescope.platescale * fiddles_fratio / telescope.fratio qe_file = os.path.join(os.environ['ENYO_DIR'], 'data', 'efficiency', 'detectors', 'thor_labs_monochrome_qe.db') fiddles_det = efficiency.Detector( pixelscale=fiddles_pixel_size / fiddles_platescale, rn=6.45, dark=5., qe=efficiency.Efficiency.from_file(qe_file, wave_units='nm')) print('FIDDLES pixelscale (arcsec/pixel): {0:.3f}'.format( fiddles_det.pixelscale)) # On-sky fiber diameter on_sky_fiber_diameter = fiber_diameter / fiddles_platescale # arcsec print('On-sky fiber diameter (arcsec): {0:.2f}'.format( on_sky_fiber_diameter)) # Size for the fiber image upsample = 1 if fiddles_det.pixelscale < seeing / 5 else int( fiddles_det.pixelscale / (seeing / 5)) + 1 print('Upsampling: {0}'.format(upsample)) sampling = fiddles_det.pixelscale / upsample print('Pixel sampling of maps: {0:.3f} (arcsec/pixel)'.format(sampling)) size = (int(numpy.ceil(on_sky_fiber_diameter * 1.2 / sampling)) // upsample + 1) * upsample * sampling print('Square map size : {0:.3f} (arcsec)'.format(size)) # Use a point source with unity integral (integral of mapped # profile will not necessarily be unity) star = source.OnSkySource(seeing, 1.0, sampling=sampling, size=size) print('Fraction of star flux in mapped image: {0:.3f}'.format( numpy.sum(star.data) * numpy.square(sampling))) # pyplot.imshow(star.data, origin='lower', interpolation='nearest') # pyplot.show() # Define fiber aperture fiber = aperture.FiberAperture(0., 0., on_sky_fiber_diameter, resolution=100) # Generate its response function fiber_response = fiber.response(star.x, star.y) print('Fiber area (mapped): {0:.2f} (arcsec^2)'.format( numpy.sum(fiber_response) * numpy.square(star.sampling))) print('Fiber area (nominal): {0:.2f} (arcsec^2)'.format(fiber.area)) in_fiber = numpy.sum(star.data * fiber_response) * numpy.square( star.sampling) print('Aperture loss: {0:.3f}'.format(in_fiber)) # This would assume that the fiber does NOT scramble the light... # fiber_img = star.data*fiber_response # Instead scale the fiber response profile to produce a uniformly # scrambled fiber output beam scale = numpy.sum(star.data * fiber_response) / numpy.sum(fiber_response) source_fiber_img = scale * fiber_response # Down-sample to the detector size if upsample > 1: source_fiber_img = util.boxcar_average(source_fiber_img, upscale) fiber_response = util.boxcar_average(fiber_response, upscale) # Scale the spectra to be erg/angstrom (per arcsec^2 for sky) print('Exposure time: {0:.1f} (s)'.format(exposure_time)) spec.rescale(telescope.area * exposure_time) sky.rescale(telescope.area * exposure_time) # Convert them to photons/angstrom (per arcsec^2 for sky) spec.photon_flux() sky.photon_flux() # Atmospheric Extinction atm = efficiency.AtmosphericThroughput(airmass=airmass) # Fiber throughput (10-m polymicro fiber run) fiber_throughput = efficiency.FiberThroughput() # TODO: assumes no fiber FRD or coupling losses # Integrate spectrally to get the total number of electrons at the # detector accounting for the g-band filter and the detector qe. flux = numpy.sum(spec.flux * atm(spec.wave) * fiber_throughput(spec.wave) * g(spec.wave) * fiddles_det.qe(spec.wave) * spec.wavelength_step()) print('Object flux: {0:.3f} (electrons)'.format(flux)) sky_flux = numpy.sum(sky.flux * fiber_throughput(sky.wave) * g(sky.wave) * fiddles_det.qe(sky.wave) * sky.wavelength_step()) print('Sky flux: {0:.3f} (electrons/arcsec^2)'.format(sky_flux)) print('Sky flux: {0:.3f} (electrons)'.format(sky_flux * fiber.area)) # Scale the source image by the expected flux (source_fiber_img was # constructed assuming unity flux) fiber_obj_img = source_fiber_img * flux # Scale the fiber response function by the sky surface brightness # (the integral of the sky image is then just the fiber area times # the uniform sky surface brightness) fiber_sky_img = fiber_response * sky_flux # Integrate spatially over the pixel size to get the number of # electrons in each pixel fiber_obj_img *= numpy.square(upsample * sampling) fiber_sky_img *= numpy.square(upsample * sampling) return fiber_obj_img, fiber_sky_img, fiddles_det, sky_mag
def fiddles_snr_plot(seeing, exptime, airmass, dmag_sky, data_file, plot_file=None): db = numpy.genfromtxt(data_file) mag = db[:, 0] apf_snr_pix = db[:, 1] apf_snr = db[:, 2] apf_saturated = db[:, 3].astype(bool) keck_snr_pix = db[:, 4] keck_snr = db[:, 5] keck_saturated = db[:, 6].astype(bool) # TODO: Read this from the file header g = efficiency.FilterResponse() sky = spectrum.MaunakeaSkySpectrum() sky_mag = sky.magnitude(g) if dmag_sky is not None: sky_mag += dmag_sky # best_apf_mag = interpolate.interp1d(apf_snr_pix, mag)(10.) # print(best_apf_mag) # best_keck_mag = interpolate.interp1d(keck_snr_pix, mag)(10.) # print(best_keck_mag) font = {'size': 16} rc('font', **font) w, h = pyplot.figaspect(1) fig = pyplot.figure(figsize=(1.5 * w, 1.5 * h)) ax = fig.add_axes([0.15, 0.15, 0.69, 0.69], facecolor='0.98') ax.minorticks_on() ax.tick_params(which='major', length=6, direction='in') ax.tick_params(which='minor', length=3, direction='in') ax.grid(True, which='major', color='0.9', zorder=0, linestyle='-') ax.plot(mag[numpy.invert(apf_saturated)], apf_snr_pix[numpy.invert(apf_saturated)], color='C0', zorder=2, lw=0.5) ax.scatter(mag[numpy.invert(apf_saturated)], apf_snr_pix[numpy.invert(apf_saturated)], marker='.', lw=0, color='C0', s=100, zorder=2) if numpy.any(apf_saturated): ax.scatter(mag[apf_saturated], apf_snr_pix[apf_saturated], marker='x', lw=1, color='C3', s=50, zorder=2) ax.plot(mag[numpy.invert(keck_saturated)], keck_snr_pix[numpy.invert(keck_saturated)], color='C2', zorder=2, lw=0.5) ax.scatter(mag[numpy.invert(keck_saturated)], keck_snr_pix[numpy.invert(keck_saturated)], marker='.', lw=0, color='C2', s=100, zorder=2) if numpy.any(keck_saturated): ax.scatter(mag[keck_saturated], keck_snr_pix[keck_saturated], marker='x', lw=1, color='C3', s=50, zorder=2) ax.axhline(10., color='k', linestyle='--') ax.set_ylim([0.01, 300]) ax.set_yscale('log') logformatter = ticker.FuncFormatter(lambda y, pos: ('{{:.{:1d}f}}'.format( int(numpy.maximum(-numpy.log10(y), 0)))).format(y)) ax.yaxis.set_major_formatter(logformatter) ax.text(-0.13, 0.5, r'S/N (pixel$^{-1}$)', ha='center', va='center', rotation='vertical', transform=ax.transAxes) ax.text(0.5, -0.1, r'$m_g$ (AB Mag)', ha='center', va='center', transform=ax.transAxes) ax.text(0.05, 0.35, 'APF', ha='left', va='center', transform=ax.transAxes, color='C0') ax.text(0.05, 0.30, 'Keck', ha='left', va='center', transform=ax.transAxes, color='C2') ax.text(0.05, 0.25, 'Saturated', ha='left', va='center', transform=ax.transAxes, color='C3') ax.text(0.05, 0.20, r'Sky $\mu_g$ = {0:.2f} AB mag / arcsec$^2$'.format(sky_mag), ha='left', va='center', transform=ax.transAxes, color='k') ax.text(0.05, 0.15, 'ExpTime = {0} s'.format(exptime), ha='left', va='center', transform=ax.transAxes, color='k') ax.text(0.05, 0.05, 'Seeing = {0} arcsec FWHM'.format(seeing), ha='left', va='center', transform=ax.transAxes, color='k') ax.text(0.05, 0.10, 'Airmass = {0}'.format(airmass), ha='left', va='center', transform=ax.transAxes, color='k') if plot_file is None: pyplot.show() else: fig.canvas.print_figure(plot_file, bbox_inches='tight') fig.clear() pyplot.close(fig)
if __name__ == '__main__': spec_eff_plot('fiddles_etc_elements.pdf') plot_file = os.path.join(os.environ['ENYO_DIR'], 'fiddles_etc_realization.pdf') fiddles_realization_plot(plot_file=plot_file) main(False) exit() wave = numpy.linspace(3000., 10000., num=7001) spec = spectrum.ABReferenceSpectrum(wave) g = efficiency.FilterResponse() # print('Magnitude: {0:.2f}'.format(spec.magnitude(g))) spec.rescale_magnitude(22.0, band=g) print('Magnitude: {0:.2f}'.format(spec.magnitude(g))) # Sky Spectrum; units are 1e-17 erg/s/cm^2/angstrom/arcsec^2 sky = spectrum.MaunakeaSkySpectrum() print('Sky Magnitude: {0:.2f}'.format(sky.magnitude(g))) pyplot.plot(sky.wave, sky.flux) pyplot.plot(spec.wave, spec.flux) pyplot.plot(g.wave, g.eta) pyplot.show() #test_mag()
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 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() # Extract the slit properties for clarity slit_x, slit_y, slit_width, slit_length, slit_rotation = args.slit effective_slit_width = slit_width / numpy.cos(numpy.radians(slit_rotation)) _extract_length = args.fwhm if args.extract is None else args.extract # TODO: The slit length is currently not used. Instead, the length # of the slit is set to the extraction length. This is mostly just # because of the current way the Observation class works. # Slit aperture. This representation of the slit is *always* # centered at (0,0). Set the aperture based on the extraction # length for now. slit = aperture.SlitAperture(0., 0., slit_width, _extract_length, rotation=slit_rotation) # Get the source distribution. If the source is uniform, onsky is None. onsky = get_source_distribution(args.fwhm, args.uniform, args.sersic) # Sky spectrum and atmospheric throughput sky_spectrum = spectrum.MaunakeaSkySpectrum() atmospheric_throughput = efficiency.AtmosphericThroughput( airmass=args.airmass) # Emission lines to add emline_db = None if args.emline is None else read_emission_line_database( args.emline) # Setup the raw object spectrum if args.spec_file is None: wavelengths = [3100, 10000, 1e-5] wave = get_wavelength_vector(*wavelengths) obj_spectrum = spectrum.ABReferenceSpectrum(wave, log=True) else: obj_spectrum = read_spectrum(args.spec_file, args.spec_wave, args.spec_wave_units, args.spec_flux, args.spec_flux_units, args.spec_res_indx, args.spec_res_value, args.spec_table) #------------------------------------------------------------------- #------------------------------------------------------------------- # Setup the instrument arms #------------------------------------------------------------------- # Blue Arm blue_arm = TMTWFOSBlue(reflectivity=args.refl, grating=args.blue_grat, cen_wave=args.blue_wave, grating_angle=args.blue_angle) # Pixels per resolution element blue_res_pix = blue_arm.resolution_element(slit_width=effective_slit_width, units='pixels') \ / args.blue_binning[0] # Get the wavelength range for each arm blue_wave_lim = blue_arm.wavelength_limits(slit_x, slit_y, add_grating_limits=True) # Setup dummy wavelength vectors to get something appropriate for sampling max_resolution = blue_arm.resolution(blue_wave_lim[1], x=slit_x, slit_width=effective_slit_width) # Set the wavelength vector to allow for a regular, logarithmic binning dw = 1 / blue_res_pix / max_resolution / numpy.log(10) blue_wave = get_wavelength_vector(blue_wave_lim[0], blue_wave_lim[1], dw) resolution = blue_arm.resolution(blue_wave, x=slit_x, slit_width=effective_slit_width) blue_spec = observed_spectrum(obj_spectrum, blue_wave, resolution, mag=args.mag, mag_band=args.mag_band, mag_system=args.mag_system, redshift=args.redshift, emline_db=emline_db) # Resample to linear to better match what's expected for the detector blue_ang_per_pix = blue_arm.resolution_element( wave=blue_wave_lim, slit_width=effective_slit_width, units='angstrom') / blue_res_pix blue_wave = get_wavelength_vector(blue_wave_lim[0], blue_wave_lim[1], numpy.mean(blue_ang_per_pix), linear=True) blue_spec = blue_spec.resample(wave=blue_wave, log=False) # Spectrograph arm efficiency (this doesn't include the telescope) blue_arm_eff = blue_arm.efficiency(blue_spec.wave, x=slit_x, y=slit_y, same_type=False) # System efficiency combines the spectrograph and the telescope blue_thru = efficiency.SystemThroughput( wave=blue_spec.wave, spectrograph=blue_arm_eff, telescope=blue_arm.telescope.throughput) # Extraction: makes simple assumptions about the monochromatic # image and extracts the flux within the aperture, assuming the # flux from both the object and sky is uniformly distributed across # all detector pixels (incorrect!). # Extraction width in pixels spatial_width = slit.length * numpy.cos(numpy.radians(slit.rotation)) / blue_arm.pixelscale \ / args.blue_binning[1] blue_ext = extract.Extraction(blue_arm.det, spatial_width=spatial_width, profile='uniform') # Perform the observation blue_obs = Observation(blue_arm.telescope, sky_spectrum, slit, args.time, blue_arm.det, system_throughput=blue_thru, atmospheric_throughput=atmospheric_throughput, airmass=args.airmass, onsky_source_distribution=onsky, source_spectrum=blue_spec, extraction=blue_ext, snr_units=args.snr_units) # Construct the S/N spectrum blue_snr = blue_obs.snr(sky_sub=True, sky_err=args.sky_err) #------------------------------------------------------------------- # Red Arm red_arm = TMTWFOSRed(reflectivity=args.refl, grating=args.red_grat, cen_wave=args.red_wave, grating_angle=args.red_angle) # Pixels per resolution element red_res_pix = red_arm.resolution_element(slit_width=effective_slit_width, units='pixels') \ / args.red_binning[0] # Get the wavelength range for each arm red_wave_lim = red_arm.wavelength_limits(slit_x, slit_y, add_grating_limits=True) # Setup dummy wavelength vectors to get something appropriate for sampling max_resolution = red_arm.resolution(red_wave_lim[1], x=slit_x, slit_width=effective_slit_width) # Set the wavelength vector to allow for a regular, logarithmic binning dw = 1 / red_res_pix / max_resolution / numpy.log(10) red_wave = get_wavelength_vector(red_wave_lim[0], red_wave_lim[1], dw) resolution = red_arm.resolution(red_wave, x=slit_x, slit_width=effective_slit_width) red_spec = observed_spectrum(obj_spectrum, red_wave, resolution, mag=args.mag, mag_band=args.mag_band, mag_system=args.mag_system, redshift=args.redshift, emline_db=emline_db) # Resample to linear to better match what's expected for the detector red_ang_per_pix = red_arm.resolution_element( wave=red_wave_lim, slit_width=effective_slit_width, units='angstrom') / red_res_pix red_wave = get_wavelength_vector(red_wave_lim[0], red_wave_lim[1], numpy.mean(red_ang_per_pix), linear=True) ree_spec = red_spec.resample(wave=red_wave, log=False) # Spectrograph arm efficiency (this doesn't include the telescope) red_arm_eff = red_arm.efficiency(red_spec.wave, x=slit_x, y=slit_y, same_type=False) # System efficiency combines the spectrograph and the telescope red_thru = efficiency.SystemThroughput( wave=red_spec.wave, spectrograph=red_arm_eff, telescope=red_arm.telescope.throughput) # Extraction: makes simple assumptions about the monochromatic # image and extracts the flux within the aperture, assuming the # flux from both the object and sky is uniformly distributed across # all detector pixels (incorrect!). # Extraction width in pixels spatial_width = slit.length * numpy.cos(numpy.radians(slit.rotation)) / red_arm.pixelscale \ / args.red_binning[1] red_ext = extract.Extraction(red_arm.det, spatial_width=spatial_width, profile='uniform') # Perform the observation red_obs = Observation(red_arm.telescope, sky_spectrum, slit, args.time, red_arm.det, system_throughput=red_thru, atmospheric_throughput=atmospheric_throughput, airmass=args.airmass, onsky_source_distribution=onsky, source_spectrum=red_spec, extraction=red_ext, snr_units=args.snr_units) # Construct the S/N spectrum red_snr = red_obs.snr(sky_sub=True, sky_err=args.sky_err) # Set the wavelength vector dw = 1 / (5 if red_res_pix > 5 else red_res_pix) / max_resolution / numpy.log(10) red_wave = get_wavelength_vector(red_wave_lim[0], red_wave_lim[1], dw) resolution = red_arm.resolution(red_wave, x=slit_x, slit_width=effective_slit_width) red_spec = observed_spectrum(obj_spectrum, red_wave, resolution, mag=args.mag, mag_band=args.mag_band, mag_system=args.mag_system, redshift=args.redshift, emline_db=emline_db) #------------------------------------------------------------------- #------------------------------------------------------------------- snr_label = 'S/N per {0}'.format('R element' if args.snr_units == 'resolution' else args.snr_units) # Report g = efficiency.FilterResponse(band='g') r = efficiency.FilterResponse(band='r') # iband = efficiency.FilterResponse(band='i') print('-' * 70) print('{0:^70}'.format('WFOS S/N Calculation (v0.1)')) 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( obj_spectrum.magnitude(band=g), obj_spectrum.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 - red_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)))) 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([obj_spectrum.wave[0], obj_spectrum.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 = obj_spectrum.plot(ax=ax, label='Object', color='k', lw=1) ax = sky_spectrum.plot(ax=ax, label='Sky', color='0.5', lw=0.5) 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([obj_spectrum.wave[0], obj_spectrum.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 = blue_snr.plot(ax=ax, color='C0', label='Blue Arm') ax = red_snr.plot(ax=ax, color='C3', label='Red Arm') 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() if args.ipython: embed()
def observed_spectrum(spec, wave, resolution, mag=None, mag_band='g', mag_system='AB', redshift=None, emline_db=None): """ Convert input spectrum to expected (noise-free) observation. Operations in order: - Redshifts spectrum, if redshift provided - Rescales magnitude, if mag provided - Matches the spectral resolution, if the input spectrum as a defined resolution vector - Matches the sampling to the input wavelength vector - Add emission lines, if provided """ if redshift is not None: spec.redshift(redshift) if mag is not None: broadband = efficiency.FilterResponse(band=mag_band) # Check that the magnitude can be calculated indx = (broadband.wave < spec.wave[0]) & (broadband.eta > 0) if numpy.any(indx): raise ValueError( 'Input spectrum (after applying any redshift) does not fully ' 'overlap selected broad-band filter. Use a different band or ' 'different spectrum to rescale to the given magnitude.') spec.rescale_magnitude(mag, band=broadband, system=mag_system) _resolution = set_resolution(wave, resolution) # Force the spectrum to be regularly sampled on a logarithmic grid if not spec.regular: print('Imposing uniform logarithmic sampling') # TODO: I'm not sure that this has to be logarithmic. spec = spec.resample(log=True) # If the resolution is available match it to the resolution # expected for the instrument if spec.sres is not None: new_sres = interpolate.interp1d(wave, _resolution, fill_value=(_resolution[0], _resolution[-1]), bounds_error=False)(spec.wave) indx = spec.sres < new_sres # TODO: set some tolerance (i.e., some fraction of the spectral range) if numpy.any(indx): warnings.warn( 'Spectral resolution of input spectrum is lower than what will be ' 'provided by the instrument over {0:.1f}% of the spectral range.' .format(numpy.sum(indx) / indx.size)) print( 'Convolving to delivered spectral resolution (as best as possible).' ) spec = spec.match_resolution(_resolution, wave=wave) else: spec.sres = interpolate.interp1d(wave, _resolution, fill_value=(_resolution[0], _resolution[-1]), bounds_error=False)(spec.wave) # Down-sample to the provided wavelength vector print('Resampling to the provided wavelength sampling.') spec = spec.resample(wave=wave, log=True) if emline_db is None: return spec warnings.warn( 'Adding emission lines will change the magnitude of the object.') return spectrum.EmissionLineSpectrum(spec.wave, emline_db['flux'], emline_db['restwave'], emline_db['fwhm'], units=emline_db['fwhmu'], redshift=redshift, resolution=spec.sres, log=True, continuum=spec.flux)
def onsky_aperture_snr(telescope, mag, seeing, on_sky_fiber_diameter, dmag_sky=None, detector_noise_ratio=1., sampling=0.1, size=5.0, re=None, sersicn=None, exptime=1., quiet=False): # Use a reference spectrum that's constant; units are 1e-17 erg/s/cm^2/angstrom wave = numpy.linspace(3000., 10000., num=7001) spec = spectrum.ABReferenceSpectrum(wave) g = efficiency.FilterResponse() spec.rescale_magnitude(mag, band=g) # Sky Spectrum; units are 1e-17 erg/s/cm^2/angstrom/arcsec^2 sky = spectrum.MaunakeaSkySpectrum() sky_mag = sky.magnitude(g) if dmag_sky is not None: sky_mag += dmag_sky sky.rescale_magnitude(sky_mag, band=g) # # For the image representation of the fiber and source, use a fixed # # sampling # size = (int(numpy.ceil(on_sky_fiber_diameter*1.2/sampling))+1)*sampling # Build the source intrinsic = 1.0 if re is None or sersicn is None \ else source.OnSkySersic(1.0, re, sersicn, sampling=sampling, size=size, unity_integral=True) src = source.OnSkySource(seeing, intrinsic, sampling=sampling, size=size) if numpy.absolute(numpy.log10(numpy.sum(src.data*numpy.square(src.sampling)))) > 0.1: raise ValueError('Bad source representation; image integral is {0:.3f}.'.format( numpy.sum(src.data*numpy.square(src.sampling)))) # Define fiber aperture fiber = aperture.FiberAperture(0., 0., on_sky_fiber_diameter, resolution=100) # Generate its response function fiber_response = fiber.response(src.x, src.y) in_fiber = numpy.sum(src.data*fiber_response)*numpy.square(src.sampling) # Scale the fiber response profile to produce a uniformly scrambled # fiber output beam scale = numpy.sum(src.data*fiber_response)/numpy.sum(fiber_response) source_fiber_img = scale*fiber_response # Convert them to photons/angstrom (per arcsec^2 for sky) spec.rescale(telescope.area*exptime) sky.rescale(telescope.area*exptime) spec.photon_flux() sky.photon_flux() # Get the total number of source and sky photons at # the detector integrated over the g-band filter flux = numpy.sum(spec.flux * g(spec.wave) * spec.wavelength_step()) sky_flux = numpy.sum(sky.flux * g(sky.wave) * sky.wavelength_step()) # Scale the source image by the expected flux (source_fiber_img was # constructed assuming unity flux) fiber_obj_img = source_fiber_img * flux # Scale the fiber response function by the sky surface brightness # (the integral of the sky image is then just the fiber area times # the uniform sky surface brightness) fiber_sky_img = fiber_response * sky_flux # Integrate spatially over the pixel size to get the number of # photons/s/cm^2 entering the fiber fiber_obj_flux = numpy.sum(fiber_obj_img * numpy.square(src.sampling)) fiber_sky_flux = numpy.sum(fiber_sky_img * numpy.square(src.sampling)) total_flux = fiber_obj_flux + fiber_sky_flux sky_var = fiber_sky_flux + detector_noise_ratio*total_flux total_var = fiber_obj_flux + sky_var total_snr = fiber_obj_flux/numpy.sqrt(total_var) skysub_snr = fiber_obj_flux/numpy.sqrt(total_var + sky_var) if not quiet: print('Star Magnitude (AB mag): {0:.2f}'.format(spec.magnitude(g))) print('Sky Surface Brightness (AB mag/arcsec^2): {0:.2f}'.format(sky_mag)) print('On-sky fiber diameter (arcsec): {0:.2f}'.format(on_sky_fiber_diameter)) print('Pixel sampling of maps: {0:.3f} (arcsec/pixel)'.format(sampling)) print('Square map size : {0:.3f} (arcsec)'.format(size)) print('Fraction of source flux in mapped image: {0:.3f}'.format(numpy.sum(src.data) * numpy.square(sampling))) print('Fiber area (mapped): {0:.2f} (arcsec^2)'.format(numpy.sum(fiber_response) * numpy.square(src.sampling))) print('Fiber area (nominal): {0:.2f} (arcsec^2)'.format(fiber.area)) print('Aperture loss: {0:.3f}'.format(in_fiber)) print('Object flux: {0:.3e} (g-band photons/s)'.format(fiber_obj_flux)) print('Sky flux: {0:.3e} (g-band photons/s)'.format(fiber_sky_flux)) print('Total S/N: {0:.3f}'.format(total_snr)) print('Sky-subtracted S/N: {0:.3f}'.format(skysub_snr)) # return fiber_obj_flux, fiber_sky_flux, total_snr, skysub_snr return total_snr, skysub_snr