Example #1
0
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
Example #2
0
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))
Example #3
0
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))
Example #4
0
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
Example #5
0
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
Example #6
0
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)
Example #7
0
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)
Example #8
0
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)
Example #9
0
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
Example #10
0
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)
Example #11
0

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()
Example #12
0
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()
Example #13
0
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))))
Example #14
0
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()
Example #15
0
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)
Example #16
0
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