default=30)

args = parser.parse_args()
a = args.a
e = args.ecc
lN = args.log2N
r_soft = args.r_soft
delta_Rm = args.dRm
M_PBH = args.MPBH

#Number of DM particles per halo
nDM_inner = 2**lN

L_sim = 1e-5

edd.loadDistribution(M_PBH, a)
#M_PBH = edd.M_PBH
r_eq = edd.r_eq

#print "  Generating ICs with %d DM pseudo-particles per PBH..."%nDM

#a = 10
#e = 0.5
apo = (1 + e) * a

#Calculate the truncation radius (just for information)
rdat = np.loadtxt("distributions/Decoupling_M=" + str(int(M_PBH)) + "Msun.txt")
r_interp = interp1d(rdat[:, 0], rdat[:, 1], kind='linear')
r_tr = r_interp(a)

print "  Semi-major axis a (pc):", a
                    help='Set flag 1 to fix the DM particle mass to zero',
                    type=int,
                    default=0)

args = parser.parse_args()

nDM = args.N_DM
d0 = args.d0
M_BH = args.M_BH
NO_DM = args.NO_DM

# Some baseline parameters which we always seem to keep fixed:
rho0 = 226.0  #Characteristic DM density in M_sun/pc^3
gamma = 7.0 / 3.0  #Power-law slope

edd.loadDistribution(M_BH, rho0, gamma)

if (NO_DM):
    M1 = M_BH
else:
    M1 = edd.Menc(d0)

M2 = args.M_NS

print("  System properties (binary):")

print("    M_BH1 [M_sun]:", M_BH)
print("    M_BH2 [M_sun]:", M2)
print("    Initial separation [pc]:", d0)
print("    Orbital period [s]:",
      2 * np.pi * np.sqrt(d0**3 * (units.L_pc)**2 / (units.G_N * M_BH)))
Esempio n. 3
0
def AddDressedBH(x0, v0, M_BH, rho0, gamma, nDM = 100, verbose=False, haloID=None):
    """Add a dressed PBH to the initial conditions...
    
    Parameters:
        x0     - initial position of BH (in pc)
        v0     - initial velocity of BH+DM halo (in km/s)
        M_BH   - BH mass in M_sun
    
        rho0   - characteristic spike density in M_sun/pc^3
        gamma  - spike power-law slope
    
        nDM    - number of DM particles around this BH

        haloID - string identifying a text file to load the halo from (in folder /halos)
                 If file not found, a new halo is generated and saved in /halos.
                 Set haloID = None (default) to ignore this option.
    """
 
    #For now, don't edit this:
    fix_CoM = False

    #BH mass and truncation radius imported from the eddington file for
    #self-consistency
    edd.loadDistribution(M_BH, rho0, gamma)
    

    r_isco = 6*M_BH*units.G_N/units.C_LIGHT**2
    
    #Minimum and maximum radius to initialise particles down to
    r_min = r_isco
    r_max = 50*edd.r_sp 
    
    
    mHalo = edd.Menc(r_max) - edd.Menc(r_min)

    #Initialise the masses, positions and velocities
    m_vals = np.zeros(nDM + 1)
    pos_vals = np.zeros((nDM + 1,3))
    vel_vals = np.zeros((nDM + 1,3))
    
    #Set up the central black hole
    m_vals[0] = M_BH
    pos_vals[0,:] = np.zeros(3)
    vel_vals[0,:] = np.zeros(3)
    
    
    #PBH position and velocity (before CoM velocity is subtracted...)
    xPBH=np.array([0.,0.,0.])
    vPBH=np.array([0.,0.,0.])
    
    if (haloID is not None):
        halofile = "halos/" + haloID + ".txt"
        
    #Check to see whether a halo file already exists...
    if ((haloID is not None) and os.path.isfile("halos/" + haloID + ".txt")):
        print("    Loading halo from file. HaloID:", haloID)
        
        #Load DM phase space coordinates from file
        halo_data = np.loadtxt(halofile)

        nStored = len(halo_data)
        #print(nStored)
        assert nStored >= nDM
        
        inds = np.random.choice(nStored, size=nDM, replace=False)
        xvals = halo_data[inds,0]
        yvals = halo_data[inds,1]
        zvals = halo_data[inds,2]

        pos_vals[1:,:] = np.array([xvals, yvals, zvals]).T
        
        vxvals = halo_data[inds,3]
        vyvals = halo_data[inds,4]
        vzvals = halo_data[inds,5]
        
        vel_vals[1:,:] = np.array([vxvals, vyvals, vzvals]).T

        m_vals[1:] = mHalo/nDM

    else:
        if (haloID is not None):
            print(("   Halo file <" + halofile+"> not found. Generating from scratch..."))
    
        #Generate the mass profile
        print("    Generating mass profile...")
    
        rlist = np.logspace(np.log10(r_min), np.log10(r_max), 1000)
        #rlist = np.logspace(np.log10(r_isco), np.log10(r_max), 1000)
        Menc = 0.0*rlist

        Min = edd.Menc(r_min)
        for i in range(len(rlist)):
            Menc[i] = edd.Menc(rlist[i]) - Min
            
        Menc -= Menc[0]
        M_max = Menc[-1]

        M_interp = interp1d(Menc/M_max, rlist, kind='linear')
        
        #DM positions        
        rvals = M_interp(np.random.rand(nDM))
        
        #Generate some random directions for setting particle positions
        ctvals = 2.0*np.random.rand(nDM) - 1.0
        thetavals = np.arccos(ctvals)
        phivals = 2*np.pi*np.random.rand(nDM)

        xvals = rvals*np.cos(phivals)*np.sin(thetavals)
        yvals = rvals*np.sin(phivals)*np.sin(thetavals)
        zvals = rvals*np.cos(thetavals)

        pos_vals[1:,:] = np.array([xvals, yvals, zvals]).T
        m_vals[1:] = mHalo/nDM
 
        
        #Generate velocities
        vvals = np.zeros(nDM)
        for ind in tqdm(range(nDM), desc="    Sampling velocities..."):
            count = 0
            r = rvals[ind]
            #Now sample f(v) at given r to get the speed v
            found = 0
    
            while (found == 0):
                count += 1
                if (count > 100000):
                    print("Velocity sampling failed at r = ", r)
                    sys.exit()

                #Rejection sampling for the velocities
                v = np.random.rand(1)*edd.vmax(r)
                #Use 5.0/vmax as the 'maximum' values of f(v)
                #but in some cases it might not be enough...
                ratio = 5.0
                if (np.random.rand(1)*(ratio/edd.vmax(r)) < edd.f(r, v)):
                    found = 1
                    vvals[ind] = v

        #Get a new set of random directions for the velocities
        ctvals2 = 2.0*np.random.rand(nDM) - 1.0
        thetavals2 = np.arccos(ctvals2)
        phivals2 = 2*np.pi*np.random.rand(nDM)

        vxvals = vvals*np.cos(phivals2)*np.sin(thetavals2)
        vyvals = vvals*np.sin(phivals2)*np.sin(thetavals2)
        vzvals = vvals*np.cos(thetavals2)


        vel_vals[1:,:] = np.array([vxvals, vyvals, vzvals]).T

    
        #Save the output to a halo file if needed
        if (haloID is not None):
            headertxt = "Number of DM particles: " + str(nDM)
            headertxt += "\nColumns: x [pc], y [pc], z [pc], vx [km/s], vy [km/s], vz [km/s]"
            np.savetxt("halos/" + haloID + ".txt", list(zip(pos_vals[1:,0],pos_vals[1:,1],pos_vals[1:,2],vel_vals[1:,0],vel_vals[1:,1],vel_vals[1:,2])), header=headertxt)
    
        
    #Subtract off net position and momentum of system
    if (fix_CoM):
        #Deal with C.o.M.    
        totmass = np.sum(m_vals)
        #print("    Total Mass [Halo + BH]:", totmass)
        x_CoM = np.zeros(3)
        x_CoM[0] = np.sum(pos_vals[:,0]*m_vals)
        x_CoM[1] = np.sum(pos_vals[:,1]*m_vals)
        x_CoM[2] = np.sum(pos_vals[:,2]*m_vals)
        x_CoM /= totmass
        
        p_CoM = np.zeros(3)
        p_CoM[0] = np.sum(vel_vals[:,0]*m_vals)
        p_CoM[1] = np.sum(vel_vals[:,1]*m_vals)
        p_CoM[2] = np.sum(vel_vals[:,2]*m_vals)

        vel_vals -= p_CoM/totmass
        pos_vals -= x_CoM

        #Now rotate so that IMBH is in x-y plane
        #x_BH = pos_vals[0,:]
        #print(x_BH)
        #theta_rot = np.arctan(-x_BH[2]/x_BH[1])
        #print(theta_rot)
        #pos_old = 1.0*pos_vals
        #pos_vals[:,0] = pos_old[:,0] #Keep x coordinates the same
        #pos_vals[:,1] = np.cos(theta_rot)*pos_old[:,1] - np.sin(theta_rot)*pos_old[:,2]
        #pos_vals[:,2] = np.sin(theta_rot)*pos_old[:,1] + np.cos(theta_rot)*pos_old[:,2]
    
    
    #Add on the CoM position and velocity
    pos_vals += np.asarray(x0)
    vel_vals += v0
    
    return m_vals, pos_vals, vel_vals