Source code for fiasco.fiasco

"""
Package-level functions.
"""
import astropy.units as u
import numpy as np
import plasmapy.particles

from plasmapy.particles.exceptions import InvalidParticleError
from scipy.interpolate import interp1d

import fiasco

from fiasco.io import DataIndexer
from fiasco.util import parse_ion_name

__all__ = ['list_elements', 'list_ions', 'proton_electron_ratio', 'get_isoelectronic_sequence']


[docs] def list_elements(hdf5_dbase_root=None, sort=True): """ List all available elements in the CHIANTI database. Parameters ---------- hdf5_dbase_root: path-like, optional If not specified, will default to that specified in ``fiasco.defaults``. sort: `bool`, optional If True, sort the list of elements by increasing atomic number. """ if hdf5_dbase_root is None: hdf5_dbase_root = fiasco.defaults['hdf5_dbase_root'] elements = [] root = DataIndexer.create_indexer(hdf5_dbase_root, '/') for f in root.fields: try: elements.append(plasmapy.particles.atomic_symbol(f.capitalize())) except InvalidParticleError: continue if sort: elements = sorted(elements, key=lambda x: plasmapy.particles.atomic_number(x)) return elements
[docs] def list_ions(hdf5_dbase_root=None, sort=True): """ List all available ions in the CHIANTI database Parameters ---------- hdf5_dbase_root: path-like, optional If not specified, will default to that specified in ``fiasco.defaults``. sort: `bool`, optional If True, sort the list of elements by increasing atomic number. """ if hdf5_dbase_root is None: hdf5_dbase_root = fiasco.defaults['hdf5_dbase_root'] root = DataIndexer(hdf5_dbase_root, '/') # NOTE: get the list from the index if possible. This is ~30x faster try: ions = root['ion_index'] except KeyError: ions = [] for f in root.fields: try: el = plasmapy.particles.atomic_symbol(f.capitalize()) for i in root[f].fields: if f == i.split('_')[0]: ions.append(f"{el} {i.split('_')[1]}") except InvalidParticleError: continue # Optional because adds significant overhead if sort: ions = sorted(ions, key=lambda x: (plasmapy.particles.atomic_number(x.split()[0]), int(x.split()[1]))) # NOTE: when grabbing straight from the index and not sorting, the result will be # a numpy array. Cast to a list to make sure the return type is consistent for # all possible inputs return ions.tolist() if isinstance(ions, np.ndarray) else ions
[docs] def get_isoelectronic_sequence(element, hdf5_dbase_root=None): """ List of ions in the isoelectronic sequence of ``element``. Ions in the same isoelectronic sequence, that is, ions that have the same number of bound electrons, :math:`Z - z`, often share common properties despite having different charge states and being from different elements. These so-called isoelectronic sequences are typically denoted by the element with an atomic number equal to the number of bound electrons, e.g. C II is in the boron isoelectronic sequence or equivalently may be said to be boron-like. Given the name of a sequence, as denoted by an element label, this function returns a list of all ions in that sequence. Parameters ---------- element: `str`, `int` Name of sequence. Can be either the full name (e.g. "hydrogren"), the atomic symbol (e.g. "H") or the atomic number (e.g. 1) hdf5_dbase_root: path-like, optional If not specified, will default to that specified in ``fiasco.defaults``. """ Z_iso = plasmapy.particles.atomic_number(element) all_ions = list_ions(hdf5_dbase_root=hdf5_dbase_root) def _is_in_sequence(ion): Z, z = parse_ion_name(ion) return Z_iso == (Z - z + 1) return [ion for ion in all_ions if _is_in_sequence(ion)]
[docs] @u.quantity_input def proton_electron_ratio(temperature: u.K, **kwargs): """ Calculate ratio between proton and electron densities as a function of temperature according to Eq. 7 of :cite:t:`young_chianti-atomic_2003`. Parameters ---------- temperature : `~astropy.units.Quantity` See Also -------- fiasco.Ion : Accepts same keyword arguments for setting database and dataset names """ # Import here to avoid circular imports from fiasco import log h_2 = fiasco.Ion('H +1', temperature, **kwargs) numerator = h_2.abundance * h_2._ion_fraction[h_2._instance_kwargs['ionization_fraction']]['ionization_fraction'] denominator = u.Quantity(np.zeros(numerator.shape)) for el_name in list_elements(h_2.hdf5_dbase_root): el = fiasco.Element(el_name, temperature, **h_2._instance_kwargs) try: abundance = el.abundance except KeyError: abund_file = el[0]._instance_kwargs['abundance'] log.warning( f'Not including {el.atomic_symbol}. Abundance not available from {abund_file}.') continue for ion in el: ionization_file = ion._instance_kwargs['ionization_fraction'] # NOTE: We use ._ion_fraction here rather than .ionization_fraction to avoid # doing an interpolation to the temperature array every single time and instead only # interpolate once at the end. # It is assumed that the ionization_fraction temperature array for each ion is the same. try: ionization_fraction = ion._ion_fraction[ionization_file]['ionization_fraction'] t_ionization_fraction = ion._ion_fraction[ionization_file]['temperature'] except KeyError: log.warning( f'Not including {ion.ion_name}. Ionization fraction not available from {ionization_file}.') continue denominator += ionization_fraction * abundance * ion.charge_state ratio = numerator / denominator f_interp = interp1d(t_ionization_fraction.to(temperature.unit).value, ratio.value, kind='linear', bounds_error=False, fill_value=(ratio[0], ratio[-1])) return u.Quantity(f_interp(temperature.value))