def get_source_distribution(fwhm, uniform, sersic): if uniform: return None # Build the source surface brightness distribution with unity # integral; intrinsic is set to 1 for a point source intrinsic = 1. if sersic is None \ else source.OnSkySersic(1.0, sersic[0], sersic[1], ellipticity=sersic[2], position_angle=sersic[3], unity_integral=True) # Set the size of the map used to render the source size = fwhm * 5 if sersic is None else max(fwhm * 5, sersic[0] * 5) sampling = fwhm / 10 if sersic is None else min(fwhm / 10, sersic[0] / 10 / sersic[1]) # Check the rendering of the Sersic profile if sersic is not None: intrinsic.make_map(sampling=sampling, size=size) r, theta = intrinsic.semi.polar(intrinsic.X, intrinsic.Y) flux_ratio = 2 * numpy.sum(intrinsic.data[r < intrinsic.r_eff.value]) \ * numpy.square(sampling) / intrinsic.get_integral() if numpy.absolute(numpy.log10(flux_ratio)) > numpy.log10(1.05): warnings.warn( 'Difference in expected vs. map-rendered flux is larger than ' '5%: {0}%.'.format((flux_ratio - 1) * 100)) # Construct the on-sky source distribution return source.OnSkySource(fwhm, intrinsic, sampling=sampling, size=size)
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 direct(): seeing = 0.6 # arcsec wave_ref = 5500 # Reference wavelength in angstrom slitwidth = 0.75 # arcsec slitlength = 5. # arcsec slitrotation = 0. # degrees sky_spec = spectrum.MaunakeaSkySpectrum() # pyplot.plot(sky_spec.wave, sky_spec.flux) # pyplot.show() psf = source.OnSkyGaussian(seeing) 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') off_slit_img = lowres_wfos.monochromatic_image(sky, slit, arm='blue') # pyplot.imshow(off_slit_img, origin='lower', interpolation='nearest') # pyplot.show() nspec, nspat = off_slit_img.shape # pyplot.plot(off_slit_img[:,nspat//2]) # pyplot.show() test_twod = observe.rectilinear_twod_spectrum( sky_spec, off_slit_img, lowres_wfos.arms['blue'].dispscale) print('finished') pyplot.imshow(test_twod, origin='lower', interpolation='nearest', aspect='auto') pyplot.show()
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 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 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