Libeskind et al. (2016) find that when you look around a close-ish pair of galaxies, you’ll find that their satellites are clumped in the space between them. I’d like to determine how large a sample I’ll need to observe this effect in another dataset and figure out how good photoZs need to be to do this with a sample of pairs selected without specZs.
Lopsided Satellite Distributions
Libeskind et al. (2016) find that when you look around a close-ish pair of galaxies, you’ll find that their satellites are clumped in the space between them.
I’d like to determine how large a sample I’ll need to observe this effect in another dataset and figure out how good photoZs need to be to do this with a sample of pairs selected without specZs. We’ll run a CASjobs query to select pairs of host galaxies that meet the pair criteria used by Libeskind et al. (2016), namely: * host magnitude: -23.5 < Mr < -21.5 * host separation 0.5 < dSep[Mpc] < 1.0 * magnitude difference of pair < 1 mag * spectroscopic redshift difference < 0.003
This query will do the job:
This sample we’ve selected includes pairs of galaxies that might belong to a bigger structure. Running a group finder and starting from a sample of 2-galaxy halos would give us the purest sample, but we can do alright by eliminating any pair in the sample that includes a galaxy that shows up more than once as either a primary or secondary pair member with this query:
Now we have a sample of unique pairs of galaxies meeting the pair criteria (no galaxy is a part of more than one pair.) We can now look for the satellites of the objects in each pair. We’ll use the GalaxyTag view and Photoz table to get position and redshift information for each potential satellite and select objects within 250 kpc of each host. Calculating all these distances is an expensive operation so it’ll be necessary to divide this query into chunks that can be completed within the maximum query time.
We’ll then transform their coordinates to get their positions relative to the line connecting the host pair. We’ll create separate tables for the primary and secondary hosts of each pair.
"""
.-.
.--.( ).--.
<-. .-.-.(.-> )_ .--.
`-`( )-' `) )
(o o ) `)`-'
( ) ,)
( () ) )
`---"\ , , ,/`
`--' `--' `--'
| | | |
| | | |
' | ' | AnneyA
"""
from __future__ import print_function, division
import numpy as np
from astropy import units as u
import matplotlib.pyplot as plt
from matplotlib import patches
from astropy.cosmology import WMAP9 as cosmo
from sklearn.neighbors import BallTree
import pandas as pd
pd.set_option('display.max_rows', 10)
from SciServer import CasJobs
from SciServer import LoginPortal
from io import StringIO
import urllib
plt.rc('font',family='serif')
# get access token
my_access_token = LoginPortal.getToken()
# get satellite data from CasJobs
#query_text = "SELECT * from rasalhague.anneya.satellites0_50__25_36DR11"
query_text = "SELECT * from mydb.satellites10_20__0_10DR11"
response = CasJobs.executeQuery(query_text, "mydb", token = my_access_token)
output = StringIO(response.read())
# put the query output in a pandas dataframe
sats1 = pd.read_csv(output)
sats1
specObjId | objId | z | zerr | ra | dec | cModelMag_r | specObjId2 | objId2 | z2 | zerr2 | ra2 | dec2 | cModelMag_r2 | sepPhysicalMpc | satObjId | satRa | satDec | d0 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 444883600110807040 | 1237663784203976830 | 0.110170 | 0.000023 | 15.035608 | 0.10439 | 17.01925 | 445951776210839552 | 1237666339188310081 | 0.109288 | 0.000029 | 15.026402 | -0.025662 | 17.05519 | 0.942928 | 1237663784203977141 | 15.004072 | 0.104298 | 0.228078 |
1 | 444883600110807040 | 1237663784203976830 | 0.110170 | 0.000023 | 15.035608 | 0.10439 | 17.01925 | 445951776210839552 | 1237666339188310081 | 0.109288 | 0.000029 | 15.026402 | -0.025662 | 17.05519 | 0.942928 | 1237663784203977140 | 15.004366 | 0.094372 | 0.237282 |
2 | 444883600110807040 | 1237663784203976830 | 0.110170 | 0.000023 | 15.035608 | 0.10439 | 17.01925 | 445951776210839552 | 1237666339188310081 | 0.109288 | 0.000029 | 15.026402 | -0.025662 | 17.05519 | 0.942928 | 1237663784203976881 | 15.004958 | 0.099941 | 0.223995 |
3 | 444883600110807040 | 1237663784203976830 | 0.110170 | 0.000023 | 15.035608 | 0.10439 | 17.01925 | 445951776210839552 | 1237666339188310081 | 0.109288 | 0.000029 | 15.026402 | -0.025662 | 17.05519 | 0.942928 | 1237663784203977646 | 15.005541 | 0.106520 | 0.217997 |
4 | 444883600110807040 | 1237663784203976830 | 0.110170 | 0.000023 | 15.035608 | 0.10439 | 17.01925 | 445951776210839552 | 1237666339188310081 | 0.109288 | 0.000029 | 15.026402 | -0.025662 | 17.05519 | 0.942928 | 1237663784203977148 | 15.006277 | 0.103348 | 0.212263 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
6639 | 1685610197671766016 | 1237663784740716774 | 0.187626 | 0.000009 | 14.796079 | 0.44305 | 18.03054 | 1219487904976365568 | 1237666339725050070 | 0.188833 | 0.000008 | 14.832151 | 0.371350 | 18.80173 | 0.907881 | 1237663784740717670 | 14.808726 | 0.442745 | 0.143093 |
6640 | 1685610197671766016 | 1237663784740716774 | 0.187626 | 0.000009 | 14.796079 | 0.44305 | 18.03054 | 1219487904976365568 | 1237666339725050070 | 0.188833 | 0.000008 | 14.832151 | 0.371350 | 18.80173 | 0.907881 | 1237663784740717681 | 14.812851 | 0.438051 | 0.197961 |
6641 | 1685610197671766016 | 1237663784740716774 | 0.187626 | 0.000009 | 14.796079 | 0.44305 | 18.03054 | 1219487904976365568 | 1237666339725050070 | 0.188833 | 0.000008 | 14.832151 | 0.371350 | 18.80173 | 0.907881 | 1237663784740717686 | 14.814525 | 0.434202 | 0.231407 |
6642 | 1685610197671766016 | 1237663784740716774 | 0.187626 | 0.000009 | 14.796079 | 0.44305 | 18.03054 | 1219487904976365568 | 1237666339725050070 | 0.188833 | 0.000008 | 14.832151 | 0.371350 | 18.80173 | 0.907881 | 1237663784740717432 | 14.815718 | 0.441924 | 0.222503 |
6643 | 1685610197671766016 | 1237663784740716774 | 0.187626 | 0.000009 | 14.796079 | 0.44305 | 18.03054 | 1219487904976365568 | 1237666339725050070 | 0.188833 | 0.000008 | 14.832151 | 0.371350 | 18.80173 | 0.907881 | 1237663784740717812 | 14.816672 | 0.446975 | 0.237118 |
6644 rows × 19 columns
# Since the satellite query was run in patches, get more or them with another call to mydb
#query_text = "SELECT * from rasalhague.anneya.satellites0_50__25_36DR11"
query_text = "SELECT * from mydb.satellites0_10__0_10DR11"
response = CasJobs.executeQuery(query_text, "mydb", token = my_access_token)
output = StringIO(response.read())
# put the query output in a pandas dataframe
sats2 = pd.read_csv(output)
sats = sats1.append(sats2,ignore_index=True)
sats
specObjId | objId | z | zerr | ra | dec | cModelMag_r | specObjId2 | objId2 | z2 | zerr2 | ra2 | dec2 | cModelMag_r2 | sepPhysicalMpc | satObjId | satRa | satDec | d0 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 444883600110807040 | 1237663784203976830 | 0.110170 | 0.000023 | 15.035608 | 0.104390 | 17.01925 | 445951776210839552 | 1237666339188310081 | 0.109288 | 0.000029 | 15.026402 | -0.025662 | 17.05519 | 0.942928 | 1237663784203977141 | 15.004072 | 0.104298 | 0.228078 |
1 | 444883600110807040 | 1237663784203976830 | 0.110170 | 0.000023 | 15.035608 | 0.104390 | 17.01925 | 445951776210839552 | 1237666339188310081 | 0.109288 | 0.000029 | 15.026402 | -0.025662 | 17.05519 | 0.942928 | 1237663784203977140 | 15.004366 | 0.094372 | 0.237282 |
2 | 444883600110807040 | 1237663784203976830 | 0.110170 | 0.000023 | 15.035608 | 0.104390 | 17.01925 | 445951776210839552 | 1237666339188310081 | 0.109288 | 0.000029 | 15.026402 | -0.025662 | 17.05519 | 0.942928 | 1237663784203976881 | 15.004958 | 0.099941 | 0.223995 |
3 | 444883600110807040 | 1237663784203976830 | 0.110170 | 0.000023 | 15.035608 | 0.104390 | 17.01925 | 445951776210839552 | 1237666339188310081 | 0.109288 | 0.000029 | 15.026402 | -0.025662 | 17.05519 | 0.942928 | 1237663784203977646 | 15.005541 | 0.106520 | 0.217997 |
4 | 444883600110807040 | 1237663784203976830 | 0.110170 | 0.000023 | 15.035608 | 0.104390 | 17.01925 | 445951776210839552 | 1237666339188310081 | 0.109288 | 0.000029 | 15.026402 | -0.025662 | 17.05519 | 0.942928 | 1237663784203977148 | 15.006277 | 0.103348 | 0.212263 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
16366 | 1737398584023738368 | 1237663716017438950 | 0.193247 | 0.000018 | 7.168378 | 0.444263 | 18.37083 | 1737399683535366144 | 1237663716017504520 | 0.192621 | 0.000025 | 7.219909 | 0.483397 | 18.48269 | 0.749309 | 1237663716017504699 | 7.183934 | 0.458505 | 0.244236 |
16367 | 1737398584023738368 | 1237663716017438950 | 0.193247 | 0.000018 | 7.168378 | 0.444263 | 18.37083 | 1737399683535366144 | 1237663716017504520 | 0.192621 | 0.000025 | 7.219909 | 0.483397 | 18.48269 | 0.749309 | 1237663716017505251 | 7.184719 | 0.444490 | 0.189253 |
16368 | 1737398584023738368 | 1237663716017438950 | 0.193247 | 0.000018 | 7.168378 | 0.444263 | 18.37083 | 1737399683535366144 | 1237663716017504520 | 0.192621 | 0.000025 | 7.219909 | 0.483397 | 18.48269 | 0.749309 | 1237663716017505491 | 7.187358 | 0.444616 | 0.219822 |
16369 | 1737398584023738368 | 1237663716017438950 | 0.193247 | 0.000018 | 7.168378 | 0.444263 | 18.37083 | 1737399683535366144 | 1237663716017504520 | 0.192621 | 0.000025 | 7.219909 | 0.483397 | 18.48269 | 0.749309 | 1237663716017505492 | 7.187483 | 0.451385 | 0.236109 |
16370 | 1737398584023738368 | 1237663716017438950 | 0.193247 | 0.000018 | 7.168378 | 0.444263 | 18.37083 | 1737399683535366144 | 1237663716017504520 | 0.192621 | 0.000025 | 7.219909 | 0.483397 | 18.48269 | 0.749309 | 1237663716017504500 | 7.189092 | 0.446392 | 0.241133 |
16371 rows × 19 columns
# Get the unique host pairs too
query_text = "select * from rasalhague.anneya.uniquePairsDR12NoNeighborsv2_0_50__25_36specphoto "+\
"where ra between 0 and 20 and dec between 0 and 10"
response = CasJobs.executeQuery(query_text, "mydb", token = my_access_token)
output = StringIO(response.read())
pairs = pd.read_csv(output)
pairs
specObjId | objId | z | zerr | ra | dec | cModelMag_r | specObjId2 | objId2 | z2 | zerr2 | ra2 | dec2 | cModelMag_r2 | sepPhysicalMpc | s | count1 | s2 | count2 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 444883600110807040 | 1237663784203976830 | 0.110170 | 0.000023 | 15.035608 | 0.104390 | 17.01925 | 445951776210839552 | 1237666339188310081 | 0.109288 | 0.000029 | 15.026402 | -0.025662 | 17.05519 | 0.942928 | 444883600110807040 | 1 | 445951776210839552 | 1 |
1 | 779216505828042752 | 1237663784739668171 | 0.132400 | 0.000027 | 12.359886 | 0.496592 | 17.42955 | 778156576585312256 | 1237663784739668202 | 0.132801 | 0.000035 | 12.312457 | 0.457162 | 17.60771 | 0.522924 | 779216505828042752 | 1 | 778156576585312256 | 1 |
2 | 779222828019902464 | 1237666340797874359 | 0.137143 | 0.000039 | 12.750498 | 1.185658 | 17.53605 | 1220599512423229440 | 1237666340797874288 | 0.136790 | 0.000042 | 12.679050 | 1.139000 | 17.66920 | 0.745362 | 779222828019902464 | 1 | 1220599512423229440 | 1 |
3 | 436953647541151744 | 1237663278465155352 | 0.094867 | 0.000008 | 1.242556 | 0.980047 | 16.56688 | 772460561966327808 | 1237663278465155357 | 0.095111 | 0.000022 | 1.249490 | 0.842499 | 17.07640 | 0.872652 | 436953647541151744 | 1 | 772460561966327808 | 1 |
4 | 438146617690843136 | 1237663784199520495 | 0.063957 | 0.000015 | 4.895507 | 0.200582 | 15.34856 | 774715655013296128 | 1237657191444185240 | 0.062766 | 0.000010 | 4.812110 | 0.289421 | 16.20651 | 0.539300 | 438146617690843136 | 1 | 774715655013296128 | 1 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
233 | 1225105041866647552 | 1237663278466924652 | 0.142192 | 0.000028 | 5.252280 | 0.877320 | 17.30590 | 1679968054525585408 | 1237657191981252773 | 0.143190 | 0.000007 | 5.279819 | 0.810610 | 18.27884 | 0.650048 | 1225105041866647552 | 1 | 1679968054525585408 | 1 |
234 | 1225115212349204480 | 1237663784199716989 | 0.158824 | 0.000011 | 5.268229 | 0.046061 | 17.74778 | 774695314048182272 | 1237657190907510865 | 0.158262 | 0.000006 | 5.234068 | -0.020846 | 18.37702 | 0.742118 | 1225115212349204480 | 1 | 774695314048182272 | 1 |
235 | 1685610197671766016 | 1237663784740716774 | 0.187626 | 0.000009 | 14.796079 | 0.443050 | 18.03054 | 1219487904976365568 | 1237666339725050070 | 0.188833 | 0.000008 | 14.832151 | 0.371350 | 18.80173 | 0.907881 | 1685610197671766016 | 1 | 1219487904976365568 | 1 |
236 | 1736232276997466112 | 1237663784199192920 | 0.139499 | 0.000029 | 4.175864 | 0.066316 | 17.55368 | 1735163001905899520 | 1237663784199192868 | 0.139257 | 0.000022 | 4.153729 | 0.176792 | 18.07148 | 0.998608 | 1736232276997466112 | 1 | 1735163001905899520 | 1 |
237 | 1737398584023738368 | 1237663716017438950 | 0.193247 | 0.000018 | 7.168378 | 0.444263 | 18.37083 | 1737399683535366144 | 1237663716017504520 | 0.192621 | 0.000025 | 7.219909 | 0.483397 | 18.48269 | 0.749309 | 1737398584023738368 | 1 | 1737399683535366144 | 1 |
238 rows × 19 columns
# Plot the pair hosts and the satellites of the primaries
plt.scatter(sats['satRa'], sats['satDec'], s=14, color='0.75', alpha=0.75, label='Satellites')
plt.scatter(sats['ra'], sats['dec'], s=8, color='red', label='Primary Hosts')
plt.scatter(sats['ra2'], sats['dec2'], s=8, color='black', label='Secondary Hosts')
# Plot a line connecting each pair
for p in pairs.itertuples():
plt.plot([p.ra,p.ra2],[p.dec,p.dec2],color='black',lw=1)
plt.gca().set_aspect('equal')
plt.xlim(0,20), plt.ylim(0,10)
plt.xlabel('ra'), plt.ylabel('dec')
plt.legend(scatterpoints=1, loc='upper right', fontsize=12, \
fancybox=True, shadow=True)
plt.gcf().set_size_inches(15,15)
plt.grid()
The dataframe now contains every satellite of the primary host of each galaxy pair. We need to transform their coordinates to determine their angle relative to the line connecting the pair.
# Add new coordinates for each satellite, moving the origin to the position of the primary host
sats['shiftRa'] = pd.Series(sats['satRa']-sats['ra'], index=sats.index)
sats['shiftDec'] = pd.Series(sats['satDec']-sats['dec'], index=sats.index)
# Rotate the x-axis to lie on the line connecting the host pair
sats['rotTheta'] = pd.Series(-1.*np.arctan2((sats['dec2']-sats['dec']),(sats['ra2']-sats['ra'])),index=sats.index)
sats['rotRa'] = np.cos(sats['rotTheta'])*sats['shiftRa'] - np.sin(sats['rotTheta'])*sats['shiftDec']
sats['rotDec'] = np.sin(sats['rotTheta'])*sats['shiftRa'] + np.cos(sats['rotTheta'])*sats['shiftDec']
# Calculate angle of each satellite relative to pair
sats['theta'] = pd.Series(np.arctan2(sats['rotDec'],sats['rotRa'])*180./np.pi,index=sats.index)
# Add position of each satellite relative to the host in physical units
sats['xPhys'] = pd.Series(sats['d0']*np.cos(sats['theta']), index=sats.index)
sats['yPhys'] = pd.Series(sats['d0']*np.sin(sats['theta']), index=sats.index)
sats['thetabs'] = pd.Series(np.abs(sats['theta']),index=sats.index)
pd.unique(sats[np.abs(sats['rotTheta']-0.5)<0.1]['objId'])
array([1237663784201748659, 1237669703199883407, 1237678661965906002,
1237663204919541845, 1237657192518451345, 1237669516368085111,
1237678662497796189, 1237669516906987707])
We can check that the transformation is working as expected:
testPair = sats[sats['objId']==pairs['objId'][1]]
#plt.scatter(testPair['shiftRa'],testPair['shiftDec'],color='blue',alpha=0.3,s=30,\
# label='Satellite positions before rotation')
plt.scatter(testPair['rotRa'],testPair['rotDec'],color='red',alpha=0.5,s=75,marker='*',\
label='Satellite positions after rotation')
plt.scatter([0],[0],color='0.4',s=60)
plt.scatter(testPair['ra2']-testPair['ra'],testPair['dec2']-testPair['dec'],color='0.4',s=60)
sep = np.array(np.sqrt((testPair['ra2']-testPair['ra'])**2+(testPair['dec2']-testPair['dec'])**2))
plt.scatter([sep],0.*testPair['ra2'],\
color='0.4',s=60,label=None)
rotAngle = 1.0*np.array(testPair['rotTheta'])[0]
if rotAngle<0:
t1 = 0.
t2 = np.abs(rotAngle)
else:
t1 = 2*np.pi-rotAngle
t2 = 0.
print(rotAngle*180/np.pi)
el = patches.Arc((0,0), 2*sep[0],2*sep[0], angle=0.0, theta2=180.*t2/np.pi, theta1=180.*t1/np.pi,lw=3,\
color='0.6',alpha=0.66)
plt.grid()
plt.gca().add_patch(el)
ps = []
for p in testPair.itertuples():
sep = np.sqrt(p.shiftRa**2 + p.shiftDec**2)
myTheta = np.arctan2(p.shiftDec,p.shiftRa)
if myTheta<0:
myTheta = 2*np.pi + myTheta
if rotAngle<0:
t2 = myTheta
t1 = myTheta + rotAngle
else:
t1 = myTheta
t2 = myTheta + rotAngle
ps.append(patches.Arc((0,0), 2*sep,2*sep, angle=0.0,theta2=180.*t2/np.pi, theta1=180.*t1/np.pi ,\
lw=2,color='purple',alpha=0.3))
for p in ps:
plt.gca().add_patch(p)
plt.gca().set_aspect('equal')
plt.gcf().set_size_inches(15,15)
plt.legend(loc='best',scatterpoints=1)