"""This module handles `webnucleo <https://webnucleo.readthedocs.io>`_ nuclear reaction networks."""
import numpy as np
import wnnet.nuc as wn
import wnnet.reac as wr
import wnnet.consts as wc
[docs]
class Net(wn.Nuc, wr.Reac):
"""A class to store webnucleo networks.
Args:
``file`` (:obj:`str`): A string giving the name of the XML file with\
the network data.
``nuc_xpath`` (:obj:`str`, optional): An XPath expression to select\
nuclides. Default is all nuclides.
``reac_xpath`` (:obj:`str`, optional): An XPath expression to select\
reactions. Default is all reactions.
"""
def __init__(self, file, nuc_xpath="", reac_xpath=""):
wn.Nuc.__init__(self, file, nuc_xpath=nuc_xpath)
wr.Reac.__init__(self, file, reac_xpath=reac_xpath)
self.valid_reactions = {}
self.valid_reactions[("", "")] = self.get_valid_reactions(
nuc_xpath=nuc_xpath, reac_xpath=reac_xpath
)
[docs]
def compute_q_values(self, nuc_xpath="", reac_xpath=""):
"""A method to compute reaction Q values for valid reactions in the network.
Args:
``nuc_xpath`` (:obj:`str`, optional): An XPath expression to select\
the nuclides.
``reac_xpath`` (:obj:`str`, optional): An XPath expression to\
select the reactions.
Returns:
A :obj:`dict`. The keys for the dictionary are the reaction\
strings and the values are the corresponding Q values.
"""
result = {}
for reac in self.get_valid_reactions(
nuc_xpath=nuc_xpath, reac_xpath=reac_xpath
):
tmp = self.compute_reaction_q_value(reac)
if tmp:
result[reac] = tmp
return result
[docs]
def get_valid_reactions(self, nuc_xpath="", reac_xpath=""):
"""Method to retrieve the valid reactions in the network.
Args:
``nuc_xpath`` (:obj:`str`, optional): An XPath expression to\
select nuclides. Default is all nuclides.
``reac_xpath`` (:obj:`str`, optional): An XPath expression to\
select reactions. Default is all reactions.
Returns:
A :obj:`dict` of `wnutils <https://wnutils.readthedocs.io>`_ \
reactions.
"""
if (nuc_xpath, reac_xpath) not in self.valid_reactions:
result = {}
reactions = self.get_reactions(reac_xpath=reac_xpath)
for reac in reactions:
if self.is_valid_reaction(reac, nuc_xpath=nuc_xpath):
result[reac] = reactions[reac]
self.valid_reactions[(nuc_xpath, reac_xpath)] = result
return self.valid_reactions[(nuc_xpath, reac_xpath)]
[docs]
def compute_reaction_q_value(self, name):
"""Method to compute the Q value for a reaction.
Args:
``name`` (:obj:`str`): A string giving the reaction.
Returns:
A :obj:`float` giving the Q value for the reaction or None if the\
reaction is not valid.
"""
nuclides = self.get_nuclides()
reaction = self.get_reactions()[name]
result = 0
for spec in reaction.nuclide_reactants:
if spec not in nuclides:
return None
result += nuclides[spec]["mass excess"]
for spec in reaction.nuclide_products:
if spec not in nuclides:
return None
result -= nuclides[spec]["mass excess"]
if (
"positron" in reaction.products
and "neutrino_e" in reaction.products
):
result -= 2.0 * wc.m_e_in_MeV
return result
def _obeys_conservation_laws(self, nuclides, reaction):
e_p = {"electron": -1, "positron": 1}
e_lepton = {
"electron": 1,
"positron": -1,
"neutrino_e": 1,
"anti-neutrino_e": -1,
}
mu_lepton = {"neutrino_mu": 1, "anti-neutrino_mu": -1}
tau_lepton = {"neutrino_tau": 1, "anti-neutrino_tau": -1}
d_z = 0
d_a = 0
d_l_e = 0
d_l_mu = 0
d_l_tau = 0
for spec in reaction.reactants:
if spec in nuclides:
d_a += nuclides[spec]["a"]
d_z += nuclides[spec]["z"]
if spec in e_p:
d_z += e_p[spec]
if spec in e_lepton:
d_l_e += e_lepton[spec]
if spec in mu_lepton:
d_l_mu += mu_lepton[spec]
if spec in tau_lepton:
d_l_tau += tau_lepton[spec]
for spec in reaction.products:
if spec in nuclides:
d_a -= nuclides[spec]["a"]
d_z -= nuclides[spec]["z"]
if spec in e_p:
d_z -= e_p[spec]
if spec in e_lepton:
d_l_e -= e_lepton[spec]
if spec in mu_lepton:
d_l_mu -= mu_lepton[spec]
if spec in tau_lepton:
d_l_tau -= tau_lepton[spec]
return (
d_a == 0
and d_z == 0
and d_l_e == 0
and d_l_mu == 0
and d_l_tau == 0
)
[docs]
def is_valid_reaction(self, name, nuc_xpath=""):
"""Method to determine a reaction is valid in the network.
Args:
``name`` (:obj:`str`): A string giving the reaction.
``nuclides`` (:obj:`str`, optional): An XPath expression\
to select nuclides. Default is all nuclides.
Returns:
A :obj:`bool` with value True if the reaction is valid and False\
if not.
"""
nuclides = self.get_nuclides(nuc_xpath=nuc_xpath)
reaction = self.get_reactions()[name]
if not self._obeys_conservation_laws(nuclides, reaction):
return False
for spec in reaction.nuclide_reactants + reaction.nuclide_products:
if spec not in nuclides:
return False
return True
[docs]
def compute_rates_for_reaction(self, name, t_9, user_funcs=""):
"""Method to compute the forward and reverse rates for a valid reaction.
Args:
``name`` (:obj:`str`): A string giving the reaction.
``t_9`` (:obj:`float`): The temperature in 10\\ :sup:`9` K at\
which to compute the rates.
``user_funcs`` (:obj:`dict`, optional): A dictionary of\
user-defined functions associated with a user_rate key.
Returns:
A two-element :obj:`tuple` with the first element being the\
forward rate and the second element being the reverse rate.\
If the reaction is not valid, returns None.
"""
if not self.is_valid_reaction(name):
return None
reaction = self.get_reactions()[name]
forward = reaction.compute_rate(t_9, user_funcs=user_funcs)
if self.is_weak_reaction(name) or len(reaction.reactants) == 1:
return (forward, 0)
d_exp = 0
for spec in reaction.nuclide_reactants:
d_exp += self.compute_nse_factor(spec, t_9, 1.0)
for spec in reaction.nuclide_products:
d_exp -= self.compute_nse_factor(spec, t_9, 1.0)
if d_exp < -300.0:
return (forward, 0)
if d_exp > 300.0:
return (0, 0)
tup = self.compute_reaction_duplicate_factors(name)
return (forward, np.exp(d_exp) * (tup[1] / tup[0]) * forward)
[docs]
def compute_rates(self, t_9, nuc_xpath="", reac_xpath="", user_funcs=""):
"""Method to compute the forward and reverse rates for valid\
reactions in a network.
Args:
``t_9`` (:obj:`float`): The temperature in 10\\ :sup:`9` K at\
which to compute the rates.
``nuc_xpath`` (:obj:`str`, optional): An XPath expression\
to select nuclides. Default is all nuclides.
``reac_xpath`` (:obj:`str`, optional): An XPath expression\
to select reactions. Default is all reactions.
``user_funcs`` (:obj:`dict`, optional): A dictionary of\
user-defined functions associated with a user_rate key.
Returns:
A :obj:`dict` containing the rates. The key is the reaction\
string while the value is a two-element :obj:`tuple` with the\
first element being the forward rate and the second element\
being the reverse rate.
"""
v_reactions = self.get_valid_reactions(
nuc_xpath=nuc_xpath, reac_xpath=reac_xpath
)
result = {}
for reac in v_reactions:
result[reac] = self.compute_rates_for_reaction(
reac, t_9, user_funcs=user_funcs
)
return result