Installation and Usage

Dependencies

This package is tested with python versions 3.7 - 3.10. It might work on older python version as well, however, compatibility is not guaranteed.

The only dependency at the moment for running the iniabu package is numpy. There is currently no pinned numpy version and the latest one should be working great.

Installation

To install the latest stable version of iniabu, run this command in your terminal:

$ pip install iniabu

Alternatively you can install iniabu directly from GitHub. This will install the latest version. To do so, type in your terminal:

$ pip install git+https://github.com/galactic-forensics/iniabu.git

Available databases

Several databases are available to work with. The current default database is called “lodders09” and is based on Lodders et al. (2009). This might be updated in the future without considering it a breaking change. Old databases will always stay available. Further databases, listed by the string used to call them, are as following:

The solar abundances of all databases were converted to number abundances and are relative to Si = 106. Conversion to other units, as described below, is possible.

Note: Not all databases mentioned here contain the solar abundances for every isotope. If an operation you are trying to perform encounters a solar abundance value that is not availagle in the currently loaded database, the result will be returned as an np.nan, i.e., as not a number.

Usage

Here we give a short overview of the iniabu module. Please also have a look at the API Reference. There, each module is described in detail, often with examples on how to use the specific function.

Furthermore, some examples in the form of Jupyter notebooks can be found on GitHub in docs/jupyter_examples.

Importing the module

Once installed, you can simply import the package from your python session as:

>>> from iniabu import ini

This is the recommended import and will be used throughout the rest of this documentation, unless otherwise noted. Here, the ini instance will be loaded with the default database (currently Lodders et al., 2009) and using linear, number abundances. Alternatively you can directly import the database using number, logarithmic abundances or mass fractions. The respective imports for these are:

>>> from iniabu import inilog  # number logarithmic abundances
>>> from iniabu import inimf  # mass fraction

Note

While the current default database is Lodders et al. (2009) we will not consider loading a different default database a breaking change. If you want to always load Lodders et al. (2009), use the specific import below.

In case multiple databases are required at the same time, e.g., db1 using Lodders et al. (2009) and db2 using Asplund et al. (2009) values and number logarithmic units, the following import could be used:

>>> import iniabu
>>> db1 = iniabu.IniAbu(database="lodders09")
>>> db2 = iniabu.IniAbu(database="asplund09", unit="num_log")

Loading a database

Switching the data base from a given instance ini can be easily accomplished. For example, the “asplund09” database can easily be loaded into a given instance by calling:

>>> ini.database = "asplund09"

Note

Switching a database does not reset the units. For example: If “lodders09” is loaded using mass fractions and you load “asplund09” as the new database, the units will stay the same that are used by default. A message will be printed to reflect this.

>>> ini.database = 'asplund09'
iniabu loaded database: 'asplund09', current units: 'mass_fraction'

Available abundance units

Abundance units can easily be switched between linear number abundances, logarithmic number abundances, and mass fraction units.

In the linear number abundances case all abundances are linear with respect to each other and are normalized such that the abundance of silicon is equal to 106 by number.

The logarithmic number abundances are generally used in astronomy. For an element X, the logarithmic abundance is defined with respect to the abundance of hydrogen as:

\[\log_{10}(\epsilon_X) = \log_{10} \left(\frac{\mathrm{N}_\mathrm{X}}{\mathrm{N}_\mathrm{H}}\right) + 12\]

Mass fraction values are common in nucleosynthesis calculations. To return mass fraction values the database can be switched to mass_fraction. The abundances are then defined as following:

\[\begin{split}X_{i} = \frac{N_{i} m_{i}}{\rho N_{A}} \\\end{split}\]

Here \(X_{i}\) is the mass fraction of element \(i\), \(N_{i}\) its number abundance, \(m_{i}\) its molecular mass, and \(N_{N}\) Avogadro’s constant. The density \(\rho\) is defined as:

\[\rho = \frac{1}{N_{A}} \sum_i N_{i} m_{i}\]

To switch a given database between linear number abundance (“num_lin”), logarithmic number abundance (“num_log”) mode, and mass fraction mode (“mass_fraction”) the following property can be set:

>>> ini.unit == "num_log"

In this case, we would switch to logarithmic number abundance mode. To check what abundance unit is currently set, the following command can be used:

>>> ini.unit
"num_log"

By default, linear number abundance values are used.

Note

To use “num_log” or “mass_fraction” mode by default you can import the module in the following ways:

from iniabu import inilog  # "num_log" units
from iniabu import inimf  # "mass_fraction" units

Note

If you use “mass_fraction” units, the relative abundances of the isotopes are also given in mass fractions!

Element and isotope properties

Properties of an element are independent from the loaded database and are taken from the NIST database. To query the loaded database for relative or solar abundances, see the next two sections.

Querying an element:

To query an element’s properties with respect to the solar abundance, it can be loaded into a temporary variable. For example: To query silicon the element and its properties can be loaded into a variable as following:

>>> ele = ini.ele["Si"]

Note that element names are not case sensitive. The following properties can now be queried from the element:

  • The name of an element, which just returns that same abbreviation used to call the element, can be queried with name.

  • The mass of the element, calculated using the isotope masses and the currently loaded abundances, using mass.

  • The solar abundance of the element itself using abu_solar, normed as discussed above

  • The mass number of its (stable) isotopes using iso_a

  • The relative abundances of its (stable) isotopes using iso_abu_rel. If you are using “mass_fractions” as units, the relative abundances will also be given as mass fractions!

  • The solar abundances of its (stable) isotopes using iso_abu_solar

  • The number of protons of an element can be queried with z.

For example, to query the solar abundance of iron one could run the following statement:

>>> ele = ini.ele["Fe"]
>>> ele.abu_solar
847990.0

Note

You can query multiple elements as once. To do so, simply pass a list of the elements to be queried.

Querying an isotope

To query an isotope’s properties with respect to the solar abundance, it can be loaded into a temporary variable, similar to when loading an element. For example: To query 54Fe, the isotope can be loaded as a variable as following:

>>> iso = ini.iso["Fe-54"]

You can also use alternative spellings for the isotope name, e.g., "54Fe" or "Fe54". Furthermore, none of these spellings are case sensitive. The following properties can then be queried from this isotope:

  • The number of nucleons / mass number of an isotope can be queried with a.

  • The name of the isotope(s) requested can be queried with name. These names will always be in the standard format, e.g., "Fe-54".

  • The mass of a specific isotope using mass.

  • The solar abundance of the isotope itself using abu_solar, normed as discussed above

  • The relative abundance of the specific isotope with respect to the element using abu_rel. Note: All isotopes of an element would sum up to a relative abundance of 1. If you are using “mass_fractions” as units, the relative abundances will also be given as mass fractions!

  • The number of protons of an isotope can be queried with z.

Note

All isotope functions can be suffixed with _all. This will return information on all available isotopes, including unstable ones. Of course, solar system abundances for these are not available and will be returned as zeros, however, this might be useful to query masses.

For example: To query the solar and the relative abundances of 54Fe one could run the following two commands in python:

>>> iso = ini.iso["Fe-54"]
>>> iso.abu_solar
49600.0
>>> iso.abu_rel
0.058449999999999995

Note

To query all isotopes of an element, you can query the isotope as following:

>>> iso = ini.iso["Ne"]
>>> iso.name
['Ne-20', 'Ne-21', 'Ne-22']

Note

You can query multiple isotopes at once. To do so, simply pass a list of the isotopes (or even elements in case of all isotopes) to be queried.

Element and isotope ratios

This function is used to calculate element and isotope ratios. Sure, the same can be accomplished by simply dividing the abundances of two isotopes. However, this function has some added benefits:

  • Select if ratio is number fraction (default) or mass fraction

  • Return multiple elements or isotopes at once

Some additional benefits when calculating isotope ratios:

  • Choosing an element as the nominator selects all isotopes of the given element for the nominator

  • Choosing an element as the denominator calculates the ratio for every isotope in the nominator with respect to the most abundant isotope of the element given as the denominator. This might sound complicated, but can be very useful since isotope ratios are often given with the most abundant isotope in the denominator

Note

If multiple isotope ratios are returned the function automatically returns them as a numpy array. This facilitates subsequent mathematical operations using these ratios.

The functions to calculate these ratios are called ele_ratio and iso_ratio. Below are some examples that describe some standard usage of these routines:

Caution

In these examples we assume that the database is loaded with “num_lin” units. If you are using “mass_fraction” units, you will get “mass_fraction” units back, even if you do not set mass_fraction=True. However, you could overwrite this behavior (the same way you can return mass_fractions even if you are in “num_lin” mode) by setting mass_fraction=False.

Some examples for elemental ratios:

  • Calculate He to Pb ratio using number fraction and mass fraction: Here we assume that number, linear units are loaded:

    >>> ini.ele_ratio("He", "Pb")  # number fraction
    759537205.0816697
    >>> ini.ele_ratio("He", "Pb", mass_fraction=True)
    39321659726.58637
    
  • Calculate multiple element ratios with the same denominator. The specific example here ratios Fe and Ni to Si:

    >>> ini.ele_ratio(["Fe", "Ni"], "Si")
    array([0.84824447, 0.04910773])
    
  • Calculate multiple element ratios that have individual nominators and denominators. Here Si to Fe and Ni to Zr is calculated:

    >>> ini.ele_ratio(["Si", "Ni"], ["Fe", "Zr"])
    array([1.17890541e+00, 4.55450413e+03])
    

Some examples for isotope ratios:

  • Calculate the isotope ratios of 6Li to 7Li as number fractions and as mass fractions. Here we assume that number, linear units are loaded:

    >>> ini.iso_ratio("Li-6", "Li-7")  # number fractions by default
    0.08212225817272835
    >>> ini.iso_ratio("Li-6", "Li-7", mass_fraction=True)
    0.09578691181324486
    
  • Calculate isotope fractions of 3He to 4He and 21Ne to 20Ne:

    >>> ini.iso_ratio(["He-3", "Ne-21"], ["He-4", "Ne-20"])
    array([0.00016603, 0.00239717])
    
  • Calculate the isotope ratios of all Si isotopes with respect to 28Si. Three methods, all identical, are specified as following:

    • Method 1: The manual way specifying each isotope individually

    • Method 2: Select element in nominator chooses all isotopes of specified element

    • Method 3: The fastest way for this specific case is to choose ‘Si’ as the element in the nominator and to choose ‘Si’ in the denominator. The latter will pick the most abundant isotope of silicon, which is 28Si.

    >>> ini.iso_ratio(["Si-28", "Si-29", "Si-30"], "Si-28")  # Method 1
    array([1.        , 0.05077524, 0.03347067])
    >>> ini.iso_ratio("Si", "Si-28")  # Method 2
    array([1.        , 0.05077524, 0.03347067])
    >>> ini.iso_ratio("Si", "Si")  # Method 3
    array([1.        , 0.05077524, 0.03347067])
    

δ-values

Note

A detailed discussion of δ-values can be found in the Background Information

The δ-value of a given isotope ratio, generally used in cosmo- and geochemistry, is defined as:

\[\delta \left( \frac{^{i}X}{^{j}X} \right) = \left(\frac{\left(\frac{^{i}X}{^{j}X}\right)_{\mathrm{measured}}} {\left(\frac{^{i}X}{^{j}X}\right)_{\mathrm{solar}}} - 1\right) \times f\]

Here the isotopes chosen for the ratio are \(^{i}X\) and \(^{j}X\). The measured isotope ratio, which is in the nominator, is a value that must be provided to the function. The solar isotope ratio (denominator) will be taken from the solar abundance table using the isotope ratios provided to the routine. The factor \(f\) is by default set to 1000. This means that δ-values are by default returned as parts-per-thousand (‰). Choosing a different factor can be done by setting the keyword argument delta_factor accordingly.

Furthermore, the keyword argument mass_fraction can also be used as for ratios. Setting this keyword to True or False allows the user to overwrite the behavior of the loaded units.

While δ-values are commonly calculated for isotopes of one individual element, the routine allows to calculate δ-values between isotopes of different elements. To calculate a δ-values of two elements, the ele_delta function should be used. The equation given above represents a specific, but most commonly used case.

Finally: The iso_delta and ele_delta functions have the same features for specifying the nominator and denominator as the iso_ratio and ele_ratio functions mentioned above.

Caution

The values must be given in the same shape as the number of ratios provided. Otherwise the routine will return a ValueError specifying that there was a length mismatch.

Some examples for calculating δ-values for isotopes:

  • Calculate one δ-value with a given measurement value. Here for 29Si/28Si. First calculated in parts per thousand (default), then as percent.

    >>> ini.iso_delta("Si-30", "Si-28", 0.04)  # parts per thousand (default)
    195.0761256883704
    >>> ini.iso_delta("Si-30", "Si-28", 0.04, delta_factor=100)  # percent
    19.50761256883704
    
  • Calculate multiple δ-values as mass fractions. Here we calculate all Si isotopes with respect to 28Si. Measurements are defined first. Three versions are provided that yield the same result. See description on calculating isotope ratios above for more detail.

    >>> msr = [1., 0.01, 0.04]  # measurement
    >>> ini.iso_delta(["Si-28", "Si-29", "Si-30"], "Si-28", msr)
    array([   0.        , -803.05359812,  195.07612569])
    >>> ini.iso_delta("Si", "Si-28", msr)
    array([   0.        , -803.05359812,  195.07612569])
    >>> ini.iso_delta("Si", "Si", msr)
    array([   0.        , -803.05359812,  195.07612569])
    
  • Calculate the δ-value for 84Sr with respect to the major Sr isotope (86Sr). The measurement value is provided as a mass fraction (assumption), but the database is loaded using number, linear units:

    >>> ini.iso_delta("Sr-84", "Sr", 0.01, mass_fraction=True)
    414.3962670607242
    

Some examples for calculating δ-values for elements:

  • Calculate a δ-value for multiple elements, here Si and Ne with respect to Fe:

    >>>  ini.ele_delta(["Si", "Ne"], "Fe", [2, 4])
    array([696.48894668,  30.26124356])
    

Bracket-notation

The bracket notation, generally used in astronomy, for a given elemental ratio is defined as:

\[[\mathrm{X}/\mathrm{Y}] = \log_{10} \left( \frac{N_\mathrm{X}}{N_\mathrm{Y}} \right)_\mathrm{star} - \log_{10} \left( \frac{N_\mathrm{X}}{N_\mathrm{Y}} \right)_\mathrm{solar}\]

Here, star stands for an arbitrary measurement, e.g., of a given star. X and Y are the elements of interest in this case, \(N_\mathrm{X}\) and \(N_\mathrm{Y}\) represent the respective number abundances of elements X and Y. Calculations with mass fractions are also allowed by the routine.

While bracket notation is commonly used with elements, there is no mathematical reason to prohibit using it for isotopes. Therefore, two routines are provided, namely ele_bracket and iso_bracket.

Finally: The ele_bracket and iso_bracket functions have the same features for specifying the nominator and denominator as the iso_ratio and ele_ratio functions mentioned above.

Some examples for calculating bracket notation values for elements:

  • Calculate bracket notation value for Fe / H for a given measurement. First we calculate it as a number fraction (default setting) then as a mass fraction while having the database loaded in number linear mode.

    >>> ini.ele_bracket("Fe", "H", 0.005)  # number fraction
    2.183887471873783
    >>> ini.ele_bracket("Fe", "H", 0.005, mass_fraction=True)  # mass fraction
    3.9274378849968263
    
  • Calculate bracket notation value for multiple measurements. Here, for O and Fe with respect to Fe.

    >>> ini.ele_bracket(["O", "Fe"], "H", [0.02, 0.005])
    array([1.51740521, 2.18388747])
    

Some examples for calculating bracket notation values for isotopes:

  • Calculate a bracket notation values for multiple isotopes. Here for all Si isotopes with respect to 28Si. Note: See ratio_isotopes for a detailed description of the possibilities.

    >>> msr = [1., 0.01, 0.04]
    >>>  ini.iso_bracket(["Si-28", "Si-29", "Si-30"], "Si-28", msr)
    array([ 0.        , -0.70565195,  0.07739557])
    >>> ini.iso_bracket("Si", "Si-28", msr)
    array([ 0.        , -0.70565195,  0.07739557])
    >>> ini.iso_bracket("Si", "Si", msr)
    array([ 0.        , -0.70565195,  0.07739557])
    

Internal normalization

Internal normalization normalizes isotope ratios to two isotopes in order to remove any effects due to mass-dependent fractionation. A detailed explanation and further references can be found in the section Background Information.

Note

Internal normalization is only available for isotopes at this point. Elemental measurements generally suffer from effects other than mass-dependent fractionation. The math could of course be applied to elements as well, however, can currently not be done with iniabu.

Several inputs are required for internal normalization. These are:

  • The nominator isotope(s)

  • The major and minor normalization isotopes

  • The nominator isotope abundance(s) in the sample

  • The normalization isotope abundances

The normalization isotopes and respective abundances must be given as a tuple or list with the main normalization isotope first. The minor normalization isotope (second) is the one used to correct mass-dependent fractionation.

You can also select the delta_factor. This is the multiplier by which the internally normalized value is multiplied at the end. By default, this factor is set to 10,000 and thus gives deviations in parts per 10,000. In geo- and cosmochemistry these deviations are often referred to as ε-values.

By default, an internally normalized value is calculated using the exponential law law="exp". However, you can also choose to use the linear law by setting law="lin".

Some examples:

  • Normalize 60Ni internally with respect to 58Ni and 62Ni. Use some made-up values for the data.

    >>> ni58_counts = 1000000
    >>> ni60_counts = 250000
    >>> ni62_counts = 10000
    >>> norm_counts = (ni58_counts, ni62_counts)
    >>> ini.iso_int_norm("Ni-60", ("Ni-58", "Ni-62"), ni60_counts, norm_counts)
    5145.864708640091
    
  • Now this value is large to express in parts per 10,000. Let’s switch the units to permil.

    >>> ini.iso_int_norm("Ni-60", ("Ni-58", "Ni-62"), ni60_counts, norm_counts,
                         delta_factor=1000)
    514.5864708640091
    
  • If all nickel isotopes have been measured, the internally normalized values can be calculated for all isotopes at once:

    >>> msrs = (1000000, 250000, 2600, 10000, 2000)
    >>> norm_msrs = (msrs[0], msrs[3])  # Ni-58 and Ni-62
    >>> ini.iso_int_norm("Ni", ("Ni-58", "Ni-62"), msrs, norm_msrs, delta_factor=1000)
    array([ 0.00000000e+00,  5.14586471e+02, -4.49223918e+02,  2.22044605e-13,
            7.41295081e+02])
    

    As expected, the internally normalized values for 58Ni and 62Ni are zero within numerical precisions.