Exemplo n.º 1
0
def ferengi_downscale(image_low,z_low,z_high,pix_low,pix_hi,upscale=False,nofluxscale=False,evo=None):

    da_in = cosmos.angular_distance(z_low)
    da_out = cosmos.angular_distance(z_high)

    dl_in=da_in*(1+z_low)**2#cosmos.luminosity_distance(z_low)
    dl_out=da_out*(1+z_high)**2#cosmos.luminosity_distance(z_high)

    if evo is not None:
        evo_fact = evo(0.0,z_high)
    else:
        evo_fact=1.0

    mag_factor = (da_in/da_out)*(pix_low/pix_hi)
    if upscale == True:
        mag_factor=1./mag_factor

    lum_factor = (dl_in/dl_out)**2*(1.+z_high)/(1.+z_low)
    if nofluxscale==True:
        lum_factor=1.0

    N,M = image_low.shape

    N_out = int(round(N*mag_factor))
    M_out = int(round(M*mag_factor))

    img_out = rebin2d(image_low,N_out,M_out,flux_scale=True)

    return img_out*lum_factor*evo_fact
Exemplo n.º 2
0
def get_luminosity_image(image, z, pixscale):

    lum_dist = cosmos.luminosity_distance(z) * Mpc  #in cm
    ang_dist = cosmos.angular_distance(z) * Mpc  ##in cm

    pixel_flux = image / (np.pi * hst_rad**2) * (h * c / l_eff)
    pixel_lum = pixel_flux * 4 * np.pi * lum_dist**2
    pixel_area = (pixscale / (180 / np.pi * 3600) * (ang_dist) *
                  (1e6 / Mpc))**2  #in pc

    lum_density = pixel_lum / pixel_area / solar_lum  ## image in L_sun/s/pc^2

    return lum_density
Exemplo n.º 3
0
def ferengi_transformation_psf(psf_low,psf_high,z_low,z_high,pix_low,pix_high,same_size=None):
    """ Compute the transformation psf. Psf_low and psf_high are the low and high redshift PSFs respectively.
    Also needed as input paramenters the redshifts (low and high) and pixelscales (low and high).
    """
    psf_l = ferengi_psf_centre(psf_low)
    psf_h = ferengi_psf_centre(psf_high)

    da_in = cosmos.angular_distance(z_low)
    da_out = cosmos.angular_distance(z_high)

    N,M=psf_l.shape
    add=0

    out_size = round((da_in/da_out)*(pix_low/pix_high)*(N+add))

    while out_size%2==0:
        add+=2
        psf_l=np.pad(psf_l,1,mode='constant')
        out_size = round((da_in/da_out)*(pix_low/pix_high)*(N+add))
        if add>N*3:
            return -99
##            raise ValueError('Enlarging PSF failed!')

    psf_l = ferengi_downscale(psf_l,z_low,z_high,pix_low,pix_high,nofluxscale=True)
    psf_l = ferengi_psf_centre(psf_l)
# Make the psfs the same size (then center)
    psf_l,psf_h=ferengi_make_psf_same(psf_l,psf_h)


    psf_l = ferengi_psf_centre(psf_l)
    psf_h = ferengi_psf_centre(psf_h)

# NORMALIZATION
    psf_l/=np.sum(psf_l)
    psf_h/=np.sum(psf_h)
    return psf_l,psf_h,ferengi_psf_centre(ferengi_deconvolve(psf_h,psf_l))
Exemplo n.º 4
0
def get_luminosity_image(image, z, pixscale, segmap):

    lum_dist = cosmos.luminosity_distance(z) * Mpc  #in cm
    ang_dist = cosmos.angular_distance(z) * Mpc  ##in cm

    pixel_flux = image / (np.pi * hst_rad**2) * (h * c / l_eff)
    pixel_lum = pixel_flux * 4 * np.pi * lum_dist**2
    pixel_area = (pixscale / (180 / np.pi * 3600) * (ang_dist) *
                  (1e6 / Mpc))**2  #in pc

    fig, ax = mpl.subplots(1, 2, figsize=(22, 13))
    ax[0].hist((pixel_flux[(pixel_flux > 0) * (segmap != 0)]),
               bins=50,
               histtype='stepfilled',
               color='LightSeaGreen')
    fig.text(0.30,
             0.93,
             r"$\sum_{i} F_{i} = %.4e\ \mathrm{ergs\ s^{-1}cm^{-2}}$" %
             ((np.sum(pixel_flux[(pixel_flux > 0) * (segmap != 0)]))),
             fontsize=25,
             color='Teal',
             weight='bold',
             ha='center')
    ax[0].set_xlabel(r'$F_{i}\ [\mathrm{ergs\ s^{-1}cm^{-2}}]$')

    ax[1].hist(np.log10(pixel_lum[(pixel_lum > 0) * (segmap != 0)] /
                        solar_lum),
               bins=50,
               histtype='stepfilled',
               color='PowderBlue')
    fig.text(
        0.70,
        0.93,
        r"$\log_{10} \left[ \frac{\sum_{i} L_{i}}{L_\odot} \right] = %.4f$" %
        (np.log10(
            np.sum(pixel_lum[(pixel_lum > 0) * (segmap != 0)]) / solar_lum)),
        fontsize=25,
        color='SteelBlue',
        weight='bold',
        ha='center')
    ax[1].set_xlabel(r'$\log_{10} \left(L_{i}/L_\odot\right)$')

    fig.canvas.mpl_connect('key_press_event', exit_code)

    lum_density = pixel_lum / pixel_area / solar_lum  ## image in L_sun/s/pc^2
    return lum_density
Exemplo n.º 5
0
            else:
                if os.path.isfile('psf.fits'):
                    sp.call('rm psf.fits',shell=True)
                focus_value = get_focus(args.focus,tilename)
                psf_dir='/'.join(match_tiles.split('/')[:-1])+'/PSFs'
                xc,yc=gfh.get_center_coords(imgname,RA[i],DEC[i])
                select_PSF(ID[i],psf_dir,focus_value,xc,yc)

        if args.fixthresh:
            t = args.threshold
        else:
            sigma_0=args.sigma0
            t = sigma_0 * ((1+Z[i])/(1+4.0))**(-3)
        galaxy_sizes,segflag = compute_size(imgname,RA[i],DEC[i],Mag[i],hsize,t,fractions,sblim)

        da=cosmos.angular_distance(Z[i],pars=None)*1000
        galaxy_physic_sizes = np.array([(N*(args.pixscale**2)*(180/np.pi*3600)**(-2)*(da**2)) for N in galaxy_sizes])
        galaxy_physic_sizes[galaxy_physic_sizes<0] = -99.0

        if args.verbose:
            print(""" threshold = %8.4f @ z = %.3f d_a=%.3f Mpc)
            Pixel  Sizes %s
            Physic Sizes %s
            Segmentation Flag %i
            """%(t,Z[i],da/1000.,"\t".join(["%10i"%(s) for s in galaxy_sizes]),"\t".join(["%10.5f"%(s) for s in galaxy_physic_sizes]) ,segflag ))

        if not args.nosaving:
            table_out.write("%10i\t%12.8f\t%12.8f\t%5.3f\t%2i\t"%(ID[i],RA[i],DEC[i],Z[i],Zflag[i]))
            table_out.write("\t".join(["%10.5f"%(s) for s in galaxy_sizes]))
            table_out.write("\t")
            table_out.write("\t".join(["%10.5f"%(s) for s in galaxy_physic_sizes]))
Exemplo n.º 6
0
def find_pairs_and_clumps(image_stamp,redshift,galmag,color,hsize,threshold,fractions,sblimit,pixelscale,zeropoint,ksky=3.0,Areamin=10,Aperture=0.5,no_dilation=True,degrade=None,size=5,safedist=1.0,title=None,plot_results=False,segmap_output=False,erosion=[3],verbose=False,ident=None,zphot_sel=None):


    if np.amax(image_stamp)==np.amin(image_stamp):
        if verbose:
            print("Invalid data values: %.4f,%.4f"%(np.amax(image_stamp),np.amin(image_stamp)))
        return {}

    dilate = define_structure(size)
    sky_med,sky_std = mi.sky_value(image_stamp,ksky)


    if degrade is not None:
        N,M=image_stamp.shape
        image_stamp = mi.rebin2d(image_stamp,int(N/degrade),int(M/degrade),flux_scale=True)
        pixelscale*=degrade


    if no_dilation:
        image_smooth = mi.sci_nd.gaussian_filter(image_stamp,sigma=1.0)


    if args.error:
        factor=-1.0
    else:
        factor=1.0

    if np.abs(color)<10:
        corrected_thresh = color_correction(threshold,color)
        new_sblimit = corrected_thresh*sblimit
#        if verbose:
#            print("K-I=%.4f\t old sb limit = %.5f\t new sb limit = %.5f counts/s/arcsec**2"%(color,threshold*sblimit,new_sblimit))
        threshold=corrected_thresh
    elif np.abs(color)>10:
        if verbose:
            print("Invalid color value: %.4f"%color)
        return {}

    segmap = mi.gen_segmap_sbthresh(factor*(image_smooth-sky_med),hsize,hsize,sblimit,pixelscale,thresh=threshold,Amin=Areamin,all_detection=True)
    single_source_map = mi.select_object_map_connected(hsize,hsize,factor*image_smooth,segmap,pixscale=pixelscale,radius=Aperture)
    image_smooth,single_source_map,imglag,segflag = mi.image_validation(image_smooth,single_source_map,pixelscale,safedist)

#    fig,ax=mpl.subplots(1,3,figsize=(25,10))
#    ax[0].imshow(image_smooth)
#    ax[1].imshow(segmap)
#    ax[2].imshow(single_source_map)
#    fig.canvas.mpl_connect('key_press_event',exit_code)
#    mpl.show()

    if no_dilation:
        dilated_map = single_source_map
    else:
        dilated_map = mi.sci_nd.binary_dilation(single_source_map,structure=dilate).astype(np.int32)


    gal_selection,gal_magnitudes = galaxy_map(image_stamp,segmap,zeropoint,sky_med,factor)
    ngals=np.amax(gal_selection)
#


    FullSet={}
    if verbose:
        print('Ngals=%i'%ngals)

    for i in range(ngals):
        GalPositionsBar={}
        GalPositionsMax={}
        GalMags={}
        GalDistances={}
        GalSizes={}

        single_gal_map=gal_selection.copy()
        single_gal_map[gal_selection!=(i+1)]=0
        single_gal_map[gal_selection==(i+1)]=1

#        fig,ax=mpl.subplots(1,4,figsize=(25,12))
#        ax[0].imshow(factor*image_smooth,vmin=0)
#        ax[1].imshow(segmap)
#        ax[2].imshow(gal_selection)
#        ax[3].imshow(single_gal_map)
#        fig.canvas.mpl_connect('key_press_event',exit_code)
#        mpl.show()


        nregs=[]
        nregs2=[]
#        fractions = [0.2,1.0]#np.linspace(0,1,101)
        Xcen,Ycen=mi.barycenter(factor*image_stamp,single_gal_map)


        if zphot_sel is None:
            prob_dist = 1.0
            prob_dist_area = 1.0
        else:
            GalDists = mi.dist(Ycen,Xcen,zphot_sel[:,0],zphot_sel[:,1])
            kmin = np.argmin(GalDists)
            dmin = GalDists[kmin]

            vpair=500. #km/s
            redshift_error = np.sqrt( vpair*vpair + 200*200. + 20000*20000.)/2.9979e5*(1+redshift)

            if dmin < 1.0/pixelscale:
                z,zl,zu = zphot_sel[kmin,2],zphot_sel[kmin,3],zphot_sel[kmin,4]
                prob_dist = probability_zphot(z,zl,zu,redshift-redshift_error,redshift+redshift_error)
            else:
                prob_dist = -99

            if gal_magnitudes[i]<MagCapak[0]:
                prob_dist_area=0.0
            else:
                NCounts = simps(NumCapak[MagCapak<gal_magnitudes[i]],MagCapak[MagCapak<gal_magnitudes[i]])
                radius = mi.dist(Ycen,Xcen,hsize,hsize)*pixelscale
                prob_dist_area = max(1-NCounts/(3600.*3600.)*radius*radius*np.pi,0)

        for f in fractions:
            S = get_segmap_level(factor*image_smooth,single_gal_map,f)

            clump_map_full,nr= mi.sci_nd.label(S)
            clump_map_clean,nr2 = clean_map(clump_map_full,minarea=Areamin)
#            fig,ax=mpl.subplots(1,2)
#            ax[0].imshow(clump_map_full)
#            ax[1].imshow(clump_map_clean)
#            mpl.show()

            M,Pb,Pm,Sc=get_clump_stats(factor*image_stamp,clump_map_clean,zeropoint)
            GalMags[str(f)]=M
            GalPositionsBar[str(f)]=Pb
            GalPositionsMax[str(f)]=Pm
            GalSizes[str(f)]=Sc

            nregs.append(nr)
            nregs2.append(nr2)

        for f in fractions:
            FP = GalPositionsBar[str(f)]
            nclumps=np.shape(FP)[0]

            DistsSingle=np.zeros(nclumps)
            for n in range(nclumps):
                DistsSingle[n] = pixelscale*np.sqrt((FP[n,0]-Xcen)*(FP[n,0]-Xcen)+(FP[n,1]-Ycen)*(FP[n,1]-Ycen))

            GalDistances[f]=DistsSingle

            if verbose:
                print('\t %i ----> f=%.2f \t nclumps=%i'%(i,f,nclumps))

        if verbose:
            print(50*'=')


        FullSet[i+1]={}
        FullSet[i+1]['weight']=[prob_dist,prob_dist_area]
        FullSet[i+1]['mags']=GalMags
        FullSet[i+1]['posibar']=GalPositionsBar
        FullSet[i+1]['posimax']=GalPositionsMax
        FullSet[i+1]['dist']=GalDistances
        FullSet[i+1]['size']=GalSizes



##    Real_Sizes  = Sizes - SizesPSF

    if plot_results:
        print("mag_cat=",galmag)
        print('sky median = %.5f +- %.6f'%(sky_med,sky_std))
        print("sky_threshold = %.8f (sigma = %.8f)"%(sblimit*threshold,threshold))
        print("Redshift = %.4f"%redshift)

#        mpl.rcParams['image.cmap']='gist_stern_r'
#        mpl.rcParams['axes.labelsize']=12
#        mpl.rcParams['xtick.labelsize']=10
#        mpl.rcParams['ytick.labelsize']=10

#        rad,flux,xc,yc,q,theta = compute_sbprofile(image_smooth-sky_med,single_source_map,pixelscale)
#        radPSF,fluxPSF,xcPSF,ycPSF,qPSF,thetaPSF = compute_sbprofile(psf_image-sky_med,single_source_map_psf,pixelscale)

#==============================================================================
# PAPER FIGURE
#==============================================================================
        sidecut=40
        import matplotlib.colors as mpc
        import matplotlib.cm as cm
#        from sklearn.cluster import MeanShift, estimate_bandwidth
#        from sklearn.datasets.samples_generator import make_blobs
#
#        centers = [[1, 1], [-1, -1], [1, -1]]
#        X, _ = make_blobs(n_samples=10000, centers=centers, cluster_std=0.6)


        figS,axS=mpl.subplots()
        dmax = 20/(cosmos.angular_distance(redshift)/(180/np.pi*3600)*1000)

        new_image = (factor*image_stamp)
        hsize_new = new_image.shape[0]/2

        if 'Hband' in args.image:
            CMAP='Reds_r'
        elif 'Iband' in args.image:
            CMAP='YlGnBu_r'
        else:
            CMAP='viridis'

        VMAX = np.amax(new_image[hsize_new-15:hsize_new+15,hsize_new-15:hsize_new+15])
        axS.imshow(np.abs(new_image),cmap=CMAP,extent=(-hsize_new*pixelscale,hsize_new*pixelscale,-hsize_new*pixelscale,hsize_new*pixelscale),vmax=VMAX)

        mi.gen_circle(axS,0,0,dmax,color='white',lw=3)
        colors = ['gold','lime','red','cyan','cyan',\
        'orange','blue','black','yellow','magenta',\
        'brown','gray','Olive','OrangeRed','Coral','Yellow','Magenta','Thistle',\
        'SpringGreen','Turquoise','RosyBrown','Silver','SlateGray','Black',\
        'Aquamarine','LimeGreen','PeachPuff','Lavender','MediumOrchid']


        P=np.array([])
        for k in range(ngals):

            single_gal_map=gal_selection.copy()
            single_gal_map[gal_selection!=(k+1)]=0
            single_gal_map[gal_selection==(k+1)]=1

            gps,nc = detect_all_clumps(FullSet[k+1],np.arange(0.1,0.8,0.1))
            P=np.append(P,gps)

            Xcen,Ycen=mi.barycenter(new_image,single_gal_map)
            GalDists = mi.dist(Ycen,Xcen,zphot_sel[:,0],zphot_sel[:,1])
            kmin = np.argmin(GalDists)
            dmin = GalDists[kmin]



#            vpair=500. #km/s
#            z_search = vpair/2.9979e5*(1+redshift)
#            z_spec_err = 0.0017*(1+redshift)
#            z_phot_err = 0.1*(1+redshift)
#            redshift_error = np.sqrt( z_search*z_search + z_spec_err*z_spec_err + z_phot_err*z_phot_err)
            vpair=500. #km/s
            redshift_error = np.sqrt( vpair*vpair + 200*200. + 20000*20000.)/2.9979e5*(1+redshift)

            if dmin < 1.0/pixelscale:
                z,zl,zu = zphot_sel[kmin,2],zphot_sel[kmin,3],zphot_sel[kmin,4]
                prob_dist = probability_zphot(z,zl,zu,redshift-redshift_error,redshift+redshift_error,plot_results=True)
            else:
                prob_dist = -99

            print('p(z), on this run',prob_dist)

            X = pixelscale*(gps[:,1]-hsize_new+0.5)
            Y = pixelscale*(gps[:,0]-hsize_new+0.5)
            axS.plot(X,Y,'o',mfc='none',mec=colors[k],ms=20,mew=3)

            S = get_segmap_level(factor*image_smooth,single_gal_map,1.0)
#            mi.draw_border(axS,S,colors[k],extent=(-hsize_new*pixelscale,hsize_new*pixelscale,-hsize_new*pixelscale,hsize_new*pixelscale))
            NDS = define_structure(3)
            axS.contour(mi.sci_nd.binary_dilation(S,structure=NDS).astype(np.int),levels=[0.5],colors=colors[k],linewidths=3.0,extent=(-hsize_new*pixelscale,hsize_new*pixelscale,-hsize_new*pixelscale,hsize_new*pixelscale))


        for eixo in [axS]:
            for t in eixo.xaxis.get_ticklines():
                t.set_color('white')
            for t in eixo.yaxis.get_ticklines():
                t.set_color('white')
            for side in ['left','top','bottom','right']:
                eixo.spines[side].set_color('white')

        P = P.reshape([len(P)/2,2])
        ZF=1.30
        axS.set_xlim(-hsize_new*pixelscale/ZF,hsize_new*pixelscale/ZF)
        axS.set_ylim(-hsize_new*pixelscale/ZF,hsize_new*pixelscale/ZF)
        axS.set_xlabel(r'$\Delta \alpha\ [\mathrm{arcsec}]$')
        axS.set_ylabel(r'$\Delta \delta\ [\mathrm{arcsec}]$')
#        figS.savefig('clumps_first_pass_SingleGalaxy.png')

        if args.zphot:
            fig2,ax2=mpl.subplots()
            for element in zphot_sel:
                xc,yc=element[:2]
                ax2.plot(xc,yc,'s',mfc='none',mec='k',markersize=12)

            for k in range(ngals):
                gps,nc = detect_all_clumps(FullSet[k+1],np.arange(0.1,0.8,0.1))
                Weights = FullSet[k+1]['weight']
                X = gps[:,1]
                Y = gps[:,0]
                print(X,Y,Weights)




                ax2.plot(X,Y,'o',mfc='none',mec=colors[k],ms=20,mew=3)

#        figS.savefig('../ClumpyGalaxies/images/clumps_example_SingleGalaxy.pdf')
#        figS.savefig('../ClumpyGalaxies/images/clumps_example_SingleGalaxy.png')



        fig,ax=mpl.subplots(ngals,3,figsize=(20,18))
        ax=ax.reshape(np.size(ax))
        mpl.subplots_adjust(wspace=0)
        jetcmap = cm.get_cmap('YlGnBu_r', 10) #generate a jet map with 10 values
        jet_vals = jetcmap(np.arange(10)) #extract those values as an array
        jet_vals[0] = [0., 0, 0., 0.] #change the first value
#        new_jet = mpc.LinearSegmentedColormap.from_list("newjet", jet_vals)




        for i in range(ngals):

            axnum=i*3

            single_gal_map=gal_selection.copy()
            single_gal_map[gal_selection!=(i+1)]=0
            single_gal_map[gal_selection==(i+1)]=1

            ax[axnum+1].imshow(new_image,cmap=CMAP,extent=(-hsize_new*pixelscale,hsize_new*pixelscale,-hsize_new*pixelscale,hsize_new*pixelscale))
            ax[axnum+2].imshow(gal_selection,cmap='viridis',extent=(-hsize_new*pixelscale,hsize_new*pixelscale,-hsize_new*pixelscale,hsize_new*pixelscale))

            nregs=[]
            nregs2=[]
#            fractions = [0.2,0.51.0]#np.linspace(0,1,101)
            for f in fractions:
                S = get_segmap_level(image_smooth,single_gal_map,f)

                clump_map_full,nr= mi.sci_nd.label(S)
                clump_map_clean,nr2 = clean_map(clump_map_full,minarea=5)
                M,P,Pm,Sc=get_clump_stats(factor*image_stamp,clump_map_clean,zeropoint)
                nregs.append(nr)
                nregs2.append(nr2)

            Xcen,Ycen=mi.barycenter(factor*image_stamp,single_gal_map)
            ax[axnum+1].set_ylim((Xcen-hsize_new-50)*pixelscale,(Xcen-hsize_new+50)*pixelscale)
            ax[axnum+1].set_xlim((Ycen-hsize_new-50)*pixelscale,(Ycen-hsize_new+50)*pixelscale)

            mi.gen_circle(ax[axnum+2],(Ycen-hsize_new)*pixelscale,(Xcen-hsize_new)*pixelscale,0.75,color='cyan',lw=2)
#            ax[axnum+2].set_ylim((Xcen-hsize_new-50)*pixelscale,(Xcen-hsize_new+50)*pixelscale)
#            ax[axnum+2].set_xlim((Ycen-hsize_new-50)*pixelscale,(Ycen-hsize_new+50)*pixelscale)


            ax[axnum].plot(np.array(fractions),nregs,'s-',color='DarkRed',label='All Disconnected')
            ax[axnum].plot(np.array(fractions),nregs2,'o-',color='Navy',label='A>10pix Disconnected')

            for f,c in zip([0.2,0.5,1.0],['red','lime','cyan','gold']):
                S = get_segmap_level(image_smooth,single_gal_map,f)
                labelmap,nr= mi.sci_nd.label(S)
#                ax[axnum+1].hlines(pixelscale*f*((new_image.shape[0]-2*sidecut)/2),-2,-1,color=c,lw=3)
#                ax[axnum+1].text(-1.5,pixelscale*f*((new_image.shape[0]-2*sidecut)/2),r'$f=%.2f$'%f,color=c,fontsize=12,va='bottom',ha='center')
                ax[axnum].vlines(f,0,nr,color=c,lw=3)
                mi.draw_border(ax[axnum+1],S,c,extent=(-hsize_new*pixelscale,hsize_new*pixelscale,-hsize_new*pixelscale,hsize_new*pixelscale))
#                NDS = define_structure(3)
#                ax[axnum].contour(mi.sci_nd.binary_dilation(S,structure=NDS).astype(np.int),levels=[0.5],colors=c,linewidths=3.0,extent=(-hsize_new*pixelscale,hsize_new*pixelscale,-hsize_new*pixelscale,hsize_new*pixelscale))


#        for eixo in ax[1:]:
#            mi.gen_circle(eixo,hsize_new,hsize_new,(2*hsize/args.size)*args.aperture,color='red')
#            eixo.tick_params(labelleft='off',labelbottom='off')


            ax[axnum].hlines(1,0,1.1,'k',':')
            ax[axnum].set_xlim(0,1.1)
            ax[axnum].set_ylim(0,1.4*max(nregs))
            ax[axnum].set_xlabel(r'$f$')
            ax[axnum].set_ylabel(r'$N_c$')

        ax[0].legend(loc='best')
#        fig.savefig('clumps_first_pass.png')
        fig.canvas.mpl_connect('key_press_event',exit_code)
        mpl.show()
        sys.exit()

#==============================================================================
# END PAPER FIGURE
#==============================================================================
    return FullSet
Exemplo n.º 7
0
def rescale_rexc(r_exc,z_low,z_high):
    d_low = cosmos.angular_distance(z_low)
    d_high = cosmos.angular_distance(z_high)    
    return r_exc*(d_low/d_high)