Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
input_err_msg = (
"The inputs to nuclear_reaction_energy should be either "
"a string representing a nuclear reaction (e.g., "
"'D + T -> He-4 + n') or the keywords 'reactants' and "
"'products' as lists with the nucleons or particles "
"involved in the reaction (e.g., reactants=['D', 'T'] "
"and products=['He-4', 'n']."
)
reaction_string_is_input = args and not kwargs and len(args) == 1
reactants_products_are_inputs = kwargs and not args and len(kwargs) == 2
if reaction_string_is_input == reactants_products_are_inputs:
raise AtomicError(input_err_msg)
if reaction_string_is_input:
reaction = args[0]
if not isinstance(reaction, str):
raise TypeError(input_err_msg)
elif "->" not in reaction:
raise AtomicError(
f"The reaction '{reaction}' is missing a '->'"
" or '-->' between the reactants and products."
)
try:
LHS_string, RHS_string = re.split("-+>", reaction)
LHS_list = re.split(r" \+ ", LHS_string)
def ionic_fractions(self, fractions):
"""
Set the ionic fractions, while checking that the new values are
valid and normalized to one.
"""
if fractions is None or np.all(np.isnan(fractions)):
self._ionic_fractions = np.full(
self.atomic_number + 1, np.nan, dtype=np.float64
)
return
try:
if np.min(fractions) < 0:
raise AtomicError("Cannot have negative ionic fractions.")
if len(fractions) != self.atomic_number + 1:
raise AtomicError(
"The length of ionic_fractions must be "
f"{self.atomic_number + 1}."
)
if isinstance(fractions, u.Quantity):
fractions = fractions.to(u.m ** -3)
self.n_elem = np.sum(fractions)
self._ionic_fractions = np.array(fractions / self.n_elem)
else:
fractions = np.array(fractions, dtype=np.float64)
sum_of_fractions = np.sum(fractions)
all_nans = np.all(np.isnan(fractions))
def number_densities(self, value: u.m ** -3):
"""Set the number densities for each state."""
if np.any(value.value < 0):
raise AtomicError("Number densities cannot be negative.")
if len(value) != self.atomic_number + 1:
raise AtomicError(
f"Incorrect number of charge states for " f"{self.base_particle}"
)
value = value.to(u.m ** -3)
self._n_elem = value.sum()
self._ionic_fractions = value / self._n_elem
True
>>> IonizationState('H', [1, 0], tol=1e-8) == IonizationState('H', [1, 1e-6], tol=1e-5)
False
"""
if not isinstance(other, IonizationState):
raise TypeError(
"An instance of the IonizationState class may only be "
"compared with another IonizationState instance."
)
same_element = self.element == other.element
same_isotope = self.isotope == other.isotope
if not same_element or not same_isotope:
raise AtomicError(
"An instance of the IonizationState class may only be "
"compared with another IonizationState instance if "
"both correspond to the same element and/or isotope."
)
# Use the tighter of the two tolerances. For thermodynamic
# quantities, use it as a relative tolerance because the values
# may substantially depart from order unity.
min_tol = np.min([self.tol, other.tol])
same_T_e = (
np.isnan(self.T_e)
and np.isnan(other.T_e)
or u.allclose(self.T_e, other.T_e, rtol=min_tol * u.K, atol=0 * u.K)
)
def n(self, n: u.m ** -3):
"""Set the number density scaling factor."""
try:
n = n.to(u.m ** -3)
except u.UnitConversionError as exc:
raise AtomicError("Units cannot be converted to u.m ** -3.") from exc
except Exception as exc:
raise AtomicError(f"{n} is not a valid number density.") from exc
if n < 0 * u.m ** -3:
raise AtomicError("Number density cannot be negative.")
self._pars["n"] = n.to(u.m ** -3)
kappa: Real = np.inf,
):
abundances_provided = abundances is not None or log_abundances is not None
set_abundances = True
if isinstance(inputs, dict):
all_quantities = np.all(
[isinstance(fracs, u.Quantity) for fracs in inputs.values()]
)
if all_quantities:
right_units = np.all(
[fracs[0].si.unit == u.m ** -3 for fracs in inputs.values()]
)
if not right_units:
raise AtomicError(
"Units must be inverse volume for number densities."
)
if abundances_provided:
raise AtomicError(
"Abundances cannot be provided if inputs "
"provides number density information."
)
set_abundances = False
try:
self._pars = collections.defaultdict(lambda: None)
self.T_e = T_e
self.n = n
self.tol = tol
self.ionic_fractions = inputs
if set_abundances:
for key in _elements_and_isotopes:
new_n += n_elems[key]
self.n = new_n
new_abundances = {}
for key in _elements_and_isotopes:
new_abundances[key] = np.float(n_elems[key] / self.n)
self._pars["abundances"] = new_abundances
elif isinstance(inputs, (list, tuple)):
try:
_particle_instances = [Particle(particle) for particle in inputs]
except (InvalidParticleError, TypeError) as exc:
raise AtomicError("Invalid inputs to IonizationStates.") from exc
_particle_instances.sort(
key=lambda p: (p.atomic_number, p.mass_number if p.isotope else 0)
)
_elements_and_isotopes = [
particle.particle for particle in _particle_instances
]
new_ionic_fractions = {
particle.particle: np.full(
particle.atomic_number + 1, fill_value=np.nan, dtype=np.float64
)
for particle in _particle_instances
}
else:
raise TypeError("Incorrect inputs to set ionic_fractions.")
new_ionic_fractions = {
particle.particle: np.full(
particle.atomic_number + 1, fill_value=np.nan, dtype=np.float64
)
for particle in _particle_instances
}
else:
raise TypeError("Incorrect inputs to set ionic_fractions.")
for i in range(1, len(_particle_instances)):
if _particle_instances[i - 1].element == _particle_instances[i].element:
if (
not _particle_instances[i - 1].isotope
and _particle_instances[i].isotope
):
raise AtomicError(
"Cannot have an element and isotopes of that element."
)
self._particle_instances = _particle_instances
self._base_particles = _elements_and_isotopes
self._ionic_fractions = new_ionic_fractions
def number_densities(self, value: u.m ** -3):
"""Set the number densities for each state."""
if np.any(value.value < 0):
raise AtomicError("Number densities cannot be negative.")
if len(value) != self.atomic_number + 1:
raise AtomicError(
f"Incorrect number of charge states for " f"{self.base_particle}"
)
value = value.to(u.m ** -3)
self._n_elem = value.sum()
self._ionic_fractions = value / self._n_elem
"Cannot simultaneously provide number density "
"through both n_elem and ionic_fractions."
)
self.n_elem = n_elem
self.ionic_fractions = ionic_fractions
if ionic_fractions is None and not np.isnan(self.T_e):
warnings.warn(
"Collisional ionization equilibration has not yet "
"been implemented in IonizationState; cannot set "
"ionic fractions."
)
except Exception as exc:
raise AtomicError(
f"Unable to create IonizationState instance for "
f"{particle.particle}."
) from exc