"""This module provides a way to grab and store raw data for atomic mass."""
from __future__ import print_function
import os
import re
import pkgutil
from pyne.utils import QA_warn
import numpy as np
import tables as tb
from pyne import nucname
from pyne.dbgen.api import BASIC_FILTERS
from pyne.dbgen.isotopic_abundance import get_isotopic_abundances
QA_warn(__name__)
# Note that since ground state and meta-stable isotopes are of the same atomic mass,
# the meta-stables have been discluded from the following data sets.
MASS_FILE = 'mass.mas16'
[docs]def copy_atomic_mass_adjustment(build_dir=""):
    """Copies the atomic mass evaluation originally from the Atomic Mass Data
    Center.  These are courtesy of Georges Audi and Wang Meng via a private
    communication, November 2012."""
    if os.path.exists(os.path.join(build_dir, MASS_FILE)):
        return
    mass = pkgutil.get_data('pyne.dbgen', MASS_FILE)
    with open(os.path.join(build_dir, MASS_FILE), 'wb') as f:
        f.write(mass) 
# Note, this regex specifically leaves our free neutrons
# amdc_regex = re.compile('[ \d-]*? (\d{1,3})[ ]{1,4}(\d{1,3}) [A-Z][a-z]? .*? (\d{1,3}) ([ #.\d]{10,11}) ([ #.\d]{1,10})[ ]*?$')
amdc_regex = re.compile('[ \d-]*? (\d{1,3})[ ]{1,4}(\d{1,3}) [A-Z][a-z]? .*? (\d{1,3}) ([ #.\d]{5,12}) ([ #.\d]+)[ ]*?$')
[docs]def parse_atomic_mass_adjustment(build_dir=""):
    """Parses the atomic mass adjustment data into a list of tuples of
    the nuclide, atomic mass, and error."""
    f = open(os.path.join(build_dir, MASS_FILE), 'r')
    atomic_masses = []
    for line in f:
        m = amdc_regex.search(line)
        if m is None:
            continue
        nuc = (10000000 * int(m.group(1))) + (10000 * int(m.group(2)))
        mass = float(m.group(3)) + 1E-6 * float(m.group(4).strip().replace('#', ''))
        error = 1E-6 * float(m.group(5).strip().replace('#', ''))
        atomic_masses.append((nuc, mass, error))
    f.close()
    return atomic_masses 
atomic_mass_desc = {
    'nuc':   tb.IntCol(pos=1),
    'mass':  tb.FloatCol(pos=2),
    'error': tb.FloatCol(pos=3),
    'abund': tb.FloatCol(pos=4),
    }
atomic_mass_dtype = np.dtype([
    ('nuc',   int),
    ('mass',  float),
    ('error', float),
    ('abund', float),
    ])
[docs]def make_atomic_mass_table(nuc_data, build_dir=""):
    """Makes an atomic mass table in the nuc_data library.
    Parameters
    ----------
    nuc_data : str
        Path to nuclide data file.
    build_dir : str
        Directory to place html files in.
    """
    # Grab raw data
    atomic_abund = get_isotopic_abundances()
    atomic_masses = parse_atomic_mass_adjustment(build_dir)
    A = {}
    # Add normal isotopes to A
    for nuc, mass, error in atomic_masses:
        if nuc in atomic_abund:
            A[nuc] = nuc, mass, error, atomic_abund[nuc]
        else:
            A[nuc] = nuc, mass, error, 0.0
    # Add naturally occuring elements
    for element in nucname.name_zz:
        nuc = nucname.id(element)
        A[nuc] = nuc, 0.0, 0.0, 0.0
    for nuc, abund in atomic_abund.items():
        zz = nucname.znum(nuc)
        element_zz = nucname.id(zz)
        element = nucname.zz_name[zz]
        _nuc, nuc_mass, _error, _abund = A[nuc]
        elem_zz, elem_mass, _error, _abund = A[element_zz]
        new_elem_mass = elem_mass + (nuc_mass * abund)
        A[element_zz] = element_zz, new_elem_mass, 0.0, float(0.0 < new_elem_mass)
    A = sorted(A.values(), key=lambda x: x[0])
    # Open the HDF5 File
    kdb = tb.open_file(nuc_data, 'a', filters=BASIC_FILTERS)
    # Make a new the table
    Atable = kdb.create_table("/", "atomic_mass", atomic_mass_desc,
                             "Atomic Mass Data [amu]", expectedrows=len(A))
    Atable.append(A)
    # Ensure that data was written to table
    Atable.flush()
    # Close the hdf5 file
    kdb.close() 
[docs]def make_atomic_mass(args):
    """Controller function for adding atomic_mass."""
    nuc_data, build_dir = args.nuc_data, args.build_dir
    if os.path.exists(nuc_data):
        with tb.open_file(nuc_data, 'r') as f:
            if hasattr(f.root, 'atomic_mass'):
                print("skipping atomic mass data table creation; already exists.")
                return
    # Then grab mass data
    print("Copying AME 2016 atomic mass data.")
    copy_atomic_mass_adjustment(build_dir)
    # Make atomic mass table once we have the array
    print("Making atomic mass data table.")
    make_atomic_mass_table(nuc_data, build_dir)