Skip to content

Commit 54a5f77

Browse files
committed
Initial commit
0 parents  commit 54a5f77

38 files changed

+63911
-0
lines changed

.github/workflows/tests.yml

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Unit Tests
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
build:
7+
runs-on: ubuntu-latest
8+
strategy:
9+
matrix:
10+
python-version: ['3.10', '3.11', '3.12', '3.13']
11+
12+
steps:
13+
- name: Checkout repository
14+
- uses: actions/checkout@v4
15+
- name: Set up Python ${{ matrix.python-version }}
16+
uses: actions/setup-python@v5
17+
with:
18+
python-version: ${{ matrix.python-version }}
19+
- name: Install dependencies
20+
run: |
21+
python -m pip install --upgrade pip
22+
pip install -e .
23+
- name: Test with pytest
24+
run: pytest -v

.gitignore

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Files and directories created by python
2+
**/__pycache__/
3+
**/.pytest_cache
4+
5+
# A catch all for virtual env directories
6+
.venv*/
7+
venv*/
8+
9+
# PyCharm settings directory
10+
.idea/
11+
12+
# Jupyter related files and directories
13+
*.ipynb
14+
.ipynb_checkpoints/
15+
16+
# Test coverage related files and directories
17+
.coverage
18+
coverage_html_report
19+
20+
# Package build files and folders
21+
*.egg-info/

LICENSE

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
MIT License
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is
8+
furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19+
SOFTWARE.

nuclearmasses/__init__.py

Whitespace-only changes.

nuclearmasses/ame_mass_file.py

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"""Storage for variable line positions."""
2+
from nuclearmasses.parse import Parse
3+
4+
5+
class AMEMassFile(Parse):
6+
"""Easy access to where the variables are in the AME mass file."""
7+
8+
def __init__(self, year: int):
9+
"""Setup up the values."""
10+
super().__init__()
11+
if year < 2020:
12+
self.HEADER = 39
13+
self.FOOTER = None
14+
self.START_A = 16
15+
self.END_A = 19
16+
self.START_Z = 11
17+
self.END_Z = 14
18+
self.START_ME = 29
19+
self.END_ME = 41
20+
self.START_DME = 42
21+
self.END_DME = 53
22+
self.START_BE_PER_A = 54
23+
self.END_BE_PER_A = 64
24+
self.START_DBE_PER_A = 65
25+
self.END_DBE_PER_A = 72
26+
self.START_BETA_DECAY_ENERGY = 76
27+
self.END_BETA_DECAY_ENERGY = 86
28+
self.START_DBETA_DECAY_ENERGY = 87
29+
self.END_DBETA_DECAY_ENERGY = 95
30+
self.START_MICRO_U = 100
31+
self.END_MICRO_U = 112
32+
self.START_MICRO_DU = 113
33+
self.END_MICRO_DU = 125
34+
else:
35+
self.HEADER = 36
36+
self.FOOTER = None
37+
self.START_A = 16
38+
self.END_A = 19
39+
self.START_Z = 11
40+
self.END_Z = 14
41+
self.START_ME = 29
42+
self.END_ME = 42
43+
self.START_DME = 43
44+
self.END_DME = 53
45+
self.START_BE_PER_A = 56
46+
self.END_BE_PER_A = 66
47+
self.START_DBE_PER_A = 69
48+
self.END_DBE_PER_A = 77
49+
self.START_BETA_DECAY_ENERGY = 82
50+
self.END_BETA_DECAY_ENERGY = 93
51+
self.START_DBETA_DECAY_ENERGY = 95
52+
self.END_DBETA_DECAY_ENERGY = 104
53+
self.START_MICRO_U = 110
54+
self.END_MICRO_U = 120
55+
self.START_MICRO_DU = 124
56+
self.END_MICRO_DU = 135

nuclearmasses/ame_mass_parse.py

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
"""Extract the data from the AME mass file."""
2+
import logging
3+
import pathlib
4+
5+
import pandas as pd
6+
7+
from nuclearmasses.ame_mass_file import AMEMassFile
8+
9+
10+
class AMEMassParser(AMEMassFile):
11+
"""Parse the AME mass file.
12+
13+
The format is known but I don't think python can easily parse it.
14+
"""
15+
16+
def __init__(self, filename: pathlib.Path, year: int):
17+
"""Set the file to read and table year."""
18+
self.filename = filename
19+
self.year = year
20+
super().__init__(self.year)
21+
logging.info(f"Reading {self.filename} from {self.year}")
22+
23+
def _read_line(self, line: str) -> dict:
24+
"""Read a line from the file."""
25+
if line.find("#") != -1:
26+
line = line.replace("#", " ")
27+
28+
data = {
29+
"TableYear": self.year,
30+
"A": self._read_as_int(line, self.START_A, self.END_A),
31+
"Z": self._read_as_int(line, self.START_Z, self.END_Z),
32+
"AMEMassExcess": self._read_as_float(line, self.START_ME, self.END_ME),
33+
"AMEMassExcessError": self._read_as_float(line, self.START_DME, self.END_DME),
34+
"BindingEnergyPerA": self._read_as_float(line, self.START_BE_PER_A, self.END_BE_PER_A),
35+
"BindingEnergyPerAError": self._read_as_float(line, self.START_DBE_PER_A, self.END_DBE_PER_A),
36+
"BetaDecayEnergy": self._read_as_float(line, self.START_BETA_DECAY_ENERGY, self.END_BETA_DECAY_ENERGY),
37+
"BetaDecayEnergyError": self._read_as_float(line, self.START_DBETA_DECAY_ENERGY, self.END_DBETA_DECAY_ENERGY),
38+
"AtomicMass": self._read_as_float(line, self.START_MICRO_U, self.END_MICRO_U),
39+
"AtomicMassError": self._read_as_float(line, self.START_MICRO_DU, self.END_MICRO_DU),
40+
}
41+
42+
data["N"] = data["A"] - data["Z"]
43+
data["Symbol"] = self.z_to_symbol[data["Z"]]
44+
45+
return data
46+
47+
def read_file(self) -> pd.DataFrame:
48+
"""Read the file."""
49+
with open(self.filename, "r") as f:
50+
lines = [line.rstrip() for line in f]
51+
52+
# Remove the header lines
53+
lines = lines[self.HEADER: self.FOOTER]
54+
55+
return pd.DataFrame([self._read_line(line) for line in lines])

nuclearmasses/ame_reaction_1_file.py

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
"""Storage for the variable line positions."""
2+
from nuclearmasses.parse import Parse
3+
4+
5+
class AMEReactionFileOne(Parse):
6+
"""Easy access to where the variables are in the first AME reaction file."""
7+
8+
def __init__(self, year: int):
9+
"""Setup the values that locate the variable."""
10+
super().__init__()
11+
if year < 2020:
12+
self.HEADER = 39
13+
self.FOOTER = None
14+
self.START_R1_A = 1
15+
self.END_R1_A = 4
16+
self.START_R1_Z = 8
17+
self.END_R1_Z = 11
18+
self.START_S2N = 14
19+
self.END_S2N = 22
20+
self.START_DS2N = 23
21+
self.END_DS2N = 30
22+
self.START_S2P = 32
23+
self.END_S2P = 40
24+
self.START_DS2P = 41
25+
self.END_DS2P = 48
26+
self.START_QA = 50
27+
self.END_QA = 58
28+
self.START_DQA = 59
29+
self.END_DQA = 66
30+
self.START_Q2B = 67
31+
self.END_Q2B = 76
32+
self.START_DQ2B = 77
33+
self.END_DQ2B = 84
34+
self.START_QEP = 85
35+
self.END_QEP = 94
36+
self.START_DQEP = 95
37+
self.END_DQEP = 102
38+
self.START_QBN = 103
39+
self.END_QBN = 112
40+
self.START_DQBN = 113
41+
self.END_DQBN = 125
42+
else:
43+
self.HEADER = 35
44+
self.FOOTER = None
45+
self.START_R1_A = 1
46+
self.END_R1_A = 4
47+
self.START_R1_Z = 8
48+
self.END_R1_Z = 11
49+
self.START_S2N = 14
50+
self.END_S2N = 24
51+
self.START_DS2N = 25
52+
self.END_DS2N = 34
53+
self.START_S2P = 36
54+
self.END_S2P = 46
55+
self.START_DS2P = 47
56+
self.END_DS2P = 56
57+
self.START_QA = 58
58+
self.END_QA = 68
59+
self.START_DQA = 69
60+
self.END_DQA = 78
61+
self.START_Q2B = 79
62+
self.END_Q2B = 90
63+
self.START_DQ2B = 91
64+
self.END_DQ2B = 100
65+
self.START_QEP = 101
66+
self.END_QEP = 112
67+
self.START_DQEP = 113
68+
self.END_DQEP = 122
69+
self.START_QBN = 123
70+
self.END_QBN = 134
71+
self.START_DQBN = 135
72+
self.END_DQBN = 144

nuclearmasses/ame_reaction_1_parse.py

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
"""Extract the data from the first reaction file."""
2+
import logging
3+
import pathlib
4+
5+
import pandas as pd
6+
7+
from nuclearmasses.ame_reaction_1_file import AMEReactionFileOne
8+
9+
10+
class AMEReactionParserOne(AMEReactionFileOne):
11+
"""Parse the first AME reaction file.
12+
13+
The format is known but I don't think python can easily parse it.
14+
"""
15+
16+
def __init__(self, filename: pathlib.Path, year: int):
17+
"""Set the file to read and table year."""
18+
self.filename = filename
19+
self.year = year
20+
super().__init__(self.year)
21+
logging.info(f"Reading {self.filename} from {self.year}")
22+
23+
def _read_line(self, line: str) -> dict:
24+
"""Read a line from the file."""
25+
# Don't use a '#' as an experimental marker in this file
26+
# but still need to remove it
27+
if line.find("#") != -1:
28+
line = line.replace("#", " ")
29+
30+
data = {
31+
"TableYear": self.year,
32+
"A": self._read_as_int(line, self.START_R1_A, self.END_R1_A),
33+
"Z": self._read_as_int(line, self.START_R1_Z, self.END_R1_Z),
34+
"TwoNeutronSeparationEnergy": self._read_as_float(line, self.START_S2N, self.END_S2N),
35+
"TwoNeutronSeparationEnergyError": self._read_as_float(line, self.START_DS2N, self.END_DS2N),
36+
"TwoProtonSeparationEnergy": self._read_as_float(line, self.START_S2P, self.END_S2P),
37+
"TwoProtonSeparationEnergyError": self._read_as_float(line, self.START_DS2P, self.END_DS2P),
38+
"QAlpha": self._read_as_float(line, self.START_QA, self.END_QA),
39+
"QAlphaError": self._read_as_float(line, self.START_DQA, self.END_DQA),
40+
"QTwoBeta": self._read_as_float(line, self.START_Q2B, self.END_Q2B),
41+
"QTwoBetaError": self._read_as_float(line, self.START_DQ2B, self.END_DQ2B),
42+
"QEpsilon": self._read_as_float(line, self.START_QEP, self.END_QEP),
43+
"QEpsilonError": self._read_as_float(line, self.START_DQEP, self.END_DQEP),
44+
"QBetaNeutron": self._read_as_float(line, self.START_QBN, self.END_QBN),
45+
"QBetaNeutronError": self._read_as_float(line, self.START_DQBN, self.END_DQBN),
46+
}
47+
48+
data["N"] = data["A"] - data["Z"]
49+
data["Symbol"] = self.z_to_symbol[data["Z"]]
50+
51+
return data
52+
53+
def read_file(self) -> pd.DataFrame:
54+
"""Read the file."""
55+
with open(self.filename, "r") as f:
56+
lines = [line.rstrip() for line in f]
57+
58+
# Remove the header lines
59+
lines = lines[self.HEADER: self.FOOTER]
60+
61+
return pd.DataFrame([self._read_line(line) for line in lines])

nuclearmasses/ame_reaction_2_file.py

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
"""Storage for the variable line positions."""
2+
from nuclearmasses.parse import Parse
3+
4+
5+
class AMEReactionFileTwo(Parse):
6+
"""Easy access to where the variables are in the second AME reaction file."""
7+
8+
def __init__(self, year: int):
9+
"""Setup the values that locate the variables."""
10+
super().__init__()
11+
if year < 2020:
12+
self.HEADER = 39
13+
self.FOOTER = None
14+
self.START_R2_A = 1
15+
self.END_R2_A = 4
16+
self.START_R2_Z = 8
17+
self.END_R2_Z = 11
18+
self.START_SN = 14
19+
self.END_SN = 22
20+
self.START_DSN = 23
21+
self.END_DSN = 30
22+
self.START_SP = 32
23+
self.END_SP = 40
24+
self.START_DSP = 41
25+
self.END_DSP = 48
26+
self.START_Q4B = 49
27+
self.END_Q4B = 58
28+
self.START_DQ4B = 59
29+
self.END_DQ4B = 66
30+
self.START_QDA = 67
31+
self.END_QDA = 76
32+
self.START_DQDA = 77
33+
self.END_DQDA = 84
34+
self.START_QPA = 85
35+
self.END_QPA = 94
36+
self.START_DQPA = 95
37+
self.END_DQPA = 102
38+
self.START_QNA = 103
39+
self.END_QNA = 112
40+
self.START_DQNA = 113
41+
self.END_DQNA = 125
42+
else:
43+
self.HEADER = 37
44+
self.FOOTER = 3645
45+
self.START_R2_A = 1
46+
self.END_R2_A = 4
47+
self.START_R2_Z = 8
48+
self.END_R2_Z = 11
49+
self.START_SN = 14
50+
self.END_SN = 24
51+
self.START_DSN = 25
52+
self.END_DSN = 34
53+
self.START_SP = 36
54+
self.END_SP = 46
55+
self.START_DSP = 47
56+
self.END_DSP = 56
57+
self.START_Q4B = 57
58+
self.END_Q4B = 68
59+
self.START_DQ4B = 69
60+
self.END_DQ4B = 78
61+
self.START_QDA = 79
62+
self.END_QDA = 90
63+
self.START_DQDA = 91
64+
self.END_DQDA = 100
65+
self.START_QPA = 101
66+
self.END_QPA = 112
67+
self.START_DQPA = 113
68+
self.END_DQPA = 122
69+
self.START_QNA = 123
70+
self.END_QNA = 134
71+
self.START_DQNA = 135
72+
self.END_DQNA = 144

0 commit comments

Comments
 (0)