Checking your network
Selecting which rates to include and which to exclude from your network is a bit of an art. pynucastro has a few tools to help check what you might be missing.
[1]:
import pynucastro as pyna
[2]:
reaclib_library = pyna.ReacLibLibrary()
RateCollection
validate method
Let’s start by trying to create a network for carbon burning.
To start, let’s pick the nuclei \(\alpha\), \({}^{12}\mathrm{C}\) and \({}^{20}\mathrm{Ne}\)
[3]:
nuclei = ["he4", "c12", "ne20"]
cburn_library = reaclib_library.linking_nuclei(nuclei)
Now we can make a rate collection
[4]:
rc = pyna.RateCollection(libraries=[cburn_library])
rc
[4]:
C12 ⟶ He4 + He4 + He4
C12 + C12 ⟶ He4 + Ne20
Ne20 + He4 ⟶ C12 + C12
He4 + He4 + He4 ⟶ C12 + 𝛾
Now, since we are primarily interested in \({}^{12}\mathrm{C} + {}^{12}\mathrm{C}\), let’s make sure we are not missing any other reactions that have the same reactants. The validate()
method will do this, by comparing the rates we have selected to another library.
[5]:
rc.validate(reaclib_library)
validation: Ne20 produced in C12 + C12 ⟶ He4 + Ne20 never consumed.
validation: missing He4 + He4 + He4 ⟶ p + B11 as alternative to He4 + He4 + He4 ⟶ C12 + 𝛾 (Q = -8.682 MeV).
validation: missing He4 + He4 + He4 ⟶ n + C11 as alternative to He4 + He4 + He4 ⟶ C12 + 𝛾 (Q = -11.4466 MeV).
validation: missing C12 + C12 ⟶ p + Na23 as alternative to C12 + C12 ⟶ He4 + Ne20 (Q = 2.242 MeV).
validation: missing C12 + C12 ⟶ n + Mg23 as alternative to C12 + C12 ⟶ He4 + Ne20 (Q = -2.598 MeV).
[5]:
False
This tells us that we are missing 2 branches of the \({}^{12}\mathrm{C} + {}^{12}\mathrm{C}\) reaction. ReacLib already scales the rates based on the branching of the products, so we should try to include these other branches.
Note: by default, validate()
only checks forward rates.
To address these issues, we need to include the additional nuclei. In particular, the branch that makes \({}^{23}\mathrm{Na}\) is likely important (the rate making \({}^{23}\mathrm{Mg}\) is endothermic, so less likely).
[6]:
nuclei += ["p", "na23"]
cburn_library = reaclib_library.linking_nuclei(nuclei)
rc = pyna.RateCollection(libraries=[cburn_library])
rc
[6]:
C12 ⟶ He4 + He4 + He4
C12 + C12 ⟶ p + Na23
C12 + C12 ⟶ He4 + Ne20
Ne20 + He4 ⟶ p + Na23
Ne20 + He4 ⟶ C12 + C12
Na23 + p ⟶ He4 + Ne20
Na23 + p ⟶ C12 + C12
He4 + He4 + He4 ⟶ C12 + 𝛾
[7]:
rc.validate(reaclib_library)
validation: Ne20 produced in C12 + C12 ⟶ He4 + Ne20 never consumed.
validation: Ne20 produced in Na23 + p ⟶ He4 + Ne20 never consumed.
validation: missing He4 + He4 + He4 ⟶ p + B11 as alternative to He4 + He4 + He4 ⟶ C12 + 𝛾 (Q = -8.682 MeV).
validation: missing He4 + He4 + He4 ⟶ n + C11 as alternative to He4 + He4 + He4 ⟶ C12 + 𝛾 (Q = -11.4466 MeV).
validation: missing C12 + C12 ⟶ n + Mg23 as alternative to C12 + C12 ⟶ He4 + Ne20 (Q = -2.598 MeV).
validation: missing C12 + C12 ⟶ n + Mg23 as alternative to C12 + C12 ⟶ p + Na23 (Q = -2.598 MeV).
validation: missing Na23 + p ⟶ n + Mg23 as alternative to Na23 + p ⟶ He4 + Ne20 (Q = -4.839 MeV).
validation: missing Na23 + p ⟶ Mg24 + 𝛾 as alternative to Na23 + p ⟶ He4 + Ne20 (Q = 11.6927 MeV).
[7]:
False
Now, looking at what is missing, we probably want to include \({}^{24}\mathrm{Mg}\) as an endpoint for carbon burning.
[8]:
nuclei += ["mg24"]
cburn_library = reaclib_library.linking_nuclei(nuclei)
rc = pyna.RateCollection(libraries=[cburn_library])
rc
[8]:
Mg24 ⟶ p + Na23
Mg24 ⟶ He4 + Ne20
C12 ⟶ He4 + He4 + He4
Ne20 + He4 ⟶ Mg24 + 𝛾
Na23 + p ⟶ Mg24 + 𝛾
C12 + C12 ⟶ p + Na23
C12 + C12 ⟶ He4 + Ne20
Ne20 + He4 ⟶ p + Na23
Ne20 + He4 ⟶ C12 + C12
Na23 + p ⟶ He4 + Ne20
Na23 + p ⟶ C12 + C12
He4 + He4 + He4 ⟶ C12 + 𝛾
[9]:
rc.validate(reaclib_library)
validation: Mg24 produced in Ne20 + He4 ⟶ Mg24 + 𝛾 never consumed.
validation: Mg24 produced in Na23 + p ⟶ Mg24 + 𝛾 never consumed.
validation: missing He4 + He4 + He4 ⟶ p + B11 as alternative to He4 + He4 + He4 ⟶ C12 + 𝛾 (Q = -8.682 MeV).
validation: missing He4 + He4 + He4 ⟶ n + C11 as alternative to He4 + He4 + He4 ⟶ C12 + 𝛾 (Q = -11.4466 MeV).
validation: missing C12 + C12 ⟶ n + Mg23 as alternative to C12 + C12 ⟶ He4 + Ne20 (Q = -2.598 MeV).
validation: missing C12 + C12 ⟶ n + Mg23 as alternative to C12 + C12 ⟶ p + Na23 (Q = -2.598 MeV).
validation: missing Ne20 + He4 ⟶ n + Mg23 as alternative to Ne20 + He4 ⟶ Mg24 + 𝛾 (Q = -7.21457 MeV).
validation: missing Na23 + p ⟶ n + Mg23 as alternative to Na23 + p ⟶ He4 + Ne20 (Q = -4.839 MeV).
validation: missing Na23 + p ⟶ n + Mg23 as alternative to Na23 + p ⟶ Mg24 + 𝛾 (Q = -4.839 MeV).
[9]:
False
This now seems reasonable. The reactions that are missing are all endothermic.
[10]:
fig = rc.plot(curved_edges=True)
Finding duplicate links in a RateCollection
Sometimes we combine multiple sources of rates into a RateCollection
or network, and we run the risk of having the same rate sequence duplicated, even though the rate itself may be different (i.e., a different source or tabulated vs. ReacLib).
Here we see how to check a RateCollection
for duplicates.
We’ll start by recreating the electron-capture supernova network explored earlier.
First we get rates from ReacLib
[11]:
all_nuclei = ["p", "he4",
"ne20", "o20", "f20",
"mg24", "al27", "o16",
"si28", "s32", "p31"]
ecsn_rl_library = reaclib_library.linking_nuclei(all_nuclei,with_reverse=True)
Next we get some tabulated rates
[12]:
tl = pyna.TabularLibrary()
ecsn_tabular_library = tl.linking_nuclei(["f20", "o20", "ne20"])
Now we build a single Library
using these two libraries
[13]:
complete_library = ecsn_rl_library + ecsn_tabular_library
If we print out the rates, we see that there are duplicates involving beta decays
[14]:
complete_library.get_rates()
[14]:
[O20 ⟶ F20 + e⁻ + 𝜈,
F20 ⟶ Ne20 + e⁻ + 𝜈,
Ne20 ⟶ He4 + O16,
Mg24 ⟶ He4 + Ne20,
Si28 ⟶ p + Al27,
Si28 ⟶ He4 + Mg24,
P31 ⟶ He4 + Al27,
S32 ⟶ p + P31,
S32 ⟶ He4 + Si28,
O16 + He4 ⟶ Ne20 + 𝛾,
Ne20 + He4 ⟶ Mg24 + 𝛾,
Mg24 + He4 ⟶ Si28 + 𝛾,
Al27 + p ⟶ Si28 + 𝛾,
Al27 + He4 ⟶ P31 + 𝛾,
Si28 + He4 ⟶ S32 + 𝛾,
P31 + p ⟶ S32 + 𝛾,
O16 + O16 ⟶ p + P31,
O16 + O16 ⟶ He4 + Si28,
Mg24 + He4 ⟶ p + Al27,
Al27 + p ⟶ He4 + Mg24,
Si28 + He4 ⟶ p + P31,
Si28 + He4 ⟶ O16 + O16,
P31 + p ⟶ He4 + Si28,
P31 + p ⟶ O16 + O16,
F20 ⟶ Ne20 + e⁻ + 𝜈,
F20 + e⁻ ⟶ O20 + 𝜈,
Ne20 + e⁻ ⟶ F20 + 𝜈,
O20 ⟶ F20 + e⁻ + 𝜈]
The find_duplicate_links()
method will find these
[15]:
dupes = complete_library.find_duplicate_links()
dupes
[15]:
[[O20 ⟶ F20 + e⁻ + 𝜈, O20 ⟶ F20 + e⁻ + 𝜈],
[F20 ⟶ Ne20 + e⁻ + 𝜈, F20 ⟶ Ne20 + e⁻ + 𝜈]]
Here we see 2 pairs of duplicates. Looking at the first
[16]:
for rate in dupes[0]:
print(f"{rate.fname:20} {type(rate)}")
O20__F20__weak__wc12 <class 'pynucastro.rates.rate.ReacLibRate'>
O20__F20 <class 'pynucastro.rates.rate.TabularRate'>
We see that one is a ReacLibRate
and the other is a TabularRate
. In general, the TabularRate
is preferred, since it has a density and temperature dependence and works at high densities.
We can use this information to remove the undesired rate from the RateCollection
using the remove_rates()
method.
[17]:
from pynucastro.rates import ReacLibRate
rates_to_remove = []
for d in dupes:
rates_to_remove += [r for r in d if isinstance(r, ReacLibRate)]
[18]:
for r in rates_to_remove:
complete_library.remove_rate(r)
Now we see only a single instance of these links
[19]:
complete_library.get_rates()
[19]:
[Ne20 ⟶ He4 + O16,
Mg24 ⟶ He4 + Ne20,
Si28 ⟶ p + Al27,
Si28 ⟶ He4 + Mg24,
P31 ⟶ He4 + Al27,
S32 ⟶ p + P31,
S32 ⟶ He4 + Si28,
O16 + He4 ⟶ Ne20 + 𝛾,
Ne20 + He4 ⟶ Mg24 + 𝛾,
Mg24 + He4 ⟶ Si28 + 𝛾,
Al27 + p ⟶ Si28 + 𝛾,
Al27 + He4 ⟶ P31 + 𝛾,
Si28 + He4 ⟶ S32 + 𝛾,
P31 + p ⟶ S32 + 𝛾,
O16 + O16 ⟶ p + P31,
O16 + O16 ⟶ He4 + Si28,
Mg24 + He4 ⟶ p + Al27,
Al27 + p ⟶ He4 + Mg24,
Si28 + He4 ⟶ p + P31,
Si28 + He4 ⟶ O16 + O16,
P31 + p ⟶ He4 + Si28,
P31 + p ⟶ O16 + O16,
F20 ⟶ Ne20 + e⁻ + 𝜈,
F20 + e⁻ ⟶ O20 + 𝜈,
Ne20 + e⁻ ⟶ F20 + 𝜈,
O20 ⟶ F20 + e⁻ + 𝜈]
and we can make a RateCollection
now
[20]:
rc = pyna.RateCollection(libraries=[complete_library])