Skip to content

Repository files navigation

Nuclear Masses

PyPI Python Version

Unit Tests codecov

Introduction

The package nuclearmasses provides convenient Python access to nuclear data published by the Atomic Mass Evaluation (AME) and NUBASE. These datasets are published in different, specialised formats, so nuclearmasses does the hard work of parsing and combining the data into a pandas DateFrame, making it simple to query and compare nuclear properties across different evaluations.

No guarantee is supplied with regards to the accuracy of the data presented. Estimated values are included, please always refer to the original sources. All data should, however, be accurate.

Documentation is here - https://nuclearmasses.readthedocs.io/

As a brief example, the combination of AME and NUBASE values from all years is available as a single DateFrame

>>> from nuclearmasses.mass_table import MassTable
>>> df = MassTable().data

You can then interrogate, or extract, whatever information you want. For example, how has the mass excess and its accuracy changed overtime for 190Re according to the AME

>>> df[(df['A'] == 190) & (df['Symbol'] == 'Re')][['TableYear', 'AMEMassExcess', 'AMEMassExcessError']]
       TableYear  AMEMassExcess  AMEMassExcessError
16054       1983     -35536.605             200.029
16055       1993     -35557.789             145.549
16056       1995     -35568.032             212.151
16057       2003     -35566.326             149.248
16058       2012     -35634.992              70.542
16059       2016     -35635.830              70.852
16060       2020     -35583.015               4.870

Mass tables

The data files released by the papers linked below are used to create the mass tables output by this code. There was no AME data published in 1997, but the 1995 AME matches the 1997 NUBASE according to section 4, "The tables" on P31 of these proceedings. As a result the 1997 NUBASE data is referred to as being from 1995 for simplicity when merging data.

There are published papers for 1971 and 1977, but I can't find the associated data files. If you are reading this and have a copy, know of someone with a copy, or have any information, please let me know via this issue #13

All data from the single NUBASE data file, the AME mass file and two reaction files are parsed and saved into the table. Details from the different sources are merged on A, Z and published year for each isotope, but otherwise, no comparison or validation is done on common values.

Setup

The package is available on the Python Package Index so can be installed via pip

pip install nuclearmasses

Or you can clone the latest version from github and install locally. All work is done on a feature branch so cloning and using main should be the same as using the latest installed version from pip. There may be some additional functionality, but nothing should have been removed.

git clone https://github.com/php1ic/nuclearmasses
cd nuclearmasses
pip install -e .

Usage

Understanding the data

Once parsed, each row of the DateFrame represents the unique combination of a single isotope and its evaluation year, with all rows correspond to the ground state (see issue #7 for discussions around the addition of isomeric states).

Missing Values: Missing data are represented using pd.NA when the DataFrame is created. Values that are missing in the source, either via empty fields, or some other symbol, are converted to pd.NA during the parse. The use of nullable pandas dtypes allows us to maintain the required column types but users should be aware that subsequent operations on the DataFrame may change how missing values are represented. This behaviour follows the approach to missing data described in the pandas documentation.

For another usage example, how does the mass excess of lithium vary across the isotopic chain according to NUBASE in the most recent table for both experimentally measured and theoretical values.

>>> from nuclearmasses.mass_table import MassTable
>>> df = MassTable().data
>>> df.query("TableYear == 2020 and Symbol == 'Li'")[['A', 'NUBASEMassExcess', 'NUBASEMassExcessError', 'Experimental']]
      A  NUBASEMassExcess  NUBASEMassExcessError  Experimental
38    3        28670.0000              2000.0000         False
59    4        25320.0000               210.0000          True
79    5        11680.0000                50.0000          True
104   6        14086.8804                 0.0014          True
133   7        14907.1050                 0.0040          True
161   8        20945.8000                 0.0500          True
196   9        24954.9100                 0.1900          True
229  10        33053.0000                13.0000          True
264  11        40728.3000                 0.6000          True
298  12        49010.0000                30.0000          True
336  13        56980.0000                70.0000          True

Adding User Data

Functionality exists for a user to add their own data into the combined table. The function add_user_data() is a method of the MassTable class and takes data in json format and adds it to the table.

An identifier column DataSource already exists and for all published data and is set to 0. Along with providing the new data, a user can specify a value for DataSource, otherwise a value of 1 is automatically assigned. The ability to specify a value means multiple data sources can be added whilst maintaining the ability to distinguish. If no value is given, the value of 1 will always be used.

To ensure uniqueness, values for A and Z must be part of the data. With the assumption that you would like to compare and contrast this new data with the published values, the name associated with your data must also match existing columns.

Let's imagine I have a new measurement for the mass excess of 100Ag at -78136.4keV +/- 0.6keV and I want to add it to the table

>>> # Addition is done on the class level so create an instance of the MassTable
>>> from nuclearmasses.mass_table import MassTable
>>> table = MassTable()
>>> # This step doesn't need to be done, but, for demonstration purposes,
>>> # extract the table data and check the current details for 100Ag
>>> df = table.data
>>> df.query("Symbol == 'Ag' and A == 100")[['A', 'Z', 'NUBASEMassExcess', 'NUBASEMassExcessError', 'DataSource']])
        A  NUBASEMassExcess  NUBASEMassExcessError  DataSource
6976  100               NaN                    NaN           0
6977  100               NaN                    NaN           0
6978  100          -78180.0                   80.0           0
6979  100          -78150.0                   80.0           0
6980  100          -78138.0                    5.0           0
6981  100          -78138.0                    5.0           0
6982  100          -78138.0                    5.0           0
>>> # Add our new value to the NUBASE columns
>>> table.add_user_data('[{"A": 100, "Z": 47, "NUBASEMassExcess": -78136.4, "NUBASEMassExcessError": 0.6}]')
>>> # The underlying table has been modified, so we need to get the latest version
>>> df = table.data
>>> # Re-run the query to see the new value,
>>> # notice the value for DataSource has been set to 1
>>> df.query("Symbol == 'Ag' and A == 100")[['A', 'Z', 'NUBASEMassExcess', 'NUBASEMassExcessError', 'DataSource']])
         A  NUBASEMassExcess  NUBASEMassExcessError  DataSource
6976   100               NaN                    NaN           0
6977   100               NaN                    NaN           0
6978   100          -78180.0                   80.0           0
6979   100          -78150.0                   80.0           0
6980   100          -78138.0                    5.0           0
6981   100          -78138.0                    5.0           0
6982   100          -78138.0                    5.0           0
21421  100          -78136.4                    0.6           1
>>> # We can add the same data but this time assign to a different source
>>> table.add_user_data('[{"A": 100, "Z": 47, "NUBASEMassExcess": -78136.4, "NUBASEMassExcessError": 0.6}]', source=5)
>>> # Again, this modifies the underlying dataframe so we need to fetch the updated version
>>> df = table.data
>>> # Run the query and see that our new data is there twice,
>>> # once against the two different sources
>>> df.query("Symbol == 'Ag' and A == 100")[['A', 'Z', 'NUBASEMassExcess', 'NUBASEMassExcessError', 'DataSource']])
         A  NUBASEMassExcess  NUBASEMassExcessError  DataSource
6976   100               NaN                    NaN           0
6977   100               NaN                    NaN           0
6978   100          -78180.0                   80.0           0
6979   100          -78150.0                   80.0           0
6980   100          -78138.0                    5.0           0
6981   100          -78138.0                    5.0           0
6982   100          -78138.0                    5.0           0
21421  100          -78136.4                    0.6           1
21422  100          -78136.4                    0.6           5

Now let's imagine that we have a large new data set in a json file, but have forgotten to add the Experimental attribute, and for some reason it is not possible to edit or update the file with this additional information. When reading in the file, we can pass a dictionary as a parameter, and the keys will be used as the column names, with the values used as value to for all new isotopes.

In the following example, we will assume the data is in a file new_data.json and of type pathlib.

>>> import pathlib
>>> from nuclearmasses.mass_table import MassTable
>>> table = MassTable()
>>> missed_values = {'Experimental': True}
>>> new_data = pathlib.Path('new_data.json')
>>> table.add_user_data(new_data, common_values=missed_values)
>>> # All isotopes from new_data.json will have their Experimental
>>> # column assigned to True

If an existing column is not populated with new data, it is assigned the value pd.NA for reason discussed in Understanding the data. We do not try and infer what a value could, should or might be.

Contributing

Details about contributing to the project are in CONTRIBUTING

Known issues

  • #6 The decay mode field from the NUBASE data is stored 'as-is' from the file. It looks like it can be split on the ';' character for isotopes where there is more than one mode. A dictionary of {decay mode: fraction} may be the best way to store all of this information.
  • #7 Information from anything other than the ground state of an isotope is ignored when parsing the NUBASE file. The selection of what is and what is not included appears random to me which is why I simply ignored for the moment.

About

Python package to read nuclear mass files from the AME and NUBASE published datasets

Topics

Resources

Contributing

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages