Empirical survey selection function for Gaia DR3#

updated: 09 Oct 2023

Completeness using the empirical Gaia DR3 selection function#

Our selection function of the Gaia DR3 catalogue can be queried via class DR3SelectionFunctionTCG, which can be loaded in three different modes:

  • 'hpx7' (default) loads a precomputed all-sky map in healpix regions of order 7

  • 'multi' loads a precomputed all-sky map in healpix regions of order 7 to 10, with finer binning in denser parts of the sky

  • 'patch' creates maps on the fly for a given circular region of the sky

from gaiaunlimited.selectionfunctions import DR3SelectionFunctionTCG

When we call the class, the package will look for the file allsky_M10_hpx7.hdf5. If it is not present, it will download it.

mapHpx7 = DR3SelectionFunctionTCG()

To obtain the completeness at a given point and magnitude we use the .query() method. The coordinates must be given as astropy objects. To quickly generate the list of coordinates of the centers of healpixels we provide the get_healpix_centers function. The array containing magnitudes must have the same shape as the coordinate array.

from gaiaunlimited.utils import get_healpix_centers
coords_of_centers = get_healpix_centers(7)
import numpy as np
gmag = np.ones_like(coords_of_centers) * 21.
print(f'Computing the completeness for {len(coords_of_centers)} points.')
completeness = mapHpx7.query(coords_of_centers,gmag)
Computing the completeness for 196608 points.

This returned an array of numbers between 0 and 1:

print(completeness)
[0.72350425 0.71772665 0.69901098 ... 0.69006834 0.73700795 0.67268723]

We can use healpy’s mollview to plot the all-sky map. The healpix numbering scheme is in equatorial coordinates (‘Celestial’ in healpy), but for all-sky maps I prefer using Galactic coordinates:

import healpy as hp
hp.mollview(completeness,coord=['Celestial','Galactic'])
../_images/cbe737e7a8f4074cb4f25d97081f50862cbcf5a85edc58afda7055a2db610aca.png

You might recognise some features already. The stripes are caused by the Gaia scanning law. Regions with high stellar density are also visible, in the direction of the Galactic centre, or the Magellanic clouds. The presence of globular clusters is also visible, as individual healpixels of low completeness.

We can zoom in on a particular region with gnomview, for instance the globular cluster Omega Cen (NGC 5139) near (ra,dec)=(200.5,-47.5).

Let’s make a plot with width of 480 pixels, and 0.5 arcminute per pixel.

hp.gnomview(completeness,rot=[200.5,-47.5],xsize=480,reso=0.5)
../_images/217a528c284f00341ca21b92d32b1204dd46f75df2c214f300b4b6456fafe212.png

The size of a healpix order 7 is about half a degree, which may be too coarse for some science cases.

Higher resolution: multi-order map#

Calling DR3SelectionFunctionTCG('multi') we load a precomputed all-sky map where the healpixel size varies with density. Here is an example around the globular cluster Omega Centauri (NGC 5139).

import matplotlib.pyplot as plt

mapMulti = DR3SelectionFunctionTCG('multi')

import numpy as np
from astropy import units as u
from astropy.coordinates import SkyCoord

#Define grid of points to evaluate completeness:
xx =[]
yy =[]
for rr in np.linspace( 198 , 203 , 100 ):
    for dd in np.linspace( -49 , -46 , 100 ):
        xx.append(rr)
        yy.append(dd)
cc = SkyCoord(ra=xx*u.degree, dec=yy*u.degree)

#Magnitude (same for all points here):
gmag = np.ones_like(cc) * 21

completenessMultiOrder = mapMulti.query( cc , gmag )
completenessHpx7 = mapHpx7.query( cc , gmag )

plt.figure(figsize=(11,5))

plt.subplot(121)
plt.title('multi')
plt.scatter( xx , yy , c=completenessMultiOrder, s=10, vmin=0.,vmax=1 , cmap='viridis')
plt.xlabel('ra'); plt.ylabel('dec')
plt.xlim(203,198); plt.ylim(-49,-46)
plt.subplot(122)
plt.title('hpx7')
plt.scatter( xx , yy , c=completenessHpx7, s=10, vmin=0.,vmax=1 , cmap='viridis')
plt.xlabel('ra'); plt.ylabel('dec')
plt.xlim(203,198); plt.ylim(-49,-46)

plt.suptitle('completeness at G=21');
../_images/a462c404f163a402d7d76999f2f1ace37aeb75ec2776724cf29a6823f5a58ea0.png

The multiorder map has a finer resolution, especially around the cluster.

Creating a high-resolution map of a specific region#

This queries the Gaia archive using the astroquery package.

We provide a way to initialise the model map for a small region of the sky (a few square degrees are still manageable). The map will employ healpixels of level 6 to 12, providing a finer resolution in the denser areas.

NOTE: In this example we use a radius of 1.5 degrees. The queries are quick but organising the multi-order map takes about two minutes.

At present this only creates circular patches, centered on a (ra,dec) position specified by the user, with a radius given in degrees. The coordinates of the center must be given as an astropy SkyCoord object.

%%time
coordCenter = SkyCoord(ra=200.5,dec=-47.5,unit='deg',frame='icrs')
mapPatch = DR3SelectionFunctionTCG( 'patch' , coordCenter , 1.5 )
Querying the Gaia archive...
SELECT ra, dec, source_id,phot_g_mean_mag
    FROM gaiadr3.gaia_source
    WHERE 1 = CONTAINS(POINT(ra,dec),CIRCLE(200.500000, -47.500000, 1.500000))
    and astrometric_matched_transits<11
    and phot_g_mean_mag<50
INFO: Query finished. [astroquery.utils.tap.core]
Obtained 91526 sources.
Grouping the stars by hpx level 12...
Grouping the stars by hpx level 11...
Grouping the stars by hpx level 10...
Grouping the stars by hpx level 9...
Grouping the stars by hpx level 8...
Done.
CPU times: user 1min 40s, sys: 263 ms, total: 1min 40s
Wall time: 1min 45s

The completeness is evaluated from the median magnitude of sources with fewer than 10 matched transits. By default, the map requires at least 20 such sources per spatial bin. If we lower this requirement we can make higher-resolution (but noisier) maps:

%%time
mapPatch_higherRes = DR3SelectionFunctionTCG( 'patch' , coordCenter , 1.5 , min_points=5)
Querying the Gaia archive...
SELECT ra, dec, source_id,phot_g_mean_mag
    FROM gaiadr3.gaia_source
    WHERE 1 = CONTAINS(POINT(ra,dec),CIRCLE(200.500000, -47.500000, 1.500000))
    and astrometric_matched_transits<11
    and phot_g_mean_mag<50
INFO: Query finished. [astroquery.utils.tap.core]
Obtained 91526 sources.
Grouping the stars by hpx level 12...
Grouping the stars by hpx level 11...
Grouping the stars by hpx level 10...
Grouping the stars by hpx level 9...
Grouping the stars by hpx level 8...
Done.
CPU times: user 1min 32s, sys: 282 ms, total: 1min 32s
Wall time: 1min 39s
plt.figure(figsize=(11,11))

completenessPatch = mapPatch.query(cc , gmag)
completenessPatch_higherRes = mapPatch_higherRes.query(cc , gmag)

plt.subplot(221)
plt.title('multi')
plt.scatter( xx , yy , c=completenessMultiOrder, s=10, vmin=0.,vmax=1 , cmap='viridis')
plt.xlabel('ra'); plt.ylabel('dec')
plt.xlim(203,198); plt.ylim(-49,-46)

plt.subplot(222)
plt.title('hpx7')
plt.scatter( xx , yy , c=completenessHpx7, s=10, vmin=0.,vmax=1 , cmap='viridis')
plt.xlabel('ra'); plt.ylabel('dec')
plt.xlim(203,198); plt.ylim(-49,-46)

plt.subplot(223)
plt.title('patch (default: min_points=20)')
plt.scatter( xx , yy , c=completenessPatch, s=10, vmin=0.,vmax=1 , cmap='viridis')
plt.xlabel('ra'); plt.ylabel('dec')
plt.xlim(203,198); plt.ylim(-49,-46)

plt.subplot(224)
plt.title('patch (min_points=5)')
plt.scatter( xx , yy , c=completenessPatch_higherRes, s=10, vmin=0.,vmax=1 , cmap='viridis')
plt.xlabel('ra'); plt.ylabel('dec')
plt.xlim(203,198); plt.ylim(-49,-46)

plt.suptitle('completeness at G=21');
Warning: some requested points are outside the map.
Warning: some requested points are outside the map.
../_images/74f04b22e55e289ec1f35464c0f23da9abf8e482deb59351803d7651c156560f.png

Comparing ‘multi’ and ‘patch’ in a very dense region#

The 'multi' map and the 'patch' (min_points=20) maps look very similar, because the multi-order map was computed for the entire sky with the requirement that healpix regions must have at least 20 sources to compute the completeness. However, it was only computed down to order 10, which means the 'patch' maps can have a finer resolution in dense regions.

clusterCentre = SkyCoord(ra=201.7,dec=-47.5,unit='deg',frame='icrs')
mapPatch_ngc5139 = DR3SelectionFunctionTCG( 'patch' , clusterCentre , 0.25 )
Querying the Gaia archive...
SELECT ra, dec, source_id,phot_g_mean_mag
    FROM gaiadr3.gaia_source
    WHERE 1 = CONTAINS(POINT(ra,dec),CIRCLE(201.700000, -47.500000, 0.250000))
    and astrometric_matched_transits<11
    and phot_g_mean_mag<50
INFO: Query finished. [astroquery.utils.tap.core]
Obtained 54274 sources.
Grouping the stars by hpx level 12...
Grouping the stars by hpx level 11...
Grouping the stars by hpx level 10...
Grouping the stars by hpx level 9...
Grouping the stars by hpx level 8...
Done.
#Define grid of points to evaluate completeness:
xx =[]
yy =[]
for rr in np.linspace( 201.4 , 202 , 100 ):
    for dd in np.linspace( -47.8 , -47.2 , 100 ):
        xx.append(rr)
        yy.append(dd)
cc = SkyCoord(ra=xx*u.degree, dec=yy*u.degree)

#Magnitude (same for all points here):
gmag = np.ones_like(cc) * 17

completenessMultiOrder = mapMulti.query( cc , gmag )
completenessPatch_ngc5139 = mapPatch_ngc5139.query( cc , gmag )

plt.figure(figsize=(11,5))

plt.subplot(121)
plt.title('multi')
plt.scatter( xx , yy , c=completenessMultiOrder, s=10, vmin=0.,vmax=1 , cmap='viridis')
plt.xlabel('ra'); plt.ylabel('dec')
plt.xlim(202,201.4); plt.ylim(-47.8,-47.2)

plt.subplot(122)
plt.title('patch')
plt.scatter( xx , yy , c=completenessPatch_ngc5139, s=10, vmin=0.,vmax=1 , cmap='viridis')
plt.xlabel('ra'); plt.ylabel('dec')
plt.xlim(202,201.4); plt.ylim(-47.8,-47.2)

plt.suptitle('completeness at G=17');
Warning: some requested points are outside the map.
../_images/aaf9b2830a0e9e1296b1ffe678373658fef6dbe9962d4c61449777373faafece.png

Evaluate the completeness along a line going through the cluster#

import numpy as np
from astropy import units as u
from astropy.coordinates import SkyCoord

ra = np.linspace(202.5,200.5,30)
dec = -47.45 * np.ones_like(ra)

cc = SkyCoord(ra=ra*u.degree, dec=dec*u.degree)


import matplotlib.pyplot as plt
plt.figure(0)
for g in [17,18,19,20,21,21.1,21.2]:
    completenessGCslice = mapPatch_higherRes.query(cc,g*np.ones_like(cc))
    plt.plot( cc.ra.degree , completenessGCslice , label='G = %.1f' % (g))
plt.legend(loc='upper right')
plt.xlim(max(cc.ra.degree),min(cc.ra.degree))
plt.xlabel('ra (degrees)')
plt.ylabel('completeness');
../_images/d8392d9bae30717d8f425605d5f69ede5bd07bd1f50d5cdffea1ad8496e9bfdc.png

More advanced: mapping \(M_{10}\)#

Here we show what is happening under the hood when a map is computed.

To be included in the Gaia catalogue, a source must have detected at least five times by Gaia. This number is given as astrometric_matched_transits in the Gaia DR3 source table. By looking at the magnitude of the sources with the smallest number of astrometric_matched_transits, we can make relatively good completeness predictions.

Our model predicts the completeness of a given patch of sky, based on the median G magnitude of the sources with ten or fewer astrometric matched transits. We call this computed quantity: \(M_{10}\)

Let’s take a look at the region around NGC 6752.

from astroquery.gaia import Gaia
Gaia.MAIN_GAIA_TABLE = "gaiadr3.gaia_source"
Gaia.ROW_LIMIT = -1  # default is 50 rows max, -1 for unlimited

ra = 288
dec = -60
size=2

scale = 1.0 / np.cos(np.radians(dec))
queryStringGaia = """SELECT ra, dec, source_id,phot_g_mean_mag
        FROM gaiadr3.gaia_source 
        WHERE ra>%.3f and ra<%.3f
        and dec>%.3f and dec<%.3f
        and astrometric_matched_transits<11
        and phot_g_mean_mag<50""" % (
            ra - scale * size / 2,
            ra + scale * size / 2,
            dec - size / 2,
            dec + size / 2,
        )

job = Gaia.launch_job_async(queryStringGaia)
GaiaT = job.get_results()
print(f"{len(GaiaT)} sources downloaded.")
INFO: Query finished. [astroquery.utils.tap.core]
15182 sources downloaded.
fig=plt.figure(0,figsize=(15,6))
ax=fig.add_subplot(121,aspect=scale)
plt.scatter( GaiaT['ra'] , GaiaT['dec'] , c=GaiaT['phot_g_mean_mag'] , vmin=20.5, vmax=21.2,s=5,cmap='turbo_r')
plt.xlabel('ra'); plt.ylabel('dec')
plt.xlim(max(GaiaT['ra']),min(GaiaT['ra']))
plt.ylim(min(GaiaT['dec']),max(GaiaT['dec']))
plt.colorbar(label='phot_g_mean_mag')
plt.title('all sources with\nastrometric_matched_transits <= 10')
ax.set_facecolor('black')

from scipy import stats
MAPm10, xedges, yedges, binnumber = stats.binned_statistic_2d( GaiaT['ra'], GaiaT['dec'] ,
                                                               GaiaT['phot_g_mean_mag'] ,
                                                               statistic=np.nanmedian, 
                                                               bins = 11 )
ax=fig.add_subplot(122, aspect=scale)
X, Y = np.meshgrid(xedges, yedges)
ax.pcolormesh(X, Y, MAPm10.T,cmap='turbo_r', vmin=20.5, vmax=21.2)
plt.xlim(max(GaiaT['ra']),min(GaiaT['ra']))
plt.ylim(min(GaiaT['dec']),max(GaiaT['dec']))
plt.title('median G magnitude of those sources')
Text(0.5, 1.0, 'median G magnitude of those sources')
../_images/b390c88926c0e2685ce6c4942c1ae82d66823eb2a1c624cfe4942eee06e2aa03.png

This map is not a completeness map yet (although \(M_{10}\) roughly correlates with the magnitude of 50% completeness). To convert \(M_{10}\) to a completeness at a given magnitude, use the function: m10_to_completeness.

from gaiaunlimited.selectionfunctions import m10_to_completeness

plt.figure(2,figsize=(8,8))

for i,g in enumerate([20.5,20.8,21,21.2]):
    plt.subplot(2,2,i+1,aspect=scale)
    plt.pcolormesh(X, Y, m10_to_completeness(g,MAPm10.T),cmap='viridis',vmin=0,vmax=1)
    plt.xlim(max(GaiaT['ra']),min(GaiaT['ra']))
    plt.ylim(min(GaiaT['dec']),max(GaiaT['dec']))
    if i in [0,1]:
        plt.xticks([])
    if i in [1,3]:
        plt.yticks([])
    plt.title(f'completeness at G={g}')
../_images/2bef476d4ae069c4e21bd9b2d063313e12c79b2ec265c3f0474883f6de8c5f8c.png

You can also use this to display the completeness as a function of magnitude in all pixels. Areas with values of \(M_{10}\) have a higher completeness.

cmap = plt.cm.turbo_r
norm = plt.Normalize(vmin=20.5, vmax=21.2)
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)


gtoplot = np.linspace(17.5,22,100)
plt.figure(3)
for m10 in MAPm10.flatten():
    plt.plot( gtoplot , m10_to_completeness(gtoplot,m10) , color=cmap(norm(m10)))
plt.xlabel('G magnitude')
plt.ylabel('completeness')
plt.colorbar(sm).set_label('$M_{10}$')



plt.figure(4)
for m10 in MAPm10.flatten():
    plt.plot( gtoplot , m10_to_completeness(gtoplot,m10) , color=cmap(norm(m10)))
plt.xlabel('G magnitude')
plt.ylabel('completeness')
plt.xlim(20.5,21.5)
plt.colorbar(sm).set_label('$M_{10}$')
/var/folders/k7/66g0jm190792cl33xj3stcy08wrsw2/T/ipykernel_89548/2322107820.py:12: MatplotlibDeprecationWarning: Unable to determine Axes to steal space for Colorbar. Using gca(), but will raise in the future. Either provide the *cax* argument to use as the Axes for the Colorbar, provide the *ax* argument to steal space from it, or add *mappable* to an Axes.
  plt.colorbar(sm).set_label('$M_{10}$')
/var/folders/k7/66g0jm190792cl33xj3stcy08wrsw2/T/ipykernel_89548/2322107820.py:22: MatplotlibDeprecationWarning: Unable to determine Axes to steal space for Colorbar. Using gca(), but will raise in the future. Either provide the *cax* argument to use as the Axes for the Colorbar, provide the *ax* argument to steal space from it, or add *mappable* to an Axes.
  plt.colorbar(sm).set_label('$M_{10}$')
../_images/efc521032072be923327d93c7ee480a1d8bc9618ea4f3226f453e5aaf3bdde29.png ../_images/2e06023d7a3f06e825cd12c6ac842dc2af0906fbef0b2f3c3cc4bbc5cabec98a.png

Accessing the \(M_{10}\) maps#

Internally, the maps are stored as numpy arrays of shape (n,3). The first element of each row is the healpix order. For instance, for the all-sky map in healpix order 7 we display at the beginning of this tutorial:

print(mapHpx7.m10map)
print(mapHpx7.m10map.shape)
[[7.00000e+00 0.00000e+00 2.10278e+01]
 [7.00000e+00 1.00000e+00 2.09157e+01]
 [7.00000e+00 2.00000e+00 2.09398e+01]
 ...
 [7.00000e+00 1.96605e+05 2.09378e+01]
 [7.00000e+00 1.96606e+05 2.11231e+01]
 [7.00000e+00 1.96607e+05 2.10904e+01]]
(196608, 3)

The maps made for patches use healpix order 12:

print(mapPatch.m10map)
print(mapPatch.m10map.shape)
[[1.20000000e+01 1.77010037e+08 2.10574417e+01]
 [1.20000000e+01 1.77010039e+08 2.10574417e+01]
 [1.20000000e+01 1.77010044e+08 2.10574417e+01]
 ...
 [1.20000000e+01 1.77160865e+08 2.08423157e+01]
 [1.20000000e+01 1.77160866e+08 2.08423157e+01]
 [1.20000000e+01 1.77160872e+08 2.08423157e+01]]
(34494, 3)
print(mapPatch_higherRes.m10map)
print(mapPatch_higherRes.m10map.shape)
[[1.20000000e+01 1.77010037e+08 2.10574417e+01]
 [1.20000000e+01 1.77010039e+08 2.10574417e+01]
 [1.20000000e+01 1.77010044e+08 2.10574417e+01]
 ...
 [1.20000000e+01 1.77160865e+08 2.08082047e+01]
 [1.20000000e+01 1.77160866e+08 2.08082047e+01]
 [1.20000000e+01 1.77160872e+08 2.08082047e+01]]
(34494, 3)

Since the maps are stored in a simple numpy array, they can be stored and loaded directly:

np.save( 'example_saved_map.npy' , mapPatch_higherRes.m10map )

new_map = DR3SelectionFunctionTCG() # on instantiation, loads all-asky hpx7 map
new_map.m10map = np.load('example_saved_map.npy')

# And replot:
xx =[]
yy =[]
for rr in np.linspace( 198 , 203 , 100 ):
    for dd in np.linspace( -49 , -46 , 100 ):
        xx.append(rr)
        yy.append(dd)
cc = SkyCoord(ra=xx*u.degree, dec=yy*u.degree)

#Magnitude (same for all points here):
gmag = np.ones_like(cc) * 21

completeness_new = new_map.query( cc , gmag )

plt.figure(figsize=(5,5))
plt.title('patch')
plt.scatter( xx , yy , c=completeness_new, s=10, vmin=0.,vmax=1 , cmap='viridis')
plt.xlabel('ra'); plt.ylabel('dec')
plt.xlim(203,198); plt.ylim(-49,-46)
Warning: some requested points are outside the map.
(-49.0, -46.0)
../_images/d63f360ce8cbe8c2fefb8f1c931ff15d3359b8af3ff06e80f74d43f914dd114e.png

Example of a custom-made adaptive binning with square bins#

Here we query the Gaia archive and make \(M_{10}\) maps of increasing resolution.

ra = 288
dec = -60
size = 4 #size of the square field of view, in degree
resMax = 0.002 #minimum bin size in degree
min_points = 10 #minimum number of points per bin

import numpy as np
import matplotlib.pyplot as plt
from scipy import stats

scale=1./np.cos(np.radians(dec))
from astroquery.gaia import Gaia
Gaia.MAIN_GAIA_TABLE = "gaiadr3.gaia_source"
Gaia.ROW_LIMIT = -1 #default is 50 rows max, -1 for unlimited
queryStringGaia = """SELECT ra, dec, phot_g_mean_mag
    FROM gaiadr3.gaia_source 
    WHERE ra>%.3f and ra<%.3f
    and dec>%.3f and dec<%.3f
    and phot_g_mean_mag<50
    and astrometric_matched_transits<11""" % (ra-scale*size/2,ra+scale*size/2,dec-size/2,dec+size/2)
job = Gaia.launch_job_async(queryStringGaia)
GaiaT = job.get_results()
print( f'{len(GaiaT)} sources downloaded.')
INFO: Query finished. [astroquery.utils.tap.core]
38485 sources downloaded.
import warnings
allHcounts = []
allHmedians = []

N=0
while size/2**N > resMax:
    print('Iteration',N,'bin size is',size/2**N)

    #we first count the sources so we can evaluate if our bins are too small:
    H, xedges, yedges = np.histogram2d(GaiaT['ra'], GaiaT['dec'], bins=2**N)
    #Then compute the median.
    #We anticipate a lot of empty bins, for which np.nanmedian will return a RuntimeWarning
    #so we catch those and don't print them to screen.
    with warnings.catch_warnings():
        warnings.simplefilter("ignore", category=RuntimeWarning)
        MAPm10, xedges, yedges, binnumber = stats.binned_statistic_2d( GaiaT['ra'], GaiaT['dec'] ,
                                                                       GaiaT['phot_g_mean_mag'] ,
                                                                       statistic=np.nanmedian, 
                                                                       bins = 2**N )

    MAPm10[H<min_points]=np.nan
    allHcounts.append( H )
    allHmedians.append( MAPm10 )

    N=N+1

#we use the highest available resolution:
finalMap = allHmedians[-1]
#we go through this map and find all empty pixels:
nbRows = len(finalMap)
nbCols = len(finalMap[0])

stepUp=1
while np.isnan(finalMap).any():
    print('Going up one level...',stepUp)
    for j in range(nbRows):
        for i in range(nbCols):
            if np.isnan(finalMap[j][i]):
                #print('replacing',finalMap[j][i],'with',allHmedians[-2][i//2][j//2])
                finalMap[j][i] = allHmedians[-1-1*stepUp][j//2**stepUp][i//2**stepUp]
    stepUp=stepUp+1

    
print('...done')
Iteration 0 bin size is 4.0
Iteration 1 bin size is 2.0
Iteration 2 bin size is 1.0
Iteration 3 bin size is 0.5
Iteration 4 bin size is 0.25
Iteration 5 bin size is 0.125
Iteration 6 bin size is 0.0625
Iteration 7 bin size is 0.03125
Iteration 8 bin size is 0.015625
Iteration 9 bin size is 0.0078125
Iteration 10 bin size is 0.00390625
Going up one level... 1
Going up one level... 2
Going up one level... 3
Going up one level... 4
Going up one level... 5
...done

We can now convert the \(M_{10}\) map to a completeness map at any magnitude.

from gaiaunlimited.selectionfunctions import m10_to_completeness

fig = plt.figure(figsize=(17,9))

ax=fig.add_subplot(231, aspect=scale)
X, Y = np.meshgrid(xedges, yedges)
plt.pcolormesh(X, Y, finalMap.T,cmap='turbo_r',vmin=16.5,vmax=21.2)
plt.colorbar(label='M10')
plt.xlim(max(GaiaT['ra']),min(GaiaT['ra']))
plt.ylim(min(GaiaT['dec']),max(GaiaT['dec']))
plt.xlabel('ra'); plt.ylabel('dec')

ax=fig.add_subplot(232, aspect=scale)
X, Y = np.meshgrid(xedges, yedges)
plt.pcolormesh(X, Y, m10_to_completeness(18,finalMap.T),cmap='viridis',vmin=0,vmax=1)
plt.colorbar(label='completeness at G=18')
plt.xlim(max(GaiaT['ra']),min(GaiaT['ra']))
plt.ylim(min(GaiaT['dec']),max(GaiaT['dec']))
plt.xlabel('ra'); plt.ylabel('dec')

ax=fig.add_subplot(233, aspect=scale)
X, Y = np.meshgrid(xedges, yedges)
plt.pcolormesh(X, Y, m10_to_completeness(21,finalMap.T),cmap='viridis',vmin=0,vmax=1)
plt.colorbar(label='completeness at G=21')
plt.xlim(max(GaiaT['ra']),min(GaiaT['ra']))
plt.ylim(min(GaiaT['dec']),max(GaiaT['dec']))
plt.xlabel('ra'); plt.ylabel('dec')

ax=fig.add_subplot(234, aspect=scale)
plt.title('zooming in on the cluster')
X, Y = np.meshgrid(xedges, yedges)
plt.pcolormesh(X, Y, finalMap.T,cmap='turbo_r',vmin=16.5,vmax=21.2)
plt.colorbar(label='M10')
plt.xlim(288.04,287.40)
plt.ylim(-60.15,-59.83)
plt.xlabel('ra'); plt.ylabel('dec')

ax=fig.add_subplot(235, aspect=scale)
X, Y = np.meshgrid(xedges, yedges)
plt.pcolormesh(X, Y, m10_to_completeness(18,finalMap.T),cmap='viridis',vmin=0,vmax=1)
plt.colorbar(label='completeness at G=18')
plt.xlim(288.04,287.40)
plt.ylim(-60.15,-59.83)
plt.xlabel('ra'); plt.ylabel('dec')

ax=fig.add_subplot(236, aspect=scale)
X, Y = np.meshgrid(xedges, yedges)
plt.pcolormesh(X, Y, m10_to_completeness(21,finalMap.T),cmap='viridis',vmin=0,vmax=1)
plt.colorbar(label='completeness at G=21')
plt.xlim(288.04,287.40)
plt.ylim(-60.15,-59.83)
plt.xlabel('ra'); plt.ylabel('dec')

plt.tight_layout()
../_images/4f3bf97061ecd4a9d0fc276d8692e51146efb9c98af64a4ebfd17791f34af291.png

Interactive map#

Note: the map will only be interactive if you run the notebook locally, not in the online (static) version.

This makes a zoomable map with a slider to change the magnitude.

The underlying MAPm10 we just created is a 1024 x 1024 array, so updating the figure is slow.

%matplotlib notebook
from matplotlib.widgets import Slider

fig=plt.figure(figsize=(7,8))
ax=plt.subplot(111,aspect=scale)
cMap = ax.pcolormesh(X, Y, m10_to_completeness(21,MAPm10.T),cmap='viridis',vmin=0,vmax=1)
plt.xlabel('ra (degrees)')
plt.ylabel('dec (degrees)')

plt.subplots_adjust(bottom=0.2)
ax_slider = plt.axes([0.25, 0.1, 0.65, 0.03])
slider_Gmag = Slider(ax_slider,"G magnitude",valmin=20.5,valmax=21.5,valinit=21,valstep=0.1)

def update(val):
    current_G = slider_Gmag.val
    newCompletenessMap = m10_to_completeness(current_G,MAPm10.T)
    print(current_G)
    ax.pcolormesh(X, Y, newCompletenessMap,cmap='viridis',vmin=0,vmax=1)
    fig.canvas.draw()
    
slider_Gmag.on_changed(update);