Beispiel #1
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))
Beispiel #2
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))
Beispiel #3
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
Beispiel #4
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
Beispiel #5
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)
Beispiel #6
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
Beispiel #7
0
    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()
Beispiel #8
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()
Beispiel #9
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