<jupyter_start><jupyter_text>Generation of particle and nuclide property database[PriNCe](https://github.com/joheinze/PriNCe) requires knowledge about particle properties, such as their life time and decay channels. For historical reasons PriNCe uses a weird particle ID scheme from the NeuCosmA framework. This scheme will be substituted by PDG IDs in future. Nuclides are represented by the ID scheme from the [CORSIKA air shower monte carlo package](https://www.ikp.kit.edu/corsika/) that encodes a nucleus with mass A and charge Z as $100*mass + Z$ (=5626 for natural iron). This scipt reads particle data from two sources:  - for Nuclides it uses the NeuCosmA files, which are data compiled from Mathematica built in functions (but essentially originate from EXFOR)   - for elementary particles data comes from [particletools](https://github.com/afedynitch/particletools),    which sources these from [PYTHIA8](http://home.thep.lu.se/~torbjorn/Pythia.html)It is foreseen to integrate the nuclide database into particletools and make this database obsolete.<jupyter_code>import pickle
import sys
from os import path

import numpy as np
from scipy.constants import c as c_mps
from particletools.tables import PYTHIAParticleData
from prince_data_utils import resource_path
# Part 1 NeuCosmA files
def parse_line(line):
    u_in_gev = 0.9314940954
    data = {}

    # data
    nco_id = int(line[0])
    mother_id = nco_id
    data['mass'] = u_in_gev * line[1]  # mass converted to GeV
    data['stable'] = bool(line[2])
    data['lifetime'] = np.inf if data['stable'] else line[3]
    data['incomplete'] = bool(line[8])
    data['charge'] = nco_id % 100

    #decay channel
    branching = line[4]
    if branching == 0.:
        data['branchings'] = []
    else:
        daughters = []
        beta_mode = int(line[5])
        if beta_mode == -1:
            # electron emitted
            mother_id += 1
            daughters += [12, 20]
        elif beta_mode == 1:
            # positron emitted
            mother_id -= 1
            daughters += [11, 21]
        # also take into account emitted baryons
        no_daughters = int(line[6])
        daughter_id = int(line[7])

        mother_id -= no_daughters * daughter_id
        daughters += no_daughters * [daughter_id]
        daughters += [mother_id]

        data['branchings'] = [(branching, daughters)]

    return nco_id, data
"""Generates from particle databases of Neucosma and
MCEq a dictionary for quick lookup in PriNCe."""

data_raw = np.loadtxt(
    path.join(resource_path, 'photo-nuclear/160302_BETA_MATHEMATICA.dat'), dtype=float)

nuclide_data = {}

for line in data_raw:
    nco_id, data = parse_line(line)
    if nco_id not in nuclide_data:
        nuclide_data[nco_id] = data
    else:
        nuclide_data[nco_id]['branchings'] += data['branchings']
        nuclide_data[nco_id]['branchings'].sort(reverse=True)

# Part 2 ParticleDataTool
pa_data = PYTHIAParticleData()

id_mapping = [
    (0, 22),  # photon
    (2, 211),  # pi_plus
    (3, -211),  # pi_minus
    (4, 111),  # pi_zero
    (5, -13),  # mu_plus_l
    (6, -13),  # mu_plus_r
    (7, -13),  # mu_plus (both)
    (8, 13),  # mu_minus_l
    (9, 13),  # mu_minus_r
    (10, 13),  # mu_minus (both)
    (11, 12),  # nu_e
    (12, -12),  # nu_bar_e
    (13, 14),  # nu_mu
    (14, -14),  # nu_bar_mu
    (15, 16),  # nu_tau
    (16, -16),  # nu_bar_tau
    (20, 11),  # electron
    (21, -11),  # positron
    (50, 321),  # k_plus
    (51, -321),  # k_minus
    (100, 2112),  # neutron
    (101, 2212),  # proton
]
id_mapping_dict = {}
for nco_id, pdg_id in id_mapping:
    id_mapping_dict[pdg_id] = nco_id

# Use scipy constants.
c = 1e2 * c_mps  # convert to centimeters

particle_data = {}
# Add an entry containing the ids of the neucosma
# particles which should be included by default incomputations
particle_data["non_nuclear_species"] = sorted(id_mapping_dict.values())

for nco_id, pdg_id in id_mapping:
    p = pa_data[pdg_id]
    branchings = pa_data.decay_channels(pdg_id)

    data_nco = {}
    data_nco['mass'] = p.mass
    data_nco['stable'] = True if p.ctau/c == np.inf else False
    data_nco['lifetime'] = p.ctau/c
    data_nco['incomplete'] = False
    data_nco['name'] = p.name
    data_nco['charge'] = p.charge

    # convert also ids in branching
    branchings_nco = []
    for ratio, daughter_ids in branchings:
        # hotfix bug in particletools
        for ida in range(len(daughter_ids)):
            if daughter_ids[ida] == -111:
                daughter_ids[ida] = 111
        daughter_ids = [
            id_mapping_dict[daughter] for daughter in daughter_ids
        ]
        branchings_nco += [(ratio, daughter_ids)]
    branchings_nco.sort(
        reverse=
        True)  # sort them to start with the hightest branching ratio
    data_nco['branchings'] = branchings_nco

    particle_data[nco_id] = data_nco

# Part 3 combine the data and save everything
all_data = nuclide_data.copy()
all_data.update(particle_data)
file_path = path.join('particle_data.ppo')
pickle.dump(all_data, open(file_path, 'wb'), protocol=-1)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10920436817472699, 'fraction_numerical': 0.029641185647425898, 'mean_word_length': 3.1263073209975865, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 3, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '1', 'dir': 'jupyter-structured-clean-dedup', 'id': '526411', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 1884, 'n_tokens_neox': 1735, 'n_words': 489, 'path': 'construct_particle_nuclide_database.ipynb', 'repo_name': 'joheinze/PriNCe-data-utils', 'size': '5128'}