Domain: Physics — Quantum Mechanics

Problem:
In cold, charge-neutral, \(\beta\)-equilibrated nucleonic matter, let the baryon density be \(n\) and the proton fraction be \(x_p = n_p / n\). 

The nucleonic isospin chemical-potential splitting, including a quartic correction, is given by

\[
\mu_n - \mu_p = 4\,S(n)\,(1 - 2x_p) + 8\,S_4(n)\,(1 - 2x_p)^3,
\]

where the density dependence of the quadratic and quartic symmetry energies is

\[
S(n) = S_0\!\left(\frac{n}{n_0}\right)^{\gamma}, \qquad
S_4(n) = S_{4,0}\!\left(\frac{n}{n_0}\right)^{\gamma_4}.
\]

The constants are

\[
S_0 = 31.6~\text{MeV}, \qquad \gamma = 0.60, \qquad 
S_{4,0} = 1.0~\text{MeV}, \qquad \gamma_4 = 1.20, \qquad
n_0 = 0.160~\text{fm}^{-3}.
\]

Leptons consist of an ideal, zero-temperature Fermi gas of electrons and muons with rest masses
\[
m_e c^2 = 0.51099895~\text{MeV}, \qquad
m_\mu c^2 = 105.6583755~\text{MeV}.
\]

For each lepton species \(\ell \in \{e, \mu\}\),
\[
\mu_\ell = \sqrt{(\hbar c\,k_{F,\ell})^2 + (m_\ell c^2)^2}, \qquad
n_\ell = \frac{k_{F,\ell}^3}{3\pi^2}, \qquad
\hbar c = 197.327~\text{MeV}\cdot\text{fm}.
\]

The matter satisfies \(\beta\)-equilibrium and charge neutrality:
\[
\mu_e = \mu_\mu = \mu_n - \mu_p, \qquad
n_p = n_e + n_\mu.
\]

For a given \(n\), determine \(x_p(n)\) and the common lepton chemical potential \(\mu_\ell(n)\) by solving the coupled system above.  Then evaluate the electron direct-Urca (DU) momentum-conservation condition:

\[
k_{Fn} = k_{Fp} + k_{Fe}
\quad\Longleftrightarrow\quad
(1 - x_p)^{1/3} = x_p^{1/3} + y_e^{1/3}, 
\qquad
y_e \equiv \frac{n_e}{n}.
\]

Compute the unique baryon density \(n_{\mathrm{DU}}^e\) at which the electron DU process 
\[
n \to p + e + \bar{\nu}_e
\]
first becomes kinematically allowed (i.e., where the equality above is satisfied for the first time as \(n\)increases). 

Calculate the ratio \(n_{\mathrm{DU}}^e/n_0\) upto three significant digits.

Answer: \[
\boxed{4.14}
\]

Solution code:
import math
import numpy as np

S0 = 31.6          # MeV
gamma = 0.60
S4_0 = 1.0         # MeV
gamma4 = 1.20
n0 = 0.160         # fm^-3
hbarc = 197.327    # MeV*fm

m_e = 0.51099895   # MeV
m_mu = 105.6583755 # MeV

pi = math.pi

def S(n):
    return S0 * (n / n0)**gamma

def S4(n):
    return S4_0 * (n / n0)**gamma4

def delta_mu(n, xp):
    # mu_n - mu_p = 4 S (1-2xp) + 8 S4 (1-2xp)^3  (MeV)
    a = 1.0 - 2.0 * xp
    return 4.0 * S(n) * a + 8.0 * S4(n) * (a**3)

def fermi_kF_from_mu(mu, m):
    # Return kF [fm^-1] given chemical potential mu [MeV] and mass m [MeV]
    # mu^2 = (hbarc kF)^2 + m^2  => kF = sqrt(mu^2 - m^2)/hbarc if mu>=m else 0
    if mu <= m:
        return 0.0
    return math.sqrt(mu*mu - m*m) / hbarc

def number_density_from_mu(mu, m):
    # n = kF^3/(3 pi^2) [fm^-3]; zero if below threshold
    kF = fermi_kF_from_mu(mu, m)
    if kF <= 0.0:
        return 0.0
    return (kF**3) / (3.0 * pi * pi)

def neutrality_residual(xp, n):
    """
    f(xp; n) = n*xp - [n_e(mu) + n_mu(mu)]  with mu = delta_mu(n, xp)
    Root f=0 gives charge neutrality at fixed n.
    """
    mu = delta_mu(n, xp)
    ne = number_density_from_mu(mu, m_e)
    nmu = number_density_from_mu(mu, m_mu)
    return n * xp - (ne + nmu)

def solve_xp(n, tol=1e-12, maxit=200):
    """
    Solve n*xp = ne(mu) + nmu(mu) with mu = delta_mu(n, xp) for xp in (0, 0.5).
    """
    # Start with a conservative bracket in (0, 0.5)
    lo, hi = 1e-12, 0.5 - 1e-9

    flo = neutrality_residual(lo, n)
    fhi = neutrality_residual(hi, n)

    # If signs don't bracket, try a few adaptive attempts
    if flo * fhi > 0.0:
        trial_points = [1e-6, 1e-4, 1e-3, 5e-3, 1e-2, 2e-2, 5e-2, 0.1, 0.2, 0.3, 0.4]
        vals = [(x, neutrality_residual(x, n)) for x in trial_points]
        # Find any sign change among sampled points plus endpoints
        xs = [lo] + trial_points + [hi]
        fs = [flo] + [v[1] for v in vals] + [fhi]
        brack_found = False
        for i in range(len(xs)-1):
            if fs[i] == 0.0:
                return xs[i]
            if fs[i] * fs[i+1] < 0.0:
                lo, hi = xs[i], xs[i+1]
                flo, fhi = fs[i], fs[i+1]
                brack_found = True
                break
        if not brack_found:
            raise RuntimeError(f"Failed to bracket xp at n={n:.6e}: f(lo)={flo:.3e}, f(hi)={fhi:.3e}")

    # Bisection
    for _ in range(maxit):
        mid = 0.5 * (lo + hi)
        fmid = neutrality_residual(mid, n)
        if abs(fmid) < tol or (hi - lo) < 1e-14:
            return mid
        if flo * fmid < 0.0:
            hi, fhi = mid, fmid
        else:
            lo, flo = mid, fmid

    # Fallback
    return 0.5 * (lo + hi)

def du_residual(n):
    """
    g(n) = (1 - xp)^(1/3) - xp^(1/3) - (ne/n)^(1/3), evaluated at the
    beta-equilibrated, neutral composition at density n.
    """
    xp = solve_xp(n)
    mu = delta_mu(n, xp)
    ne = number_density_from_mu(mu, m_e)
    ye = ne / n
    # Guard tiny numerical negatives in cube roots
    def cbrt01(z):
        return (z if z > 0.0 else 0.0)**(1.0/3.0)
    return cbrt01(1.0 - xp) - cbrt01(xp) - cbrt01(ye)

def find_n_DU(tol_rel=1e-10, maxit=200):
    """
    Find n where g(n) = 0 (first occurrence with increasing n).
    Bracket by expanding upper bound until sign change, then bisection.
    """
    n_lo = 0.8 * n0
    g_lo = du_residual(n_lo)

    # If already below, shift lower bound down
    if g_lo <= 0.0:
        n_lo = 0.4 * n0
        g_lo = du_residual(n_lo)

    n_hi = 2.0 * n0
    g_hi = du_residual(n_hi)

    # Expand until sign change
    expand_count = 0
    while g_lo * g_hi > 0.0 and expand_count < 64:
        n_hi *= 1.5
        g_hi = du_residual(n_hi)
        expand_count += 1

    if g_lo * g_hi > 0.0:
        # As a fallback, also try expanding downward a bit
        n_try = n_lo
        for _ in range(8):
            n_try *= 0.7
            g_try = du_residual(n_try)
            if g_try * g_hi < 0.0:
                n_lo, g_lo = n_try, g_try
                break
        if g_lo * g_hi > 0.0:
            raise RuntimeError("Failed to bracket DU threshold in density.")

    # Bisection on n
    for _ in range(maxit):
        n_mid = 0.5 * (n_lo + n_hi)
        g_mid = du_residual(n_mid)
        # Convergence: relative interval small and residual small
        if abs(g_mid) < 1e-12 or abs(n_hi - n_lo) / n_mid < tol_rel:
            return n_mid
        if g_lo * g_mid < 0.0:
            n_hi, g_hi = n_mid, g_mid
        else:
            n_lo, g_lo = n_mid, g_mid

    return 0.5 * (n_lo + n_hi)

if __name__ == "__main__":
    n_DU = find_n_DU()
    val = n_DU / n0
    print(f"{val:.3g}")