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:
“asplund09”: Asplund et al. (2009)
“lodders09” (current default): Lodders et al. (2009)
“nist”: NIST database
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:
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:
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:
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 aboveThe 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 aboveThe 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:
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:
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.