Screening Rates#
Consider a nuclear fusion between two ions characterized by their atomic number, \(Z\), and atomic weight number, \(A\), surrounded by a sea of electrons.
The electron screening effect reduces the effective Coulomb potential barrier that the ions must overcome for fusion to take place, causing an enhancement to the nuclear reaction rates. Suppose the unscreened rate is represented by \(R_\mathrm{uns}\), then screened rate is
where \(h\) characterizes the screening magnitude. \(h\) generally depends on the density, temperature, composition, as well as \(Z\) and \(A\) of the two nuclei of the entrance channel, i.e.
Screening implementations#
pynucastro currently has 4 screening implementations that computes \(h\) for the fusion process between two ions:
potekhin_1998based on Chabrier and Potekhin [1998]chugunov_2007based on Chugunov et al. [2007]chugunov_2009based on Chugunov and DeWitt [2009]
Each of these use various approximations to account for the influence of the plasma on screening the electric charge of the nuclei that are fusing.
N-body Reaction#
Now consider a general reaction of the from:
In order to extend the screening routine for a binary nuclear reaction to a general N-body reaction, consider the formation of an intermediate composite nuclei, \(O\), i.e.
By breaking up the N-body reaction into N-1 sub-reaction with the formation of intermedite composite nuclei, the overall screening magnitude is then
Note
If the screening routine for a binary reaction takes the form:
then it is straight forward to see that with the consideration of the formation of intermediate composite nuclei, \(h\) for a N-body reaction takes the form
The only screening routine in pynucastro that takes this form is potekhin_1998
import math
import pynucastro as pyna
For each rate, the reactant ion pairs needed to compute the overall screening factor is stored in the attribute screening_pairs.
A simple demonstration with the triple alpha rate is the following
reaclib_library = pyna.ReacLibLibrary()
triple_alpha = reaclib_library.get_rate_by_name("a(aa,g)c12")
triple_alpha.screening_pairs
[(He4, He4), (He4, Be8)]
Here we see that there are two screening pairs for the triple alpha rate. The first step involves two \({}^4\mathrm{He}\) producing \({}^8\mathrm{Be}\), and the second step involves fusing \({}^4\mathrm{He}\) and \({}^8\mathrm{Be}\) giving the final product \({}^{12}\mathrm{C}\), i.e.
Using screening#
To compute the screening factor for a rate, we need to know the entire composition (since the electron number density matters) as well as the two nuclei involved in the reaction. There are two special types that store this information:
PlasmaState: stores the aggregate information about the entire plasmaScreenFactors: stores the information about the two nuclei
We create these objects separate before we call the screening routine since there are some computational expensive calculations here that don’t change with repeated calls.
Here we create a PlasmaState that knows about the conditions where the screening is taking place and then we use that to compute the screening factor for a rate. The helper function
make_plasma_state can create the PlasmaState object
nuclei = [pyna.Nucleus("h1"),
pyna.Nucleus("he4"),
pyna.Nucleus("c12"),
pyna.Nucleus("o16"),
pyna.Nucleus("n14"),
pyna.Nucleus("ca40")]
comp = pyna.Composition(nuclei)
comp.set_solar_like()
dens = 1.e6
temp = 1.e8
plasma = pyna.make_plasma_state(temp, dens, comp.get_molar())
Now let’s get the \({}^{12}\mathrm{C}(\alpha,\gamma){}^{16}\mathrm{O}\) rate and compute the screening factor
rfilter = pyna.RateFilter(reactants=["c12", "he4"], products=["o16"])
r = reaclib_library.filter(rfilter).get_rates()[0]
r
C12 + He4 ⟶ O16 + 𝛾
For the rate, we need the ScreenFactors object
scn_fac = pyna.make_screen_factors(r.ion_screen[0], r.ion_screen[1])
Finally, we’ll select the Chugunov (2009) screening and compute the screening factor. All of the needed thermodynamic information is contained in the PlasmaState and all of the needed
reaction rate information is contained in the ScreenFactors
Note
All screening routines return \(h\) = log(screening). Therefore, in order to get the actual multiplicative rate enhancement factor, we need to do an exp() afterwards.
from pynucastro.screening import chugunov_2009
log_scn = chugunov_2009(plasma, scn_fac)
scn = math.exp(log_scn)
print(f"{scn:12.8f}")
4.42076841
In this case, we see that screening enhances the rate by over 4 times!
Screening pair set#
For a RateCollection or a network derived from it, there are a lot of rates that will need to be screened, and some might have the same nuclei that need to be screened. A “screening pair set” keeps track of all the unique pairs of reactants used for screening the rates.
The screening pair set is a set of tuples that contain pairs of nuclei that need a screening factor computed.
Here’s an example: let’s build a helium and carbon burning network.
mynet = reaclib_library.linking_nuclei(["p", "n", "he4", "c12", "o16",
"na23", "mg24", "ne20"])
pynet = pyna.PythonNetwork(libraries=[mynet])
From the RateCollection, we can get the screening pair set using the get_screening_pair_set function:
from pynucastro.screening import get_screening_pair_set
screening_pair_set = get_screening_pair_set(pynet.get_rates())
for s in screening_pair_set:
print(s)
(He4, C12)
(C12, C12)
(He4, He4)
(He4, O16)
(He4, Ne20)
(p, Na23)
(C12, O16)
(He4, Mg24)
(He4, Be8)
Screening and a python network#
When we write out the module that defines a python network, it contains all of the information needed to define the righthand side and Jacobian. By default, screening is not used, but the rhs and jacobian functions can take an optional argument that is the name of the screening function to use.
Here we demonstrate this.
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp
pynet.write_network("cburn.py")
import cburn
First we’ll run with screening. Here we pass the name of the screening function to the rhs and jacobian function through the args keyword
We’ll pick conditions appropriate for the center of a Chandra mass white dwarf just after a carbon flame ignites.
rho = 1.e9
T = 2.e9
X0 = np.zeros(cburn.nnuc)
X0[cburn.jc12] = 0.5
X0[cburn.jo16] = 0.5
Y0 = X0/cburn.A
tmax = 1000.0
sol = solve_ivp(cburn.rhs, [0, tmax], Y0, method="BDF", jac=cburn.jacobian,
dense_output=True, args=(rho, T, chugunov_2009), rtol=1.e-6, atol=1.e-10)
Now we run without screening:
sol_noscreen = solve_ivp(cburn.rhs, [0, tmax], Y0, method="BDF", jac=cburn.jacobian,
dense_output=True, args=(rho, T), rtol=1.e-6, atol=1.e-10)
and we can plot the two cases together. The non-screened X’s will be shown with a dotted line.
fig = plt.figure()
ax = fig.add_subplot(111)
threshold = 1.e-4
icolor = 0
for i in range(cburn.nnuc):
if (sol.y[i,:]).max() > threshold:
ax.loglog(sol.t, sol.y[i,:] * cburn.A[i],
label=f"X({cburn.names[i].capitalize()})",
color=f"C{icolor}")
ax.loglog(sol_noscreen.t, sol_noscreen.y[i,:] * cburn.A[i],
linestyle=":", color=f"C{icolor}")
icolor += 1
ax.set_ylim(1.e-8, 1.0)
ax.legend(fontsize="small")
ax.set_xlabel("t (s)")
ax.set_ylabel("X")
fig.set_size_inches((8, 6))
As expected, using screening makes the carbon burn much more quickly.