In-Class Assignment 18#
In-Class Only, Not Submitted for Credit, Credit: Michael Zingale.
Learning Objectives#
Develop a qualitative understanding of the M-R relationship for neutron stars
Identify the impact of the compressibility modulus \(K\) on the M-R relation and the maximum mass
import numpy as np
import matplotlib.pyplot as plt
Here’s a general integration class that implemented 4th order Runge-Kutta. It takes
a rhs
function of the form:
rhs(r, Y, rho_func)
that returns the derivatives \(d{\bf Y}/dr\) for each of the variables in the array \({\bf Y}\).
Here, rho_func
is a function rho_func(pres)
that takes the pressure and returns the
density according to our EOS.
class Integrator:
""" this is a simple RK4 integrator with a uniform step size
that can accept a list of stopping criteria to halt the integration.
There is one list element for each variable. An entry of "None"
means that we have no stopping criteria for that variable. A tuple
of the form ("LT", val) or ("GT", val), means that we stop if
y[n] < val (or y[n] > val)
"""
def __init__(self, rhs, *, stop_cond=None, rhs_args=None):
self.rhs = rhs
self.stop_cond = stop_cond
if rhs_args is None:
rhs_args = []
self.args = rhs_args
def rk4_step(self, x, y, dx):
dydx1 = self.rhs(x, y, *self.args)
y1 = y + 0.5*dx*dydx1
if self.check_stop_cond(y1) < 0: return None
dydx2 = self.rhs(x + 0.5*dx, y1, *self.args)
y2 = y + 0.5*dx*dydx2
if self.check_stop_cond(y2) < 0: return None
dydx3 = self.rhs(x + 0.5*dx, y2, *self.args)
y3 = y + dx*dydx3
if self.check_stop_cond(y3) < 0: return None
dydx4 = self.rhs(x + dx, y3, *self.args)
y_final = y + (dx/6.0)*(dydx1 + 2.0*dydx2 + 2.0*dydx3 + dydx4)
if self.check_stop_cond(y_final) < 0: return None
return y_final
def check_stop_cond(self, y):
# check our stopping conditions
if not self.stop_cond is None:
for n, cond in enumerate(self.stop_cond):
if cond is None: continue
c, v = cond
if c == "LT":
if y[n] < v: return -1
elif c == "GT":
if ynew[n] > v: return -1
else:
sys.exit("invalid condition")
return 0
def integrate(self, initial_conditions, dx, xmax, tol=1.e-8):
ic = np.array(initial_conditions)
N = len(ic)
sol = {}
for n in range(N):
sol[f"y{n}"] = [ic[n]]
x = 0.0
sol["x"] = [x]
y = initial_conditions
while (x < xmax and dx > tol * xmax):
# advance for a step
ynew = self.rk4_step(x, y, dx)
if ynew is None:
# we hit our stopping condition
# cut the stepsize so we can try to get closer
dx /= 2
continue
# store the solution
x += dx
sol["x"].append(x)
for n in range(N):
sol[f"y{n}"].append(ynew[n])
y = ynew
return sol
Some physical constants (CGS)
G = 6.67e-8
c = 3.e10
h = 6.63e-27
m_u = 1.66e-24
Here’s the righthand side function for the TOV equation
def rhs_tov(r, y, rho_func):
m = y[0]
P = y[1]
rho = rho_func(P)
dmdr = 4.0 * np.pi * r**2 * rho
if r == 0.0:
dPdr = 0.0
else:
rho_term = rho + P / c**2
m_term = m + 4.0 * np.pi * r**3 * P / c**2
metric_term = (1.0 - 2.0 * G * m / (r * c**2))
dPdr = -(G / r**2) * rho_term * m_term / metric_term
return np.array([dmdr, dPdr])
Here’s the righthand side function for Newtonian HSE
def rhs_newton(r, y, rho_func):
m = y[0]
P = y[1]
rho = rho_func(P)
dmdr = 4.0 * np.pi * r**2 * rho
if r == 0.0:
dPdr = 0.0
else:
dPdr = -G * m / r**2 * rho
return np.array([dmdr, dPdr])
Degenerate neutrons#
For zero-T neutron degeneracy, the number density of neutrons is just the integral of the distribution function, \(n(p)\), over all momenta:
but at zero temperature the distribution function is just a step function:
This gives:
where \(x_F = p_F / (m_u c)\).
We can then compute the pressure as:
where we now need to use the general expression for velocity,
This gives:
with
from scipy.optimize import brentq
def pres_deg(rho):
# compute the Fermi momentum x = p / mc
B = 8 * np.pi * m_u / 3.0 * (m_u * c / h)**3
x = np.cbrt(rho / B)
f = x * (2 * x**2 - 3) * np.sqrt(1 + x**2) + 3 * np.arcsinh(x)
A = (np.pi / 3) * (m_u * c / h)**3 * m_u * c**2
return A * f
def rho_deg(pres):
A = (np.pi / 3) * (m_u * c / h)**3 * m_u * c**2
f = pres / A
# find the x that gives us this f
x = brentq(lambda x: f - (x * (2 * x**2 - 3) * np.sqrt(1 + x**2) + 3 * np.arcsinh(x)), 0, 10)
B = 8 * np.pi * m_u / 3.0 * (m_u * c / h)**3
return B * x**3
A polytropic nuclear EOS#
A simple polytopic approximation to a nuclear equation of state (the SLy4 EOS) is:
If we choose \(K = 3.5 \mathrm{MeV~fm^{-3}}\) and \(\rho_0 = 150 \mathrm{MeV~fm^{-3}}\), we approximate the behavior of the SLy4 nuclear EOS (thanks to Tianqi Zhao for these approximations) with \(\gamma = 2.5\).
In CGS units, using \(\gamma = 2.5\), we can take \(K = 4.80\times 10^{-3}~\mathrm{erg~cm^{-3}}\) and write this as:
def pres_nuc(rho):
gamma = 2.5
K = 4.8e-3
return K * rho**gamma
def rho_nuc(pres):
gamma = 2.5
K = 4.8e-3
return (pres / K)**(1.0 / gamma)
Notebook begins here#
a. - The “Chandrasekhar mass” of a Neutron Star#
The Chandrasekhar mass, the maximum mass that electron degenerate objects (of fixed \(\mu_{e}\)) can have before becoming gravitationally unstable and collapsing, is given by HKT 3.67:
For electrons, the \(\mu\) was \(\mu_e\), as in white dwarfs. But, let’s use this to compute the maximum mass of a neutron star, where now \(\mu = 1\) for neutrons.
Compute the maximum mass for a NS using the equation above.
# a result here
b. - Case I: Newtonian gravity, neutron degeneracy#
Now, let’s look at the M-R relation for a Neutron star where we will use our usual HSE, but with pressure derived from \(n\) degeneracy pressure.
Evaluate the cell below for a M-R relation using our usual non-GR HSE, but with \(n\) degeneracy pressure.
Plot the resulting M-R and label the maximum allowed NS mass from this result.
Compare with the result of a. Do they match?
We’ll loop over the central density and integrate the neutron star structure and store the mass and radius corresponding to each central density.
ns1 = Integrator(rhs_newton, rhs_args=[rho_deg],
stop_cond=[("LT", 0.0), ("LT", 0.0)])
R_max = 5.e6
N = 1000
mass_ns1 = []
radius_ns1 = []
for rhoc in np.logspace(np.log10(5.e14), np.log10(5.e18), 100):
Pc = pres_deg(rhoc)
sol = ns1.integrate([0.0, Pc], R_max/N, R_max)
mass_ns1.append(sol["y0"][-1]/2.e33)
radius_ns1.append(sol["x"][-1]/1.e5)
# a result here
We see that we are approaching that maximum mass as \(R \rightarrow 0\), as expected.
c. - Case II: GR gravity (TOV) + neutron degeneracy#
Evalue the cell below, plot the resulting R vs. M plot.
Compute the maximum allowed mass under these assumptions.
Is this mass less or more than the previous? Qualitatively, what lead to the change in the maximum mass from looking at the R-M profile?
ns2 = Integrator(rhs_tov, rhs_args=[rho_deg],
stop_cond=[("LT", 0.0), ("LT", 0.0)])
R_max = 5.e6
N = 1000
# my vector is [m, p]
mass_ns2 = []
radius_ns2 = []
for rhoc in np.logspace(np.log10(5.e14), np.log10(5.e16), 100):
Pc = pres_deg(rhoc)
sol = ns2.integrate([0.0, Pc], R_max/N, R_max)
mass_ns2.append(sol["y0"][-1]/2.e33)
radius_ns2.append(sol["x"][-1]/1.e5)
# c results here
Now, adding GR, we see that there is a maximum mass for the neutron star, but it is quite low.
d - Case III: GR gravity (TOV) + nuclear EOS#
Next, let’s use a more accuracte equation of state using an analtical form for the nuclear EoS defined above.
Evaluate the cell below, plot the result.
Compute the maximum NS mass allowed under these assumptions.
Qualitatively describe the new M-R profile when changing the EoS compared to the previous profile in c.
ns3 = Integrator(rhs_tov, rhs_args=[rho_nuc],
stop_cond=[("LT", 0.0), ("LT", 0.0)])
R_max = 5.e6
N = 1000
# my vector is [m, p]
mass_ns3 = []
radius_ns3 = []
for rhoc in np.logspace(np.log10(5.e14), np.log10(5.e16), 100):
Pc = pres_nuc(rhoc)
sol = ns3.integrate([0.0, Pc], R_max/N, R_max)
mass_ns3.append(sol["y0"][-1]/2.e33)
radius_ns3.append(sol["x"][-1]/1.e5)
# d results here
In this final case, we get a much more reasonable maximum neutron star mass. We also see that for some values of radius there are multiple masses (the M-R relation is double-valued) – this is okay. However, at the very highest central densities, we get a smaller mass than the maximum at a smaller radius – that is not physical, and is dynamically unstable.
e - The impact of the compressibility modulus#
Plot the results of
c
andd
on the same plot.Looking only at the respective maximum mass for each profile, label which relation suggests a “softer” EoS and which suggests a “stiffer” EoS.
Qualitatively describe the difference between the “softer” and “stiffer” EoS, what that means for the incompressibility of the matter and the maximum mass.