Source code for pyne.ensdf

from __future__ import division
import re
import sys
import copy
from warnings import warn
from pyne.utils import QAWarning

import numpy as np

from pyne import nucname, rxname, data
from pyne.utils import to_sec

if sys.version_info[0] > 2:
    basestring = str

warn(__name__ + " is not yet QA compliant.", QAWarning)

_valexp = re.compile('([0-9.]*)([Ee][+-]?\d*)')
_val = re.compile('(\d*)[.](\d*)')
_specialval = re.compile("([0-9. ]*)[+]([A-Z])")
_specialval2 = re.compile("([A-Z]*)[+]([0-9.]*)")
_errpm = re.compile('[+](\d*)[-](\d*)')
_err = re.compile('[ ]*(\d*)')
_base = '([ \d]{3}[ A-Za-z]{2})'
_ident = re.compile(_base + '    (.{30})(.{26})(.{7})(.{6})')
_g = re.compile(_base + '  G (.{10})(.{2})(.{8})(.{2}).{24}(.{7})(.{2})(.{10})'
                + '(.{2})')
_gc = re.compile(_base + '[0-9A-Za-z] [GE] (.{70})')
_beta = re.compile(_base + '  B (.{10})(.{2})(.{8})(.{2}).{10}(.{8})(.{6})')
_betac = re.compile(_base + '[0-9A-Za-z] ([BE]) (.{70})')
_ec = re.compile(_base + '  E (.{10})(.{2})(.{8})(.{2})'
                 + '(.{8})(.{2})(.{8})(.{6})(.{10})(.{2})')
_p = re.compile(_base + '  P (.{10})(.{2})(.{18})(.{10})'
                + '(.{6}).{9}(.{10})(.{2})(.{4})')
_norm = re.compile(_base + '  N (.{10})(.{2})(.{8})(.{2})(.{8})(.{2})(.{8})'
                   + '(.{6})(.{7})(.{2})')
_normp = re.compile(_base +
                    ' PN (.{10})(.{2})(.{8})(.{2})(.{8})(.{2})(.{7})(.{2})')
_q = re.compile(_base + '  Q (.{10})(.{2})(.{8})(.{2})'
                + '(.{8})(.{2})(.{8})(.{6})')
_alpha = re.compile(_base + '  A (.{10})(.{2})(.{8})(.{2})(.{8})(.{2})')
_dp = re.compile(_base + '  D(.{1})(.{10})(.{2})(.{8})(.{2})(.{8})(.{10})'
                 + '(.{6})')
_decays = ['B-', 'B+A', 'EC', 'B-A', 'B+', 'B+P', 'B-N', 'ECP', 'EC2P', 'N',
           '2N', 'IT', 'B+2P', 'B-2N', 'B+3P', 'ECA', 'P', '2P', '2B-', 'SF',
           'A', '2B+', '2EC', '14C']
_level_regex = re.compile(_base + '  L (.{10})(.{2})(.{18})(.{10})(.{6})'
                          + '(.{9})(.{10})(.{2})(.{1})([ M])([ 1-9])')
_level_cont_regex = re.compile('([ \d]{3}[ A-Za-z]{2})[0-9A-Za-z] L (.*)')


def _getvalue(obj, fn=float, rn=None):
    x = obj.strip()
    x = x.replace('$', '')
    x = x.replace('?', '')
    try:
        return fn(x)
    except ValueError:
        return rn


def _to_id(nuc):
    if 'NN' not in nuc:
        nucid = nucname.ensdf_to_id(nuc.strip())
    else:
        warn('Neutron data not supported!')
        return 0
    return nucid


def _to_time(tstr, errstr):
    t = tstr.strip()
    # This accepts questionable levels
    t = t.replace('?', '')
    tobj = [s.strip(' ()') for s in t.split()]
    if len(tobj) == 2:
        t, t_unit = tobj
        t, terr = _get_val_err(t, errstr)
        tfinal = to_sec(t, t_unit)
        tfinalerr = None
        if type(terr) == float:
            tfinalerr = to_sec(terr, t_unit)
        elif terr is not None:
            tfinalerr = to_sec(terr[0], t_unit), to_sec(terr[1], t_unit)
    elif 'STABLE' in t:
        tfinal = np.inf
        tfinalerr = None
    else:
        tfinal = None
        tfinalerr = None
    return tfinal, tfinalerr


def _get_val_err(valstr, errstr):
    pm = _errpm.match(errstr)
    err = _err.match(errstr)
    if pm is None and err.group(1) == '':
        return _getvalue(valstr), None
    val = _valexp.match(valstr)
    if val is None:
        valexp = ''
        val = valstr
    else:
        valexp = val.group(2)
        val = val.group(1)
    punc = _val.match(val.strip())
    if pm is not None:
        if punc is None:
            errplus = _getvalue(pm.group(1) + valexp)
            errminus = _getvalue(pm.group(2) + valexp)
        else:
            errplus = _get_err(len(punc.group(2)), pm.group(1), valexp)
            errminus = _get_err(len(punc.group(2)), pm.group(2), valexp)
        return _getvalue(valstr), (errplus, errminus)
    else:
        if punc is None:
            errplus = _getvalue(errstr + valexp)
        else:
            errplus = _get_err(len(punc.group(2)), errstr, valexp)
        return _getvalue(valstr), errplus


def _get_err(plen, errstr, valexp):
    errp = list((errstr.strip()).zfill(plen))
    errp.insert(-plen, '.')
    return _getvalue(''.join(errp) + valexp)


def _parse_level_record(l_rec):
    """
    This Parses and ENSDF level record

    Parameters
    ----------
    g : re.MatchObject
        regular expression MatchObject

    Returns
    -------
    e : float
        Level energy in keV
    tfinal : float
        Half life in seconds
    from_nuc : int
        nuc id of nuclide
    state : int
        metastable state of level
    special : str
        A-Z character denoting a group of known levels with no reference
        to the ground state. P and N are special characters reserved for
        proton and neutron resonances given in center of mass system energy.
    """
    lm = re.match("[ ]*([A-Z]+)(?![A-Z0-9+])", l_rec.group(2))
    spv = _specialval.match(l_rec.group(2).strip())
    spv2 = _specialval2.match(l_rec.group(2).strip())
    special = ' '
    if lm is not None:
        special = lm.group(1)
        if "S" in special and len(special.strip()) > 1:
            special = special.strip()[1]
        e = 0.0
        de = np.nan
    elif spv is not None:
        e, de = _get_val_err(spv.group(1), l_rec.group(3))
        special = spv.group(2)
    elif spv2 is not None:
        e, de = _get_val_err(spv2.group(2), l_rec.group(3))
        special = spv2.group(1)
        if "S" in special and len(special.strip()) > 1:
            special = special.strip()[1]
    else:
        e, de = _get_val_err(l_rec.group(2).strip('() '), l_rec.group(3))
    tfinal, tfinalerr = _to_time(l_rec.group(5), l_rec.group(6))
    from_nuc = _to_id(l_rec.group(1))
    m = l_rec.group(11)
    s = l_rec.group(12)
    state = 0
    if m == 'M':
        state = s.strip()
        if 0 < len(state):
            state = int(state)
        else:
            state = 1
    return e, tfinal, from_nuc, state, special


def _parse_level_continuation_record(lc_rec):
    """
    This Parses and ENSDF level record

    Parameters
    ----------
    g : re.MatchObject
        regular expression MatchObject

    Returns
    -------
    dat : dict
        dictionary of branching ratios of different reaction channels
    """
    g = lc_rec.groups()
    dat = {}
    raw_children = g[-1].replace(' AP ', '=')
    raw_children = raw_children.replace('$', ' ').split()
    for raw_child in raw_children:
        if '=' in raw_child:
            rx, br = raw_child.split('=')[:2]
            br = br.strip()
        else:
            continue
        if '%' in rx and '?' not in br and len(br) > 0:
            dat[rx] = br
    return dat


def _parse_gamma_record(g):
    """
    This parses an ENSDF gamma record

    Parameters
    ----------
    g : re.MatchObject
        regular expression MatchObject

    Returns
    -------
    dat : np.ndarray
        This array contains 6 floats corresponding to:
            * gamma ray energy in keV
            * uncertainty in energy
            * intensity
            * uncertainty in intensity
            * electron conversion intensity
            * uncertainty in electron conversion intensity
    """
    en, en_err = _get_val_err(g.group(2), g.group(3))
    inten, inten_err = _get_val_err(g.group(4), g.group(5))
    conv, conv_err = _get_val_err(g.group(6), g.group(7))
    tti, tti_err = _get_val_err(g.group(8), g.group(9))
    return [en, en_err, inten, inten_err, conv, conv_err, tti, tti_err]


def _parse_gamma_continuation_record(g, inten, tti):
    """
    This parses an ENSDF gamma continuation record

    """
    conversions = {}
    entries = g.group(2).split('$')
    for entry in entries:
        entry = entry.replace('AP', '=')
        entry = entry.replace('EL1C+EL2C', 'LC')
        if '+=' in entry or 'EAV' in entry:
            continue
        if 'C=' in entry:
            tsplit = entry.split('C')
        else:
            tsplit = entry.split('=')
            tsplit[0] = tsplit[0].lstrip('C')
        greff = inten
        if '/T' in entry:
            tsplit = entry.split('/T')
            greff = tti
            if greff is None:
                greff = inten
        if greff is None:
            greff = 1.0
        if len(tsplit) == 2:
            conv = None
            err = None
            contype = tsplit[0].lstrip('E')
            eff = tsplit[1].lstrip('= ').split()
            if len(eff) == 2:
                conv, err = _get_val_err(eff[0], eff[1])
            elif len(eff) == 1:
                conv = _getvalue(eff[0])
            if conv is None and contype not in conversions:
                conversions[contype] = (None, None)
            elif contype not in conversions:
                conversions[contype] = (conv * greff, err)
    return conversions


def _parse_beta_record(b_rec):
    """
    This parses an ENSDF beta minus record

    Parameters
    ----------
    b_rec : re.MatchObject
        regular expression MatchObject

    Returns
    -------
    en : float
        b- endpoint energy in keV
    en_err : float
        error in b- endpoint energy
    ib : float
        branch intensity
    dib : float
        error in branch intensity
    logft : float
        logft of the decay
    dft : float
        error in logft
    """
    en, en_err = _get_val_err(b_rec.group(2), b_rec.group(3))
    ib, dib = _get_val_err(b_rec.group(4), b_rec.group(5))
    logft, dft = _get_val_err(b_rec.group(6), b_rec.group(7))
    return en, en_err, ib, dib, logft, dft


def _parse_beta_continuation_record(bc_rec):
    """
    This parse the beta continuation record for EAV
    """
    entries = bc_rec.group(3).split('$')
    eav = None
    eav_err = None
    for entry in entries:
        if 'EAV' in entry and '=' in entry:
            dat = entry.split('=')[1]
            dat = dat.split()
            if len(dat) == 2:
                eav, eav_err = _get_val_err(dat[0], dat[1])
            elif len(dat) == 1:
                eav = _getvalue(dat[0])
    return eav, eav_err


def _parse_ec_record(e_rec):
    """
    This parses an ENSDF electron capture + b+ record

    Parameters
    ----------
    e_rec : re.MatchObject
        regular expression MatchObject

    Returns
    -------
    en : float
        b+ endpoint energy in keV
    en_err : float
        error in b+ endpoint energy
    ib : float
        b+ branch intensity
    dib : float
        error in b+ branch intensity
    ie : float
        ec branch intensity
    die : float
        error in ec branch intensity
    logft : float
        logft of the decay
    dft : float
        error in logft
    """
    en, en_err = _get_val_err(e_rec.group(2), e_rec.group(3))
    ib, dib = _get_val_err(e_rec.group(4), e_rec.group(5))
    ie, die = _get_val_err(e_rec.group(6), e_rec.group(7))
    logft, dft = _get_val_err(e_rec.group(8), e_rec.group(9))
    tti, dtti = _get_val_err(e_rec.group(10), e_rec.group(11))
    return en, en_err, ib, dib, ie, die, logft, dft, tti, dtti


def _parse_normalization_record(n_rec):
    """
    This parses an ENSDF normalization record

    Parameters
    ----------
    n_rec : re.MatchObject
        regular expression MatchObject

    Returns
    -------
    nr : float
        Multiplier for converting relative photon intensity to photons per 100
        decays of the parent through the decay branch or to photons per 100
        neutron captures for (n,g).
    nr_err : float
        Uncertainty in nr
    nt : float
        Multiplier for converting relative transition intensity to transitions
        per 100 decays of the parent through the decay branch or to photons
        per 100 neutron captures for (n,g).
    nt_err : float
        Uncertainty in nt
    br : float
        Branching ratio multiplier for converting intensity per 100 decays
        through this decay branch to intensity per 100 decays of the parent
        nuclide.
    br_err : float
        Uncertainty in br
    nb : float
        Multiplier for converting relative B- and EC intensities to intensities
        per 100 decays through this decay branch.
    nb_err : float
        Uncertainty in nb

    """
    nr, nr_err = _get_val_err(n_rec.group(2), n_rec.group(3))
    nt, nt_err = _get_val_err(n_rec.group(4), n_rec.group(5))
    br, br_err = _get_val_err(n_rec.group(6), n_rec.group(7))
    nb, nb_err = _get_val_err(n_rec.group(8), n_rec.group(9))
    if nr is not None and br is not None:
        nrbr = nr * br
    else:
        nrbr = None
    if nr_err is not None and br_err is not None:
        nrbr_err = nrbr*np.sqrt((br_err/br) ** 2 * (nr_err/nr) ** 2)
    else:
        nrbr_err = None
    return nr, nr_err, nt, nt_err, br, br_err, nb, nb_err, nrbr, nrbr_err


def _parse_production_normalization_record(np_rec):
    """
    This parses an ENSDF production normalization record

    Parameters
    ----------
    np_rec : re.MatchObject
        regular expression MatchObject

    Returns
    -------
    nrbr : float
        Multiplier for converting relative photon intensity to photons per 100
        decays of the parent nuclide
    nrbr_err : float
        Uncertainty in nrbr
    ntbr : float
        Multiplier for converting relative transition intensity to transitions
        per 100 decays of the parent nuclide
    ntbr_err : float
        Uncertainty in ntbr
    nbbr: float
        Multiplier for converting relative B- and EC intensities to intensity
        per 100 decays of the parent nuclide
    nbbr_err : float
        Uncertainty in nbbr
    """
    nrbr, nrbr_err = _get_val_err(np_rec.group(2), np_rec.group(3))
    ntbr, ntbr_err = _get_val_err(np_rec.group(4), np_rec.group(5))
    nbbr, nbbr_err = _get_val_err(np_rec.group(6), np_rec.group(7))
    return nrbr, nrbr_err, ntbr, ntbr_err, nbbr, nbbr_err


def _parse_parent_record(p_rec):
    """
    This parses an ENSDF parent record

    Parameters
    ----------
    p_rec : re.MatchObject
        regular expression MatchObject

    Returns
    -------
    tfinal : float
        half-life in seconds
    tfinalerr : float
        Uncertainty in half-life in seconds
    """
    lm = re.match("[ ]*([A-Z]+)(?![A-Z0-9+])", p_rec.group(2))
    spv = _specialval.match(p_rec.group(2).strip())
    spv2 = _specialval2.match(p_rec.group(2).strip())
    special = ' '
    if lm is not None:
        special = lm.group(1)
        if "S" in special and len(special.strip()) > 1:
            special = special.strip()[1]
        e = 0.0
        de = np.nan
    elif spv is not None:
        e, de = _get_val_err(spv.group(1), p_rec.group(3))
        special = spv.group(2)
    elif spv2 is not None:
        e, de = _get_val_err(spv2.group(2), p_rec.group(3))
        special = spv2.group(1)
        if "S" in special and len(special.strip()) > 1:
            special = special.strip()[1]
    else:
        e, de = _get_val_err(p_rec.group(2).strip('() '), p_rec.group(3))
    j = p_rec.group(4)
    tfinal, tfinalerr = _to_time(p_rec.group(5), p_rec.group(6))
    return p_rec.group(1), tfinal, tfinalerr, e, de, special


def _parse_qvalue_record(q_rec):
    """
    This parses and ENSDF q-value record

    Parameters
    ----------
    q_rec : re.MatchObject
        regular expression MatchObject

    Returns
    -------
    qminus : float
        total energy for B- decay (if qminus > 0 B- decay is possible)
    dqminus : float
        standard uncertainty in qminus
    sn : float
        neutron separation energy in keV
    dsn : float
        standard uncertainty in sn
    sp : float
        neutron separation energy in keV
    dsp : float
        standard uncertainty in sp
    qa : float
        total energy available for alpha decay of the ground state
    dqa : float
        standard uncertainty in qa
    """
    qminus, dqminus = _get_val_err(q_rec.group(2), q_rec.group(3))
    sn, dsn = _get_val_err(q_rec.group(4), q_rec.group(5))
    sp, dsp = _get_val_err(q_rec.group(5), q_rec.group(7))
    qa, dqa = _get_val_err(q_rec.group(8), q_rec.group(9))
    return qminus, dqminus, sn, dsn, sp, dsp, qa, dqa


def _parse_alpha_record(a_rec):
    """
    This parses and ENSDF alpha record

    Parameters
    ----------
    q_rec : re.MatchObject
        regular expression MatchObject

    Returns
    -------
    e : float
        energy of alpha particle
    de : float
        standard uncertainty in energy
    ia : float
        intensity of the decay branch in percent
    dia : float
        standard uncertainty in intensity
    hf : float
        hindrance factor
    dhf : float
        standard uncertainty in hindrance factor
    """
    e, de = _get_val_err(a_rec.group(2), a_rec.group(3))
    ia, dia = _get_val_err(a_rec.group(4), a_rec.group(5))
    hf, dhf = _get_val_err(a_rec.group(5), a_rec.group(7))
    return e, de, ia, dia, hf, dhf


def _parse_delayed_particle_record(dp_rec):
    """
    This parses and ENSDF delayed particle record

    Parameters
    ----------
    dp_rec : re.MatchObject
        regular expression MatchObject

    Returns
    -------
    ptype : str
        symbol for delayed particle
    e : float
        particle energy
    de : float
        standard uncertainty in energy
    ip : float
        intensity of delayed particle in percent
    dip : float
        standard uncertainty in intensity
    ei : float
        energy level of the intermediate
    t : float
        half-life of the transition (in seconds)
    dt : float
        standard uncertainty in half-life
    """
    ptype = dp_rec.group(2)
    e, de = _get_val_err(dp_rec.group(3), dp_rec.group(4))
    ip, dip = _get_val_err(dp_rec.group(5), dp_rec.group(6))
    ei = _getvalue(dp_rec.group(7))
    t, dt = _to_time(dp_rec.group(8), dp_rec.group(9))
    return ptype, e, de, ip, dip, ei, t, dt


def _parse_decay_dataset(lines, decay_s):
    """
    This parses a gamma ray dataset. It returns a tuple of the parsed data.

    Parameters
    ----------
    lines : list of str
        list containing lines from one dataset of an ensdf file
    decay_s : str
        string of the decay type

    Returns
    -------
    Tuple of decay parameters which is described in detail in gamma_rays docs

    """
    gammarays = []
    betas = []
    alphas = []
    ecbp = []
    ident = _ident.match(lines[0])
    daughter = ident.group(1)
    daughter_id = _to_id(daughter)
    parent = ident.group(2).split()[0]
    parent = parent.split('(')[0]
    parents = parent.split(',')
    if len(parents) > 1:
        pfinal = _to_id(parents[0])
    else:
        pfinal = _to_id(parents[0][:5])
    tfinal = None
    tfinalerr = None
    nrbr = None
    nbbr = None
    nrbr_err = None
    nbbr_err = None
    nb_err = None
    br_err = None
    nb = None
    br = None
    level = None
    special = " "
    goodgray = False
    parent2 = None
    for line in lines:
        level_l = _level_regex.match(line)
        if level_l is not None:
            level, half_lifev, from_nuc, \
            state, special = _parse_level_record(level_l)
            continue
        b_rec = _beta.match(line)
        if b_rec is not None:
            dat = _parse_beta_record(b_rec)
            if parent2 is None:
                bparent = pfinal
            else:
                bparent = parent2
            level = 0.0 if level is None else level
            bdaughter = data.id_from_level(_to_id(daughter), level)
            betas.append([bparent, bdaughter, dat[0], 0.0, dat[2]])
        bc_rec = _betac.match(line)
        if bc_rec is not None:
            bcdat = _parse_beta_continuation_record(bc_rec)
            if bcdat[0] is not None:
                if bc_rec.group(2) == 'B':
                    betas[-1][3] = bcdat[0]
                else:
                    ecbp[-1][3] = bcdat[0]
                    bggc = _gc.match(line)
                    conv = _parse_gamma_continuation_record(bggc, dat[2],
                                                            dat[8])
                    if 'K' in conv:
                        ecbp[-1][-3] = conv['K'][0]
                    if 'L' in conv:
                        ecbp[-1][-2] = conv['L'][0]
                    if 'M' in conv:
                        ecbp[-1][-1] = conv['M'][0]
        a_rec = _alpha.match(line)
        if a_rec is not None:
            dat = _parse_alpha_record(a_rec)
            if parent2 is None:
                aparent = pfinal
            else:
                aparent = parent2
            level = 0.0 if level is None else level
            adaughter = data.id_from_level(_to_id(daughter), level)
            alphas.append([aparent, adaughter, dat[0], dat[2]])
        ec_rec = _ec.match(line)
        if ec_rec is not None:
            dat = _parse_ec_record(ec_rec)
            if parent2 is None:
                ecparent = pfinal
            else:
                ecparent = parent2
            level = 0.0 if level is None else level
            ecdaughter = data.id_from_level(_to_id(daughter), level)
            ecbp.append([ecparent, ecdaughter, dat[0], 0.0, dat[2], dat[4],
                         0, 0, 0])
            continue
        g_rec = _g.match(line)
        if g_rec is not None:
            dat = _parse_gamma_record(g_rec)
            if dat[0] is not None:
                gparent = 0
                gdaughter = 0
                if level is not None:
                    gparent = data.id_from_level(_to_id(daughter), level,
                                                 special)
                    dlevel = level - dat[0]
                    gdaughter = data.id_from_level(_to_id(daughter), dlevel,
                                                   special)
                if parent2 is None:
                    gp2 = pfinal
                else:
                    gp2 = parent2
                dat.insert(0, daughter_id)
                dat.insert(0, gp2)
                dat.insert(0, gdaughter)
                dat.insert(0, gparent)
                for i in range(3):
                    dat.append(0)
                gammarays.append(dat)
                goodgray = True
            else:
                goodgray = False
            continue
        gc_rec = _gc.match(line)
        if gc_rec is not None and goodgray is True:
            conv = _parse_gamma_continuation_record(gc_rec, gammarays[-1][6],
                                                    gammarays[-1][10])
            if 'K' in conv:
                gammarays[-1][-3] = conv['K'][0]
            if 'L' in conv:
                gammarays[-1][-2] = conv['L'][0]
            if 'M' in conv:
                gammarays[-1][-1] = conv['M'][0]
            continue
        n_rec = _norm.match(line)
        if n_rec is not None:
            nr, nr_err, nt, nt_err, br, br_err, nb, nb_err, nrbr, nrbr_err = \
                _parse_normalization_record(n_rec)
            if nb is not None and br is not None:
                nbbr = nb * br
            if nb_err is not None and br_err is not None and nb_err != 0:
                nbbr_err = nbbr*((br_err/br) ** 2 * (nb_err/nb) ** 2) ** 0.5
            continue
        np_rec = _normp.match(line)
        if np_rec is not None:
            nrbr2, nrbr_err2, ntbr, ntbr_err, nbbr2, nbbr_err2 = \
                _parse_production_normalization_record(np_rec)
            if nrbr2 is not None and nrbr is None:
                nrbr = nrbr2
                nrbr_err = nrbr_err2
            if nbbr2 is not None and nbbr is None:
                nbbr = nbbr2
                nbbr_err = nbbr_err2
            continue
        p_rec = _p.match(line)
        if p_rec is not None:
            # only 2 parents are supported so this can be here
            multi = False
            if parent2 is not None:
                multi = True
                pfinal = [parent2,]
                tfinal = [t,]
                tfinalerr = [terr,]
            parent2, t, terr, e, e_err, special = _parse_parent_record(p_rec)
            parent2 = data.id_from_level(_to_id(parent2), e, special)
            if terr is not None and not isinstance(terr, float):
                terr = (terr[0] + terr[1])/2.0
            if multi:
                tfinal.append(t)
                tfinalerr.append(terr)
                pfinal.append(parent2)
            else:
                tfinal = t
                tfinalerr = terr
                pfinal = parent2
            continue
    if len(gammarays) > 0 or len(alphas) > 0 or len(betas) > 0 or len(ecbp) > 0:
        if len(parents) > 1 and parent2 is None:
            pfinal = []
            for item in parents:
                pfinal.append(_to_id(item))
        return pfinal, daughter_id, rxname.id(decay_s.strip().lower()), \
               tfinal, tfinalerr, \
               br, br_err, nrbr, nrbr_err, nbbr, nbbr_err, gammarays, alphas, \
               betas, ecbp
    return None


[docs]def levels(filename, levellist=None): """ This takes an ENSDF filename or file object and parses the ADOPTED LEVELS records to assign level numbers by energy. It also parses the different reported decay types and branching ratios. Parameters ---------- filename : str or file Name of ENSDF formatted file or a file-like object containing ENSDF formatted data levellist : list of tuples This is a list object which all newly processed levels will be added to. If it's None a new one will be created. Returns ------- levellist : list of tuples This is a list of all the level data. Each level has base entry with a reaction id of 0 and additional entries for any listed decays. The format of each row is: nuc_id : int The state_id of the level rx_id : int The id of the decay "reaction" in PyNE reaction id form. half_life : float Half life of the state in s level : float energy of the level in keV branch_ratio : float if rx_id != 0 this is the percent of decays in that channel metastable : int metastable id number of the level (if given) special : string single character denoting levels with unknown relation to ground state """ badlist = ["ecsf", "34si", "|b{+-}fission", "{+24}ne", "{+22}ne", "24ne", "b-f", "{+20}o", "2|e", "b++ec", "ecp+ec2p", "ecf", "mg", "ne", "{+20}ne", "{+25}ne", "{+28}mg", "sf(+ec+b+)"] special = "" if levellist is None: levellist = [] if isinstance(filename, str): with open(filename, 'r') as f: dat = f.read() else: dat = filename.read() datasets = dat.split(80 * " " + "\n")[0:-1] for dataset in datasets: lines = dataset.splitlines() ident = re.match(_ident, lines[0]) if ident is None: continue if 'ADOPTED LEVELS' in ident.group(2): leveln = 0 brs = {} level_found = False for line in lines: level_l = _level_regex.match(line) if level_l is not None: if len(brs) > 0: for key, val in brs.items(): goodkey = True keystrip = key.replace("%", "").lower() for item in badlist: if keystrip == item: goodkey = False if goodkey is True: rx = rxname.id(keystrip) levellist.append((nuc_id, rx, half_lifev, level, val.split("(")[0], state, special)) if level_found is True: levellist.append((nuc_id, 0, half_lifev, level, 0.0, state, special)) brs = {} level, half_lifev, from_nuc, state, special = \ _parse_level_record(level_l) if from_nuc is not None: nuc_id = from_nuc + leveln leveln += 1 level_found = True else: level_found = False continue levelc = _level_cont_regex.match(line) if levelc is not None: brs.update(_parse_level_continuation_record(levelc)) continue if len(brs) > 0: for key, val in brs.items(): goodkey = True keystrip = key.replace("%", "").lower() for item in badlist: if keystrip == item: goodkey = False if goodkey is True: rx = rxname.id(keystrip) levellist.append((nuc_id, rx, half_lifev, level, val.split("(")[0], state, special)) if level_found is True: levellist.append((nuc_id, 0, half_lifev, level, 0.0, state, special)) return levellist
[docs]def decays(filename, decaylist=None): """ This splits an ENSDF file into datasets. It then passes the dataset to the appropriate parser. Currently only a subset of decay datasets are supported. The output is a list of objects containing information pertaining to a particular decay. Parameters ---------- filename : str or file Name of ENSDF formatted file or a file-like object containing ENSDF formatted data decaylist : list of tuples This is a list object which all newly processed decays will be added to. If it's None a new one will be created. Returns ------- decaylist : list of tuples list of objects containing information pertaining to a particular decay. This information is in the following format: int nuc_id of the parent int nuc_id of the daughter int PyNE reaction id float half-life in seconds float half-life error in seconds float branching ratio (percent) float Conversion factor for gamma intensity to photons per 100 decays of the parent float Error in conversion factor for gamma intensity float Conversion factor for electron capture/beta intensity to electron captures/betas per 100 decays of the parent float Error in conversion factor for electron capture/beta intensity list a list containing information about each gamma ray: * starting level of gamma transition in stats_id form * final level of gamma transition in state_id form * original parent * energy in keV * uncertainty in energy * intensity (multiply by conversion factor for percentage) * uncertainty in intensity * electron conversion intensity * uncertainty in electron conversion intensity * total transition intensity * total transition intensity error * k electron conversion intensity * l electron conversion intensity * m electron conversion intensity list a list containing information about each alpha: * parent nuclide id in state_id form * child nuclide id in state_id form * alpha energy * alpha intensity in percent of total alphas list a list containing information about each beta minus from the parent decay: * parent nuclide id in state_id form * child nuclide id in state_id form * beta endpoint energy * beta average energy * beta intensity (multiply by conversion factor for percentage) list a list containing information about each beta plus and electron capture from the parent decay: * parent nuclide id in state_id form * child nuclide id in state_id form * beta plus endpoint energy * beta plus average energy * beta intensity (multiply by conversion factor for percentage) * electron capture intensity (multiply by conversion factor for percentage) * k electron conversion intensity * l electron conversion intensity * m electron conversion intensity """ if decaylist is None: decaylist = [] if isinstance(filename, str): with open(filename, 'r') as f: dat = f.read() else: dat = filename.read() datasets = dat.split(80 * " " + "\n") for dataset in datasets: lines = dataset.splitlines() if len(lines) == 0: continue ident = re.match(_ident, lines[0]) if ident is None: continue if 'DECAY' in ident.group(2): decay_s = ident.group(2).split()[1] decay = _parse_decay_dataset(lines, decay_s) if decay is not None: if isinstance(decay[0], list): if isinstance(decay[3], list): for i, parent in enumerate(decay[0]): dc = copy.deepcopy(list(decay)) dc[0] = parent dc[3] = decay[3][i] dc[4] = decay[4][i] for gamma in dc[11]: gamma[2] = parent for alpha in dc[12]: alpha[0] = parent for beta in dc[13]: beta[0] = parent for ecbp in dc[14]: ecbp[0] = parent decaylist.append(tuple(dc)) else: for parent in decay[0]: dc = copy.deepcopy(list(decay)) dc[0] = parent for gamma in dc[11]: gamma[2] = parent for alpha in dc[12]: alpha[0] = parent for beta in dc[13]: beta[0] = parent for ecbp in dc[14]: ecbp[0] = parent decaylist.append(tuple(dc)) else: decaylist.append(decay) return decaylist
def _dlist_gen(f): """ This compiles a list of decay types in an ensdf file Parameters ---------- f : str Name of ENSDF formatted file Returns ------- decaylist : list list of decay types in the ENSDF file eg. ['B+','B-','A'] """ if isinstance(f, str): with open(f, 'r') as f: dat = f.read() else: dat = f.read() decaylist = [] datasets = dat.split(80 * " " + "\n")[0:-1] for dataset in datasets: lines = dataset.splitlines() ident = re.match(_ident, lines[0]) if ident is not None: if 'DECAY' in ident.group(2): #print ident.group(2) fin = ident.group(2).split()[1] if fin not in decaylist: decaylist.append(fin) return decaylist def _level_dlist_gen(f, keys): """ This compiles a list of decay types in an ensdf file Parameters ---------- f : str Name of ENSDF formatted file Returns ------- decaylist : list list of decay types in the ENSDF file eg. ['B+','B-','A'] """ if isinstance(f, str): with open(f, 'r') as f: dat = f.read() else: dat = f.read() datasets = dat.split(80 * " " + "\n")[0:-1] for dataset in datasets: lines = dataset.splitlines() ident = re.match(_ident, lines[0]) if ident is not None: if 'ADOPTED LEVELS' in ident.group(2): #print ident.group(2) for line in lines: levelc = _level_cont_regex.match(line) if levelc is None: continue ddict = _parse_level_continuation_record(levelc) for item in ddict.keys(): if item in keys: continue keys.append(item) return keys