예제 #1
0
def get_gaia_data(coordinates, radius=20 * u.arcmin, mag_limit=16.):
    gaia_data = Gaia.cone_search(coordinates, radius)

    gaia_data = gaia_data.get_results()

    p = np.where(gaia_data['phot_g_mean_mag'] <= mag_limit)[0]
    gaia_data = gaia_data[p]

    gaia_data.sort('phot_g_mean_mag')

    return gaia_data
예제 #2
0
def draw_fc(fc_params, bk_image=None, bk_lam=None, do_pdf=False, do_png=False):
    '''
   The finding chart master plotting function.

   Args:
      fc_params: a dictionnary containing the OB parameters
      bk_image: the background image as string, either a SkyView entry, or local filename
      bk_lam: the wavelength of the chart as a string
      do_pdf (bool): save a pdf file, in addition to jpg
      do_png (bool): save a png file, in addition to jpg
   Returns:
      The finding chart filename as a string
   '''

    # Load all the fields dictionaries
    fields = fcm_id.get_fields_dict(fc_params)

    # Get the radius and center of the charts
    (left_radius, right_radius) = fcm_id.get_chart_radius(fc_params)
    (left_center, right_center) = fcm_id.get_chart_center(fc_params)

    # Get the background image in place
    (fn_bk_image_L, survey_L, bk_lam_L) = get_bk_image(bk_image, bk_lam,
                                                       left_center,
                                                       left_radius, fc_params)

    # If this is not the DSS2 Red, then download this as well for the right-hand-side plot
    if not (survey_L == 'DSS2 Red'):
        (fn_bk_image_R, survey_R,
         bk_lam_R) = get_bk_image('DSS2 Red', None, right_center, right_radius,
                                  fc_params)
    else:
        (fn_bk_image_R, survey_R, bk_lam_R) = copy.deepcopy(
            (fn_bk_image_L, survey_L, bk_lam_L))

    # Start the plotting
    plt.close(1)
    fig1 = plt.figure(
        1,
        figsize=(14.17, 7))  #14.17 inches, at 50% = 1 full page plot in A&A.

    ax1 = aplpy.FITSFigure(fn_bk_image_L,
                           figure=fig1,
                           north=fcm_m.set_North,
                           subplot=[0.12, 0.12, 0.35, 0.76])
    ax1.show_grayscale(invert=True,
                       stretch='linear',
                       pmin=fcm_id.get_pmin(survey_L))

    ax2 = aplpy.FITSFigure(fn_bk_image_R,
                           figure=fig1,
                           north=fcm_m.set_North,
                           subplot=[0.59, 0.12, 0.38, 0.8])
    ax2.show_grayscale(invert=True,
                       stretch='linear',
                       pmin=fcm_id.get_pmin(survey_R))

    ax1.recenter(left_center.ra, left_center.dec, radius=left_radius / 3600.)
    ax2.recenter(right_center.ra,
                 right_center.dec,
                 radius=right_radius / 3600.)

    # I will be adding stuff to the left-hand-side plot ... start collecting them
    ax1_legend_items = []

    # Do I have the epoch of observation for the background image ?
    '''
   try:
      bk_obsdate = fits.getval(fn_bk_image, 'DATE-OBS')

      # If not specified, assume UTC time zone for bk image
      if bk_obsdate.tzinfo is None:
      #   #warnings.warn(' "--obsdate" timezone not specified. Assuming UTC.')
         bk_obsdate = bk_obsdate.replace(tzinfo=pytz.utc)

      nowm_time = Time(bk_obsdate)
      pm_track_time = (fcm_obsdate - bk_obsdate).total_seconds()*u.s

   except:
      # If not, just shows the default length set in fcmaker_metadata
      nowm_time = Time(fcm_m.obsdate) - fcm_m.pm_track_time
      pm_track_time = fcm_m.pm_track_time
   '''

    # Just keep things simple. Same lookback time for all charts.
    nowm_time = Time(fcm_m.obsdate) - fcm_m.pm_track_time
    #pm_track_time = fcm_m.pm_track_time

    # If yes, then let's show where are the fastest stars moved from/to.
    #if do_GAIA_pm and (fcm_m.min_abs_GAIA_pm >=0):
    if (fcm_m.min_abs_GAIA_pm >= 0):

        # Query GAIA
        print(
            '   Querying GAIA DR2 to look for high proper motion stars in the area ...'
        )

        # Make it a sync search, because I don't think I need more than 2000 targets ...
        j = Gaia.cone_search(left_center,
                             right_radius * u.arcsec,
                             verbose=False)
        r = j.get_results()

        selected = np.sqrt(r['pmra']**2 + r['pmdec']**2
                           ) * u.mas / u.yr > fcm_m.min_abs_GAIA_pm

        # Show the fastest stars
        past_tracks = []
        future_tracks = []

        # Need to propagate their coords to the fc epoch and the obstime
        for s in range(len(r['ra'][selected])):

            star = SkyCoord(
                ra=r['ra'][selected][s],
                dec=r['dec'][selected][s],
                obstime=Time(r['ref_epoch'][selected][s],
                             format='decimalyear'),
                frame='icrs',
                unit=(u.deg, u.deg),
                pm_ra_cosdec=r['pmra'][selected][s] * u.mas / u.yr,
                pm_dec=r['pmdec'][selected][s] * u.mas / u.yr,
                # I must specify a generic distance to the target,
                # if I want to later on propagate the proper motions
                distance=fcm_m.default_pm_d,
            )

            now = star.apply_space_motion(new_obstime=Time(fcm_m.obsdate))
            nowm = star.apply_space_motion(new_obstime=nowm_time)

            past_tracks += [
                np.array([[nowm.ra.deg, now.ra.deg],
                          [nowm.dec.deg, now.dec.deg]])
            ]

            for ax in [ax1, ax2]:
                ax.show_markers([now.ra.deg], [now.dec.deg],
                                marker='.',
                                color='crimson',
                                facecolor='crimson',
                                edgecolor='crimson')

        #if len(r['ra'][selected])>0:
        #   # Prepare a dedicated legend entry
        #   ax1_legend_items += [mlines.Line2D([], [],color='crimson',
        #                         markerfacecolor='crimson',
        #                         markeredgecolor='crimson',
        #                         linestyle='-',
        #                         linewidth=0.75,
        #                         marker='.',
        #                         #markersize=10,
        #                         label='PM* (track$=-$%.1f yr)' % (pm_track_time.to(u.yr).value)) ]
        for ax in [ax1, ax2]:
            ax.show_lines(past_tracks,
                          color='crimson',
                          linewidth=0.75,
                          linestyle='-')

    # Query UCAC2 via Vizier over the finding chart area, to look for suitable Guide Stars
    print('   Querying UCAC2 via Vizier to look for possible Guide Stars ...')
    Vizier.ROW_LIMIT = 10000
    gs_table = Vizier.query_region(
        right_center,
        radius=right_radius * u.arcsec,
        inner_radius=fcm_id.get_inner_GS_search(fc_params) * u.arcsec,
        catalog="UCAC2")
    gs_table = gs_table[gs_table.keys()[0]]

    # Turn the table into a list of SkyCoord, that I can clean as I go.
    # Only select guide stars in the nominal GS mag range
    gs_list = [
        SkyCoord(
            ra=line['RAJ2000'],
            dec=line['DEJ2000'],
            obstime=Time('J2000'),
            equinox='J2000',
            frame='icrs',
            unit=(u.deg, u.deg),
            pm_ra_cosdec=line['pmRA'] / 1000. * u.mas / u.yr,
            pm_dec=line['pmDE'] / 1000. * u.mas / u.yr,
            # Assume a fixed distance, so that I can then propagate proper motions
            distance=100. *
            u.pc).apply_space_motion(new_obstime=Time(fcm_m.obsdate))
        for line in gs_table
        if fcm_m.gs_mag[0] <= line['UCmag'] <= fcm_m.gs_mag[1]
    ]

    # Here, I will show all the ephemeris points I have prepared, ahead of the
    # observations (If I have any)

    if len(fc_params['ephem_points_past']) > 0:
        ax1.show_markers(
            [item.ra.deg for item in fc_params['ephem_points_past']],
            [item.dec.deg for item in fc_params['ephem_points_past']],
            marker='*',
            color='crimson',
            s=100,
            facecolor='none',
            edgecolor='crimson')

        # Prepare a dedicated legend entry
        ax1_legend_items += [
            mlines.Line2D(
                [],
                [],
                color='crimson',
                markerfacecolor='none',
                markeredgecolor='crimson',
                linestyle='',
                #linewidth=0.75,
                marker='*',
                #markersize=10,
                label='Target ($\Delta T=-%.0f$ min)' %
                (fc_params['ephem_past_delta'].total_seconds() / 60.))
        ]

    if len(fc_params['ephem_points_future']) > 0:
        ax1.show_markers(
            [item.ra.deg for item in fc_params['ephem_points_future']],
            [item.dec.deg for item in fc_params['ephem_points_future']],
            marker='*',
            color='crimson',
            s=100,
            facecolor='crimson',
            edgecolor='crimson')

        # Prepare a dedicated legend entry
        ax1_legend_items += [
            mlines.Line2D(
                [],
                [],
                color='crimson',
                markerfacecolor='crimson',
                markeredgecolor='crimson',
                linestyle='',
                #linewidth=0.75,
                marker='*',
                #markersize=10,
                label='Target ($\Delta T=+%.0f$ min)' %
                (fc_params['ephem_future_delta'].total_seconds() / 60.))
        ]

    # Show the observation footprint
    for f in fields:

        # Call the function that will plot all the important stuff for this field.
        fcm_id.plot_field(ax1, ax2, fc_params, fields[f])

        # Keep all the possible guide stars in the area.
        gs_list = [
            star for star in gs_list
            if (fields[f][2].separation(star) >
                (fcm_id.get_inner_GS_search(fc_params) / 3600. * u.deg)) and (
                    fields[f][2].separation(star) <
                    (fcm_id.get_GS_outer_radius(fc_params) / 3600. * u.deg))
        ]

    # Show all the suitable Guide Star in the area
    if len(gs_list) > 0:
        ax2.show_markers([np.array(star.ra) for star in gs_list],
                         [np.array(star.dec) for star in gs_list],
                         marker='o',
                         edgecolor='crimson',
                         s=50,
                         linewidth=1.0)
    else:
        warnings.warn('Watch out ... no suitable Guide Star found in UCAC2 !')

    # Add orientation arrows to the large view plot
    add_orient(ax2,
               right_center,
               radius=(right_radius * 0.82) * u.arcsec,
               arrow_width=(right_radius * 0.82) / 4.5 * u.arcsec,
               usetex=fcm_m.fcm_usetex)
    add_orient(ax1,
               left_center,
               radius=(left_radius * 0.82) * u.arcsec,
               arrow_width=(left_radius * 0.82) / 4.5 * u.arcsec,
               usetex=fcm_m.fcm_usetex)

    # Add a scale bar
    (scl, scll) = fcm_id.get_scalebar(fc_params['inst'],
                                      ins_mode=fc_params['ins_mode'])

    ax1.add_scalebar(scl)  # Length in degrees
    ax1.scalebar.show(scl,
                      label=scll,
                      corner='bottom left',
                      color='k',
                      frame=1)
    ax1.scalebar.set_font_size(12)

    scl2 = np.floor(right_radius / 60 / 6) / 60.
    scll2 = r'%.0f$^{\prime}$' % (scl2 * 60)
    ax2.add_scalebar(scl2)
    ax2.scalebar.show(scl2,
                      label=scll2,
                      corner='bottom left',
                      color='k',
                      frame=1)
    ax2.scalebar.set_font_size(12)

    for ax in [ax1, ax2]:
        ax.scalebar.set_linewidth(2)

    # Fine tune things a bit further, just because I can ...
    for ax in [ax1, ax2]:
        ax.tick_labels.set_xformat('hh:mm:ss')
        ax.axis_labels.set_xpad(10)
        ax.ticks.set_linewidth(1.5)
        ax.set_tick_size(10)
        ax.set_tick_color('k')
        ax.axis_labels.set_xtext('R.A. [J2000]')

    ax1.axis_labels.set_ytext('Dec. [J2000]')
    ax1.axis_labels.set_ypad(-10)
    ax2.axis_labels.set_ytext('')

    # Add the required OB information to comply with ESO requirements ...
    # ... and make the life of the night astronomer a lot easier !
    ax1.add_label(0.0,
                  1.11,
                  'Run ID: ' + fc_params['prog_id'] + ' | ' + fc_params['pi'],
                  relative=True,
                  horizontalalignment='left',
                  size=14)

    # Fix some bugs for anyone not using LaTeX ... sigh ...
    if fcm_m.fcm_usetex:
        lab = fc_params['ob_name'].replace('_', '\_')
    else:
        lab = fc_params['ob_name']

    ax1.add_label(0.0,
                  1.06,
                  'OB: %i | %s' % (fc_params['ob_id'], lab),
                  relative=True,
                  horizontalalignment='left',
                  size=14)
    #ax1.add_label(0.0,1.08, r'$\lambda_{fc}$: %s (%s)' % (bk_lam_L, survey_L), relative=True,
    #              horizontalalignment='left')

    # Display the Wavelength of the plots
    ax1.add_label(0.02,
                  0.965,
                  r'%s (%s)' % (bk_lam_L, survey_L),
                  relative=True,
                  fontsize=12,
                  horizontalalignment='left',
                  verticalalignment='center',
                  bbox=dict(edgecolor='w', facecolor='w', alpha=0.85))
    ax2.add_label(0.02,
                  0.965,
                  r'%s (%s)' % (bk_lam_R, survey_R),
                  relative=True,
                  fontsize=12,
                  horizontalalignment='left',
                  verticalalignment='center',
                  bbox=dict(edgecolor='w', facecolor='w', alpha=0.85))

    # Add a legend (if warranted) for the left plot
    if len(ax1_legend_items) > 0:
        ax1._ax1.legend(
            handles=ax1_legend_items,
            bbox_to_anchor=(-0.03, 0.82, 0.4, .1),  #loc='lower right',
            ncol=1,  #mode="expand",
            borderaxespad=0.,
            fontsize=10,
            borderpad=0.3,
            handletextpad=0.,
            handlelength=2.0)

    # Start keeping track of any tags I need to show
    tag_string = r' '

    # Show the obsdate
    ax1.add_label(1.0,
                  1.02,
                  r'Obs. date: ' +
                  datetime.strftime(fcm_m.obsdate, '%Y-%m-%d %H:%M %Z'),
                  relative=True,
                  color='k',
                  horizontalalignment='right',
                  fontsize=11)

    # Show the OB tags
    if 'moving_target' in fc_params['tags']:
        tag_string += '$\leadsto$ '

    if 'parallactic_angle' in fc_params['tags']:
        tag_string += '$\measuredangle$ '

    if len(tag_string) > 1:  # only show the tag if it has a non-zero length
        ax1.add_label(
            -0.18,
            1.08,
            tag_string,
            relative=True,
            color='k',
            horizontalalignment='center',
            verticalalignment='center',
            fontsize=30,
            bbox=dict(edgecolor='k',
                      facecolor='lightsalmon',
                      alpha=1,
                      linewidth=1,
                      boxstyle="sawtooth,pad=0.2,tooth_size=0.075"),
        )

    # Finally include the version of fcmaker in there
    ax1.add_label(1.01,
                  0.00,
                  r'Created with fcmaker v%s' % (fcm_m.__version__),
                  relative=True,
                  horizontalalignment='left',
                  verticalalignment='bottom',
                  fontsize=10,
                  rotation=90)

    # Save it all, both jpg for upload to p2, and pdf for nice quality.
    fn_out = os.path.join(
        fcm_m.plot_loc, fc_params['ob_name'].replace(' ', '_') + '_' +
        survey_L.replace(' ', '-'))

    fig1.savefig(fn_out + '.jpg')
    if do_pdf:
        fig1.savefig(fn_out + '.pdf')
    if do_png:
        fig1.savefig(fn_out + '.png')

    plt.close()

    return fn_out + '.jpg'
예제 #3
0
def gaia_cmd(target, sources, catalog='eDR3', plot=True):
    '''Authors:
            Patrick Tamburo, Boston University, May 2021
        Purpose:
            Queries Gaia for sources and creates a CMD for them. 
            Adds Gaia M_G and BP-RP to the source csv file. 
        Inputs:
            target (str): The target's full 2MASS name.
            sources (pd.DataFrame): DataFrame of sources to be tracked in the field. These sources must have RA and Dec values.
            catalog (str, optional): The name of the Gaia catalog you wish to query, 'DR2' or 'eDR3'.
            plot (bool, optional): Whether or not to save the CMD to the object's 'sources' directory.
            red_only (bool, optional): Whether or not to limit the retained sources to the reddest stars in the field. 
        Outputs:
            sources (pd.DataFrame): DataFrame of sources, with M_G and BP-RP columns added. Also updates the target_and references_source_detection.csv file in the object's 'sources' directory. 
        TODO:
            None.
    '''

    pines_path = pat.utils.pines_dir_check()
    short_name = pat.utils.short_name_creator(target)

    #Set the Gaia data release to query.
    if catalog == 'DR2':
        Gaia.MAIN_GAIA_TABLE = 'gaiadr2.gaia_source'
    elif catalog == 'eDR3':
        Gaia.MAIN_GAIA_TABLE = 'gaiadr3.gaia_source'
    else:
        raise ValueError('catalog must be DR2 or eDR3.')

    num_s = len(sources)
    bp_rp = np.zeros(num_s)  #Gaia Bp - Rp color
    p_mas = np.zeros(num_s)  #Parallax in mas
    M_G = np.zeros(num_s)  #Absolute Gaia Rp

    print('Querying sources in Gaia {}.'.format(catalog))

    for i in range(num_s):
        ra = sources['Source Detect RA'][i]
        dec = sources['Source Detect Dec'][i]
        c = SkyCoord(ra=ra, dec=dec, unit=(u.deg, u.deg))

        #Query Gaia
        query_gaia = Gaia.cone_search(c, radius=5 * u.arcsec)
        result_gaia = query_gaia.get_results()

        if len(result_gaia) == 0:
            bp_rp[i] = np.nan
            p_mas[i] = np.nan
            M_G[i] = np.nan
            print(
                'No source found in Gaia {} at ({:1.4f},{:1.4f}). Returning NaNs.'
                .format(catalog, ra, dec))
        else:
            bp_rp[i] = result_gaia['bp_rp'][0]
            m_G = result_gaia['phot_g_mean_mag'][0]
            p_mas[i] = result_gaia['parallax'][0]
            dist_pc = 1 / (p_mas[i] / 1000)
            M_G[i] = m_G + 5 - 5 * np.log10(dist_pc)

    if plot:
        plt.figure(figsize=(6, 9))
        plt.scatter(bp_rp, M_G)
        plt.title(sources['Name'][0] + ' field CMD', fontsize=18)
        plt.xlabel('G$_{BP}$ - G$_{RP}$', fontsize=16)
        plt.ylabel('M$_G$', fontsize=16)
        plt.xlim(-1, 5)
        plt.ylim(-5, 16)
        plt.gca().invert_yaxis()
        plt.tight_layout()
        plt.savefig(pines_path /
                    ('Objects/' + short_name + '/sources/gaia_cmd.png'),
                    dpi=300)

    sources['M_G'] = M_G
    sources['bp_rp'] = bp_rp

    sources.to_csv(pines_path /
                   ('Objects/' + short_name +
                    '/sources/target_and_references_source_detection.csv'),
                   index=0,
                   na_rep='NaN')

    return sources
예제 #4
0
def make_gaia_image(skycoord,
                    fov,
                    sampling,
                    fwhm,
                    obsdate=dup.parse("2018 07 01 00:00:00 UTC"),
                    fn=os.path.join('.', 'pseudo_gaia.fits')):
    '''
   Given a coordinate, use the Gaia catalogue to create a mock image of the sky.

   Args:
      skycoord: the astropy SkyCoord of the center of the image
      fov: size of the image to create, in astropy.units
      sampling: pixel size of the image to create, in astropy.units
      fwhm: fwhm of the fake stars in the image, in astropy.units
      obsdate: date of the observation (to propagate the motion of the stars) as datetime.datetime object
      fn: relative path + FITS filename
   Returns:
      The FITS filename
   '''

    # Ok, assemble an array of the suitable size
    nx = int(np.floor((fov / sampling).value))
    im = np.zeros((nx, nx))

    # Query GAIA
    print('   Querying GAIA to create a pseudo-image of the sky ...')
    # Make it a sync search, because I don't think I need more than 2000 targets ...

    j = Gaia.cone_search(skycoord, fov, verbose=False)
    r = j.get_results()
    nstars = len(r)

    # TODO: well, what if I do need more than 2000 targets ?
    # Issue a warning for now ...
    if nstars == 2000:
        warnings.warn(
            ' Reached query limit of 2k stars. Gaia image will not be complete.'
        )

    # Create a very basic header for the FITS file
    hdr = fits.Header()
    hdr.set('CRPIX1', nx / 2, 'X reference pixel')
    hdr.set('CRPIX2', nx / 2, 'Y reference pixel')
    hdr.set('CRVAL1', skycoord.ra.deg, 'Reference longitude')
    hdr.set('CRVAL2', skycoord.dec.deg, 'Reference latitude')
    hdr.set('CTYPE1', 'RA---TAN', 'Coordinates -- projection')
    hdr.set('CTYPE2', 'DEC--TAN', 'Coordinates -- projection')
    hdr.set('CDELT1', -sampling.to(u.deg).value, 'X scale')  # mind the sign !
    hdr.set('CDELT2', sampling.to(u.deg).value, 'Y scale')
    hdr.set('RADESYS', 'ICRS', 'Coordinate system')
    hdr.set('EQUINOX', 2000.0, 'Epoch of the equinox')
    hdr['COMMENT'] = 'Artificial sky image reconstructed from the Gaia catalogue.'
    hdr['COMMENT'] = 'Number of stars: %i' % (nstars)
    hdr['COMMENT'] = 'FWHM: %i mas' % (int(fwhm.to(u.mas).value))
    hdr['COMMENT'] = 'Bandpass:    285.5-908 THz '
    hdr['COMMENT'] = 'Epoch: %s' % (obsdate.strftime('%Y-%h-%d %H:%M:%S UTC'))
    hdr['COMMENT'] = 'Created %s with fcmaker v%s' % (
        datetime.utcnow().strftime('%Y-%h-%d %H:%M:%S UTC'), fcm_m.__version__)
    hdr['COMMENT'] = 'See http://fpavogt.github.io/fcmaker for details.'

    # Save the empty FITS file for now
    hdu = fits.PrimaryHDU(im, header=hdr)
    hdul = fits.HDUList([hdu])
    hdul.writeto(fn, overwrite=True)

    # Re-extract the WCS info
    w = WCS(fn)

    # Prepare some variables
    x, y = np.meshgrid(np.arange(0, nx, 1), np.arange(0, nx, 1))
    sigma = (fwhm / sampling).value / (2 * np.sqrt(2 * np.log(2)))

    # For each star, add a Gaussian of the suitable size
    for s in range(nstars):
        # First, create a SkyCoord entry
        star = SkyCoord(
            ra=r['ra'][s],
            dec=r['dec'][s],
            obstime=Time(r['ref_epoch'][s], format='decimalyear'),
            frame='icrs',
            unit=(u.deg, u.deg),
            pm_ra_cosdec=r['pmra'][s] * u.mas / u.yr,
            pm_dec=r['pmdec'][s] * u.mas / u.yr,
            # I must specify a generic distance to the target,
            # if I want to later on propagate the proper motions
            distance=fcm_m.default_pm_d,
        )

        # Then, propagate the star at the time of the observation
        star_now = star.apply_space_motion(new_obstime=Time(obsdate))

        # Get the image coordinate of the star
        star_coords = w.wcs_world2pix([star_now.ra.deg], [star_now.dec.deg], 0)

        # The distance of all pixels to the star
        d = np.sqrt((x - star_coords[0])**2 + (y - star_coords[1])**2)

        # Add the star to the array ... scale it as a function of its flux
        im += r['phot_g_mean_flux'][s] * np.exp(-(
            (d)**2 / (2.0 * (fwhm / sampling).value**2)))

    # Save the final FITS file
    hdu = fits.PrimaryHDU(im, header=hdr)
    hdul = fits.HDUList([hdu])
    hdul.writeto(fn, overwrite=True)

    return fn