Beispiel #1
0
def ln_priors_initial(x):
    """ Calculate the prior probability for the initial mass
    and birth time calculations.

    Parameters
    ----------
    x : M1, M2, M2_d, t_obs
        Model parameters plus the observed companion mass

    Returns
    -------
    ll : float
        Prior probability of the model parameters
    """

    M1, M2, M2_d, t_obs = x

    # M1
    if M1 < c.min_mass or M1 > c.max_mass: return -np.inf

    # M2
    if M2 < 0.3*M1 or M2 > M1: return -np.inf

    # Add a prior so that the post-MT secondary is within the correct bounds
    M2_c = M1 + M2 - load_sse.func_sse_he_mass(M1)
    if M2_c > c.max_mass or M2_c < c.min_mass: return -np.inf

    # Add a prior so the primary can go through a SN by t_obs
    if load_sse.func_sse_tmax(M1) > t_obs: return -np.inf

    # Add a prior so the effective time remains bounded
    t_eff_obs = binary_evolve.func_get_time(M1, M2, t_obs)
    if t_eff_obs < 0.0: return -np.inf

    # Add a prior so that only those masses with a non-zero Mdot are allowed
    M2_tmp, M2_dot, R_tmp, k_tmp = load_sse.func_get_sse_star(M2_c, t_eff_obs)
    if M2_dot == 0.0: return -np.inf

    return 0.0
Beispiel #2
0
def full_forward(M1, M2, A, ecc, v_k, theta, phi, t_obs):
    """ Evolve a binary forward from its initial conditions

    Parameters
    ----------
    M1 : float
        Initial primary mass (Msun)
    M2 : float
        Initial secondary mass (Msun)
    A : float
        Initial orbital separation (Rsun)
    ecc : float
        Initial orbital eccentricity (unitless)
    v_k : float
        SN kick velocity
    theta : float
        SN kick polar angle
    phi : float
        SN kick azimuthal angle
    t_obs : float
        observation time

    Returns
    -------
    M_NS : float or ndarray
        Array of final primary masses (Currently set to the NS mass, c.M_NS)
    M_2 : float or ndarray
        Array of final secondary masses (Msun)
    L_x : float or ndarray
        X-ray luminosity (erg/s)
    v_sys : float or ndarray
        Systemic velocity (km/s)
    M2_dot : float or ndarray
        Mass accretion rate (Msun/yr)
    A : float or ndarray
        Orbital separation (Rsun)
    ecc : float or ndarray
        Orbital eccentricity (unitless)
    theta : float or ndarray
        Projected angular distance traveled from birth location (radians)
    k_type : int
        k-type of HMXB donor
    """

    if load_sse.func_sse_mass is None:
        load_sse.load_sse()


    if isinstance(M1, np.ndarray):
        dtypes = [('M_NS','<f8'), \
                ('M_2','<f8'), \
                ('L_x','<f8'), \
                ('v_sys','<f8'), \
                ('M2_dot','<f8'), \
                ('A','<f8'), \
                ('ecc','<f8'), \
                ('theta','<f8'), \
                ('k_type','<i8')]

        HMXB = np.recarray(len(M1), dtype=dtypes)

        for i in np.arange(len(M1)):

            if isinstance(t_obs, np.ndarray):
                if t_obs[i] < load_sse.func_sse_ms_time(M1[i]):
                    HMXB["M_NS"][i] = M1[i]
                    HMXB["M_2"][i] = M2[i]
                    HMXB["A"][i] = A[i]
                    continue
            else:
                if t_obs < load_sse.func_sse_ms_time(M1[i]):
                    HMXB["M_NS"][i] = M1[i]
                    HMXB["M_2"][i] = M2[i]
                    HMXB["A"][i] = A[i]
                    continue


            # First MT phase
            M_1_b, M_2_b, A_b = binary_evolve.func_MT_forward(M1[i], M2[i], A[i], ecc[i])

            if isinstance(t_obs, np.ndarray):
                if t_obs[i] < load_sse.func_sse_tmax(M1[i]):
                    HMXB["M_NS"][i] = M_1_b
                    HMXB["M_2"][i] = M_2_b
                    HMXB["A"][i] = A_b
                    continue
            else:
                if t_obs < load_sse.func_sse_tmax(M1[i]):
                    HMXB["M_NS"][i] = M_1_b
                    HMXB["M_2"][i] = M_2_b
                    HMXB["A"][i] = A_b
                    continue


            # SN
            A_tmp, v_sys_tmp, e_tmp = binary_evolve.func_SN_forward(M_1_b, M_2_b, A_b, v_k[i], theta[i], phi[i])

            # XRB
            if isinstance(t_obs, np.ndarray):
                M_2_tmp, L_x_tmp, M2_dot_out, A_out = binary_evolve.func_Lx_forward(M1[i], M2[i], M_2_b, A_tmp, e_tmp, t_obs[i])
                theta_out = (t_obs[i] - load_sse.func_sse_tmax(M1[i])) * v_sys_tmp / c.dist_SMC * c.yr_to_sec * 1.0e6 * np.sin(get_theta(1))
                tobs_eff = binary_evolve.func_get_time(M1[i], M2[i], t_obs[i])
            else:
                M_2_tmp, L_x_tmp, M2_dot_out, A_out = binary_evolve.func_Lx_forward(M1[i], M2[i], M_2_b, A_tmp, e_tmp, t_obs)
                theta_out = (t_obs - load_sse.func_sse_tmax(M1[i])) * v_sys_tmp / c.dist_SMC * c.yr_to_sec * 1.0e6 * np.sin(get_theta(1))
                tobs_eff = binary_evolve.func_get_time(M1[i], M2[i], t_obs)

            # To get k-type of HMXB donor
            if M_2_b > c.max_mass:
                k_type = -999
            else:
                M_tmp, M_dot_tmp, R_tmp, k_type = load_sse.func_get_sse_star(M_2_b, tobs_eff)


            HMXB["M_NS"][i] = c.M_NS
            HMXB["M_2"][i] = M_2_tmp
            HMXB["L_x"][i] = L_x_tmp
            HMXB["v_sys"][i] = v_sys_tmp
            HMXB["M2_dot"][i] = M2_dot_out
            HMXB["A"][i] = A_out
            HMXB["ecc"][i] = e_tmp
            HMXB["theta"][i] = theta_out
            HMXB["k_type"][i] = int(k_type)


        return HMXB["M_NS"], HMXB["M_2"], HMXB["L_x"], HMXB["v_sys"], HMXB["M2_dot"], HMXB["A"], HMXB["ecc"], HMXB["theta"], HMXB["k_type"]

    else:

        # Star does not make it to MT phase
        if t_obs < load_sse.func_sse_ms_time(M1): return M1, M2, 0.0, 0.0, 0.0, A, ecc, 0.0

        # MT phase
        M_1_b, M_2_b, A_b = binary_evolve.func_MT_forward(M1, M2, A, ecc)

        # Star does not make it to SN
        if t_obs < load_sse.func_sse_tmax(M1): return M_1_b, M_2_b, 0.0, 0.0, 0.0, A_b, ecc, 0.0

        # SN
        A_tmp, v_sys_tmp, e_tmp = binary_evolve.func_SN_forward(M_1_b, M_2_b, A_b, v_k, theta, phi)

        # XRB
        M_2_tmp, L_x_tmp, M2_dot_out, A_out = binary_evolve.func_Lx_forward(M1, M2, M_2_b, A_tmp, e_tmp, t_obs)

        theta_out = (t_obs - load_sse.func_sse_tmax(M1)) * v_sys_tmp / c.dist_SMC * c.yr_to_sec * 1.0e6 * np.sin(get_theta(1))

        # To get k-type of HMXB donor
        tobs_eff = binary_evolve.func_get_time(M1, M2, t_obs)
        M_tmp, M_dot_tmp, R_tmp, k_type = load_sse.func_get_sse_star(M_2_b, tobs_eff)

        return c.M_NS, M_2_tmp, L_x_tmp, v_sys_tmp, M2_dot_out, A_out, e_tmp, theta_out, int(k_type)
Beispiel #3
0
ax[0].set_xlabel(r"${\rm log}\ P_{\rm orb}\ {\rm (days)}$", size=16)
ax[0].set_ylabel(r"$e$", size=16)
ax[0].set_xticks([0,1,2,3,4])
ax[0].set_yticks([0,0.25,0.5,0.75,1.0])

plt_range = ([8, 24], [0,80])
corner.hist2d(HMXB[4], HMXB[5], ax=ax[1], bins=40, range=plt_range, plot_datapoints=False)
ax[1].set_xlabel(r"$M_2\ ({\rm M}_{\odot})$", size=16)
ax[1].set_ylabel(r"$v_{\rm sys}\ {\rm (km\ s}^{-1})$", size=16)
ax[1].set_xticks([8,12,16,20,24])
ax[1].set_yticks([0,20, 40, 60, 80])


# Get flight time from birth time and M1 lifetime
load_sse.load_sse()
t_flight = sampler.flatchain.T[9] - load_sse.func_sse_tmax(sampler.flatchain.T[0])
plt_range = ([0,55], [0,25])
contour_kwargs = {'colors':'r', 'linestyles':'dashed'}
corner.hist2d(t_flight, HMXB[7], ax=ax[2], bins=40, range=plt_range, plot_density=False,
		plot_datapoints=False, contour_kwargs=contour_kwargs)
corner.hist2d(sampler.flatchain.T[9], HMXB[7], ax=ax[2], bins=40, range=plt_range,
		plot_density=False, plot_datapoints=False)
ax[2].set_xlabel(r"$t_{\rm i}\ {\rm (Myr)}$", size=16)
ax[2].set_ylabel(r"$\theta\ {\rm (amin)}$", size=16)

plt.tight_layout()

plt.savefig('../figures/smc_population_HMXB.pdf')


Beispiel #4
0
def get_initial_values(M2_d, nwalkers=32):
    """ Calculate an array of initial masses and birth times

    Parameters
    ----------
    M2_d : float
        Observed secondary mass

    Returns
    -------
    pos : ndarray, shape=(nwalkers,3)
        Array of (M1, M2, t_b)
    """


    # Start by using MCMC on just the masses to get a distribution of M1 and M2

    args = [[M2_d]]
    sampler = emcee.EnsembleSampler(nwalkers=nwalkers, dim=3, lnpostfn=ln_posterior_initial, args=args)

    # Picking the initial masses and birth time will need to be optimized
    t_b = 1000.0
    M1_tmp = max(0.6*M2_d, c.min_mass)
    M2_tmp = 1.1*M2_d - M1_tmp
    p_i = [M1_tmp, M2_tmp,t_b]
    tmp = binary_evolve.func_get_time(*p_i) - 1000.0
    t_b = 0.9 * (load_sse.func_sse_tmax(p_i[0] + p_i[1] - load_sse.func_sse_he_mass(p_i[0])) - tmp)
    p_i[2] = t_b

    t_eff_obs = binary_evolve.func_get_time(*p_i)
    M_b_prime = p_i[0] + p_i[1] - load_sse.func_sse_he_mass(p_i[0])
    M_tmp, Mdot_tmp, R_tmp, k_tmp = load_sse.func_get_sse_star(M_b_prime, t_eff_obs)

    min_M = load_sse.func_sse_min_mass(t_b)

    n_tries = 0
    while t_eff_obs < 0.0 or Mdot_tmp == 0.0:

        p_i[0] = (c.max_mass - min_M) * np.random.uniform() + min_M
        p_i[1] = (0.7 * np.random.uniform() + 0.3) * p_i[0]
        p_i[2] = (np.random.uniform(5.0) + 1.2) * load_sse.func_sse_tmax(M2_d*0.6)

        t_eff_obs = binary_evolve.func_get_time(*p_i)
        if t_eff_obs < 0.0: continue

        M_b_prime = p_i[0] + p_i[1] - load_sse.func_sse_he_mass(p_i[0])
        if M_b_prime > c.max_mass: continue

        M_tmp, Mdot_tmp, R_tmp, k_tmp = load_sse.func_get_sse_star(M_b_prime, t_eff_obs)

        # Exit condition
        n_tries += 1
        if n_tries > 100: break


    # initial positions for walkers
    p0 = np.zeros((nwalkers,3))
    a, b = (min_M - p_i[0]) / 0.5, (c.max_mass - p_i[0]) / 0.5
    p0[:,0] = truncnorm.rvs(a, b, loc=p_i[0], scale=1.0, size=nwalkers) # M1
    p0[:,1] = np.random.normal(p_i[1], 0.5, size=nwalkers) # M2
    p0[:,2] = np.random.normal(p_i[2], 0.2, size=nwalkers) # t_b

    # burn-in
    pos,prob,state = sampler.run_mcmc(p0, N=100)

    return pos
Beispiel #5
0
def ln_priors(y):
    """ Priors on the model parameters

    Parameters
    ----------
    y : ra, dec, M1, M2, A, ecc, v_k, theta, phi, ra_b, dec_b, t_b
        Current HMXB location (ra, dec) and 10 model parameters

    Returns
    -------
    lp : float
        Natural log of the prior
    """

#    M1, M2, A, v_k, theta, phi, ra_b, dec_b, t_b = y
    ra, dec, M1, M2, A, ecc, v_k, theta, phi, ra_b, dec_b, t_b = y

    lp = 0.0

    # P(M1)
    if M1 < c.min_mass or M1 > c.max_mass: return -np.inf
    norm_const = (c.alpha+1.0) / (np.power(c.max_mass, c.alpha+1.0) - np.power(c.min_mass, c.alpha+1.0))
    lp += np.log( norm_const * np.power(M1, c.alpha) )
    # M1 must be massive enough to evolve off the MS by t_obs
    if load_sse.func_sse_tmax(M1) > t_b: return -np.inf

    # P(M2)
    # Normalization is over full q in (0,1.0)
    q = M2 / M1
    if q < 0.3 or q > 1.0: return -np.inf
    lp += np.log( (1.0 / M1 ) )

    # P(ecc)
    if ecc < 0.0 or ecc > 1.0: return -np.inf
    lp += np.log(2.0 * ecc)

    # P(A)
    if A*(1.0-ecc) < c.min_A or A*(1.0+ecc) > c.max_A: return -np.inf
    norm_const = np.log(c.max_A) - np.log(c.min_A)
    lp += np.log( norm_const / A )
    # A must avoid RLOF at ZAMS, by a factor of 2
    r_1_roche = binary_evolve.func_Roche_radius(M1, M2, A*(1.0-ecc))
    if 2.0 * load_sse.func_sse_r_ZAMS(M1) > r_1_roche: return -np.inf

    # P(v_k)
    if v_k < 0.0: return -np.inf
    lp += np.log( maxwell.pdf(v_k, scale=c.v_k_sigma) )

    # P(theta)
    if theta <= 0.0 or theta >= np.pi: return -np.inf
    lp += np.log(np.sin(theta) / 2.0)

    # P(phi)
    if phi < 0.0 or phi > np.pi: return -np.inf
    lp += -np.log( np.pi )

    # Get star formation history
    sf_history.load_sf_history()
    sfh = sf_history.get_SFH(ra_b, dec_b, t_b, sf_history.smc_coor, sf_history.smc_sfh)
    if sfh <= 0.0: return -np.inf

    # P(alpha, delta)
    # From spherical geometric effect, we need to care about cos(declination)
    lp += np.log(np.cos(c.deg_to_rad * dec_b) / 2.0)

    ##################################################################
    # We add an additional prior that scales the RA and Dec by the
    # area available to it, i.e. pi theta^2, where theta is the angle
    # of the maximum projected separation over the distance.
    #
    # Still under construction
    ##################################################################
    M1_b, M2_b, A_b = binary_evolve.func_MT_forward(M1, M2, A, ecc)
    A_c, v_sys, ecc = binary_evolve.func_SN_forward(M1_b, M2_b, A_b, v_k, theta, phi)
    if ecc < 0.0 or ecc > 1.0 or np.isnan(ecc): return -np.inf

    # Ensure that we only get non-compact object companions
    tobs_eff = binary_evolve.func_get_time(M1, M2, t_b)
    M_tmp, M_dot_tmp, R_tmp, k_type = load_sse.func_get_sse_star(M2_b, tobs_eff)
    if int(k_type) > 9: return -np.inf

#    t_sn = (t_b - func_sse_tmax(M1)) * 1.0e6 * yr_to_sec  # The time since the primary's core collapse
#    theta_max = (v_sys * t_sn) / dist_LMC  # Unitless
#    area = np.pi * rad_to_dec(theta_max)**2
#    lp += np.log(1.0 / area)
    ##################################################################
    # Instead, let's estimate the number of stars formed within a cone
    # around the observed position, over solid angle and time.
    # Prior is in Msun/Myr/steradian
    ##################################################################
    t_min = load_sse.func_sse_tmax(M1) * 1.0e6 * c.yr_to_sec
    t_max = (load_sse.func_sse_tmax(M2_b) - binary_evolve.func_get_time(M1, M2, 0.0)) * 1.0e6 * c.yr_to_sec
    if t_max-t_min < 0.0: return -np.inf
    theta_C = (v_sys * (t_max - t_min)) / c.dist_SMC
    stars_formed = get_stars_formed(ra, dec, t_min, t_max, v_sys, c.dist_SMC)
    if stars_formed == 0.0: return -np.inf
    volume_cone = (np.pi/3.0 * theta_C**2 * (t_max - t_min) / c.yr_to_sec / 1.0e6)
    lp += np.log(sfh / stars_formed / volume_cone)
    ##################################################################

#    # P(t_b | alpha, delta)
#    sfh_normalization = 1.0e-6
#    lp += np.log(sfh_normalization * sfh)

    # Add a prior so that the post-MT secondary is within the correct bounds
    M2_c = M1 + M2 - load_sse.func_sse_he_mass(M1)
    if M2_c > c.max_mass or M2_c < c.min_mass: return -np.inf

    # Add a prior so the effective time remains bounded
    t_eff_obs = binary_evolve.func_get_time(M1, M2, t_b)
    if t_eff_obs < 0.0: return -np.inf

    if t_b * 1.0e6 * c.yr_to_sec < t_min: return -np.inf
    if t_b * 1.0e6 * c.yr_to_sec > t_max: return -np.inf

    return lp
Beispiel #6
0
def ln_priors_population(y):
    """ Priors on the model parameters

    Parameters
    ----------
    y : M1, M2, A, ecc, v_k, theta, phi, ra_b, dec_b, t_b
        10 model parameters

    Returns
    -------
    lp : float
        Natural log of the prior
    """

    M1, M2, A, ecc, v_k, theta, phi, ra_b, dec_b, t_b = y

    lp = 0.0

    # P(M1)
    if M1 < c.min_mass or M1 > c.max_mass: return -np.inf
    norm_const = (c.alpha+1.0) / (np.power(c.max_mass, c.alpha+1.0) - np.power(c.min_mass, c.alpha+1.0))
    lp += np.log( norm_const * np.power(M1, c.alpha) )
    # M1 must be massive enough to evolve off the MS by t_obs
    if load_sse.func_sse_tmax(M1) > t_b: return -np.inf

    # P(M2)
    # Normalization is over full q in (0,1.0)
    q = M2 / M1
    if q < 0.3 or q > 1.0: return -np.inf
    lp += np.log( (1.0 / M1 ) )

    # P(ecc)
    if ecc < 0.0 or ecc > 1.0: return -np.inf
    lp += np.log(2.0 * ecc)

    # P(A)
    if A*(1.0-ecc) < c.min_A or A*(1.0+ecc) > c.max_A: return -np.inf
    norm_const = np.log(c.max_A) - np.log(c.min_A)
    lp += np.log( norm_const / A )
    # A must avoid RLOF at ZAMS, by a factor of 2
    r_1_roche = binary_evolve.func_Roche_radius(M1, M2, A*(1.0-ecc))
    if 2.0 * load_sse.func_sse_r_ZAMS(M1) > r_1_roche: return -np.inf

    # P(v_k)
    if v_k < 0.0: return -np.inf
    lp += np.log( maxwell.pdf(v_k, scale=c.v_k_sigma) )

    # P(theta)
    if theta <= 0.0 or theta >= np.pi: return -np.inf
    lp += np.log(np.sin(theta) / 2.0)

    # P(phi)
    if phi < 0.0 or phi > np.pi: return -np.inf
    lp += -np.log( np.pi )

    # Get star formation history
    sfh = sf_history.get_SFH(ra_b, dec_b, t_b, sf_history.smc_coor, sf_history.smc_sfh)
    if sfh <= 0.0: return -np.inf
    lp += np.log(sfh)

    # P(alpha, delta)
    # From spherical geometric effect, scale by cos(declination)
    lp += np.log(np.cos(c.deg_to_rad*dec_b) / 2.0)

    M1_b, M2_b, A_b = binary_evolve.func_MT_forward(M1, M2, A, ecc)
    A_c, v_sys, ecc = binary_evolve.func_SN_forward(M1_b, M2_b, A_b, v_k, theta, phi)
    if ecc < 0.0 or ecc > 1.0 or np.isnan(ecc): return -np.inf

    # Ensure that we only get non-compact object companions
    tobs_eff = binary_evolve.func_get_time(M1, M2, t_b)
    M_tmp, M_dot_tmp, R_tmp, k_type = load_sse.func_get_sse_star(M2_b, tobs_eff)
    if int(k_type) > 9: return -np.inf

    # Add a prior so that the post-MT secondary is within the correct bounds
    M2_c = M1 + M2 - load_sse.func_sse_he_mass(M1)
    if M2_c > c.max_mass or M2_c < c.min_mass: return -np.inf

    # Add a prior so the effective time remains bounded
    t_eff_obs = binary_evolve.func_get_time(M1, M2, t_b)
    if t_eff_obs < 0.0: return -np.inf
    t_max = (load_sse.func_sse_tmax(M2_b) - binary_evolve.func_get_time(M1, M2, 0.0))
    if t_b > t_max: return -np.inf

    return lp
Beispiel #7
0
def ln_posterior(x, args):
    """ Calculate the natural log of the posterior probability

    Parameters
    ----------
    x : M1, M2, A, ecc, v_k, theta, phi, ra_b, dec_b, t_b
        10 model parameters
    args : M2_d, P_orb_obs, ecc_obs, ra, dec
        System observations

    Returns
    -------
    lp : float
        Natural log of the posterior probability
    """

    M1, M2, A, ecc, v_k, theta, phi, ra_b, dec_b, t_b = x
    M2_d, M2_d_err, P_orb_obs, P_orb_obs_err, ecc_obs, ecc_obs_err, ra, dec = args
    y = ra, dec, M1, M2, A, ecc, v_k, theta, phi, ra_b, dec_b, t_b


    # Call priors
    lp = ln_priors(y)
    if np.isinf(lp): return -np.inf

    ll = 0

    M1_b, M2_b, A_b = binary_evolve.func_MT_forward(M1, M2, A, ecc)
    A_c, v_sys, ecc_out = binary_evolve.func_SN_forward(M1_b, M2_b, A_b, v_k, theta, phi)
    M2_d_out, L_x_out, M_dot_out, A_d = binary_evolve.func_Lx_forward(M1, M2, M2_b, A_c, ecc_out, t_b)
    P_orb_d = A_to_P(c.M_NS, M2_d_out, A_d)

    # If system disrupted or no X-ray luminosity, return -infty
    if ecc_out < 0.0 or ecc_out > 1.0 or np.isnan(ecc) or L_x_out==0.0: return -np.inf

    # Observed secondary mass
    delta_M_err = M2_d_err # uncertainty is user input
    coeff_M = -0.5 * np.log( 2. * np.pi * delta_M_err**2 )
    argument_M = -( M2_d - M2_d_out ) * ( M2_d - M2_d_out ) / ( 2. * delta_M_err**2 )
    ll += coeff_M + argument_M

    # Observed X-ray luminosity
#    delta_ln_L_x_err = 0.2
#    coeff_ln_L_x = -0.5 * np.log( 2. * np.pi * delta_ln_L_x_err**2 )
#    argument_ln_L_x = -( np.log(L_x) - np.log(L_x_out) ) * ( np.log(L_x) - np.log(L_x_out) ) / ( 2. * delta_ln_L_x_err**2 )
#    ll += coeff_ln_L_x + argument_ln_L_x

    # Observed orbital period
    delta_P_orb_err = P_orb_obs_err # uncertainty is user input
    coeff_P_orb = -0.5 * np.log( 2. * np.pi * delta_P_orb_err**2)
    argument_P_orb = -( P_orb_obs - P_orb_d )**2 / ( 2. * delta_P_orb_err**2 )
    ll += coeff_P_orb + argument_P_orb

    # Observed eccentricity
    delta_ecc_err = ecc_obs_err # uncertainty is user input
    coeff_ecc = -0.5 * np.log( 2. * np.pi * delta_ecc_err**2)
    argument_ecc = -( ecc_obs - ecc_out )**2 / ( 2. * delta_ecc_err**2 )
    ll += coeff_ecc + argument_ecc

    ######## Under Construction #######
    theta_proj = get_theta_proj(c.deg_to_rad*ra, c.deg_to_rad*dec, c.deg_to_rad*ra_b, c.deg_to_rad*dec_b)  # Projected travel distance
    t_sn = (t_b - load_sse.func_sse_tmax(M1)) * 1.0e6 * c.yr_to_sec  # The time since the primary's core collapse
    tmp = (v_sys * t_sn) / c.dist_SMC  # Unitless
    conds = [theta_proj>tmp, theta_proj<=tmp]  # Define conditional
    funcs = [lambda theta_proj: -np.inf, lambda theta_proj: np.log(np.tan(np.arcsin(theta_proj/tmp))/tmp)]
    J_coor = np.abs(get_J_coor(c.deg_to_rad*ra, c.deg_to_rad*dec, c.deg_to_rad*ra_b, c.deg_to_rad*dec_b)) # Jacobian for coordinate change
    P_omega = 1.0 / (2.0 * np.pi)
    ll += np.piecewise(theta_proj, conds, funcs) + np.log(P_omega) + np.log(J_coor)

#    print rad_to_dec(theta_proj)*3600.0, tmp, t_sn, v_sys, v_sys*t_sn, \
#        np.arcsin(theta_proj/tmp), np.tan(np.arcsin(theta_proj/tmp)), np.piecewise(theta_proj, conds, funcs), \
#        np.log(J_coor * P_omega)

    # Observed distance from the birth cluster
#    t_travel = (t_b - func_sse_tmax(M1)) * 1.0e6 * yr_to_sec
#    sin_theta = theta_proj * dist_LMC / (v_sys * t_travel)
#    if sin_theta < 0.0 or sin_theta > 1.0: return -np.inf  # sine must be bounded

#    cos_theta = np.sqrt(1.0 - sin_theta*sin_theta)
#    prob = sin_theta / cos_theta * v_sys * t_travel / dist_LMC
#    ll += np.log(prob)

    if np.isnan(ll): return -np.inf

    return ll + lp
Beispiel #8
0
def func_Lx_forward(M_1_a, M_2_a, M_2_in, A_in, ecc_in, t_obs):
    """ Calculate the X-ray luminosity from accretion for a binary

    Parameters
    ----------
    M_1_a : float
        ZAMS mass of the primary, now a NS (Msun)
    M_2_a : float
        ZAMS mass of the secondary (Msun)
    M_2_in : float
        Post-mass transfer mass of the secondary (Msun)
    A_in : float
        Post-SN orbital separation (Rsun)
    ecc_in : float
        Post-SN eccentricity (unitless)
    t_obs : float
        Time which the binary is being observed

    Returns
    -------
    M_2_out : float
        Current mass of the secondary
    L_x : float
        X_ray luminosity
    M_dot_out : float
        Mass accretion rate
    A_out : float
        Current orbital separation
    """

    t_eff_obs = func_get_time(M_1_a, M_2_a, t_obs)

    if isinstance(t_eff_obs, np.ndarray):
        M_2_out = np.array([])
        M_dot_wind = np.array([])
        R_out = np.array([])
        k_out = np.array([])
        for i in np.arange(len(t_eff_obs)):
            if (t_eff_obs[i] < 0.0 or ecc_in[i] < 0.0 or ecc_in[i] >= 1.0):
                ecc_in[i] = 0.0
                if isinstance(M_2_in, np.ndarray):
                    M_2_out = np.append(M_2_out, M_2_in[i])
                else:
                    M_2_out = np.append(M_2_out, M_2_in)
                M_dot_wind = np.append(M_dot_wind, 0.0)
                R_out = np.append(R_out, 0.0)
            else:
                if isinstance(M_2_in, np.ndarray):
                    if M_2_in[i] > c.max_mass:
                        aa, bb, cc = 0.0, 0.0, 0.0
                    else:
                        aa, bb, cc, dd = load_sse.func_get_sse_star(M_2_in[i], t_eff_obs[i])
                else:
                    if M_2_in > c.max_mass:
                        aa, bb, cc = 0.0, 0.0, 0.0
                    else:
                        aa, bb, cc, dd = load_sse.func_get_sse_star(M_2_in, t_eff_obs[i])

                M_2_out = np.append(M_2_out, aa)
                M_dot_wind = np.append(M_dot_wind, bb)
                R_out = np.append(R_out, cc)
                k_out = np.append(k_out, dd)
    else:
        if (t_eff_obs < 0.0 or M_2_in > c.max_mass or ecc_in < 0.0 or ecc_in > 1.0 or t_eff_obs > load_sse.func_sse_tmax(M_2_in)):
            M_2_out = M_2_in
            M_dot_wind = 0.0
            R_out = 0.0
            ecc_in = 0.0
        else:
            M_2_out, M_dot_wind, R_out, k_out = load_sse.func_get_sse_star(M_2_in, t_eff_obs)

    # Get wind velocity
    v_wind = get_v_wind(M_2_out, R_out)
    if isinstance(v_wind, np.ndarray):
        v_wind[np.where(v_wind <= 0.0)] = 1.0e50 # To eliminate "bad" winds
    else:
        if v_wind <= 0.0: v_wind = 1.0e50

    # Get final orbital separation
    if isinstance(A_in, np.ndarray):
        A_in[np.where(A_in <= 0.0)] = 1.0e50 # To eliminate "bad" separations
    else:
        if A_in <= 0.0: A_in = 1.0e50
    A_out = (c.M_NS + M_2_in) / (c.M_NS + M_2_out) * A_in

    # Capture fraction takes into account eccentricity
    f_capture = (c.GGG*c.M_NS / (v_wind*v_wind*A_out))**2 / np.sqrt(1.0 - ecc_in**2)
    M_dot_out = f_capture * M_dot_wind

    L_bol = c.GGG * c.M_NS * M_dot_out / c.R_NS * c.km_to_cm * c.Msun_to_g * c.Rsun_to_cm / c.yr_to_sec
    L_x = c.eta_bol * L_bol

    return M_2_out, L_x, M_dot_out, A_out