fusion-neutron-utils 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fusion_neutron_utils-0.1.0/.github/workflows/ci-python.yml +36 -0
- fusion_neutron_utils-0.1.0/.github/workflows/ci-rust.yml +19 -0
- fusion_neutron_utils-0.1.0/.github/workflows/python-publish.yml +159 -0
- fusion_neutron_utils-0.1.0/.gitignore +162 -0
- fusion_neutron_utils-0.1.0/Cargo.lock +171 -0
- fusion_neutron_utils-0.1.0/Cargo.toml +12 -0
- fusion_neutron_utils-0.1.0/LICENSE +21 -0
- fusion_neutron_utils-0.1.0/PKG-INFO +80 -0
- fusion_neutron_utils-0.1.0/README.md +65 -0
- fusion_neutron_utils-0.1.0/pyproject.toml +18 -0
- fusion_neutron_utils-0.1.0/src/lib.rs +396 -0
- fusion_neutron_utils-0.1.0/tests/test_average_neutron_energy.py +58 -0
- fusion_neutron_utils-0.1.0/tests/test_relative_reaction_rates.py +41 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: CI Python testing
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches:
|
|
6
|
+
- develop
|
|
7
|
+
- main
|
|
8
|
+
push:
|
|
9
|
+
branches:
|
|
10
|
+
- main
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
strategy:
|
|
17
|
+
matrix:
|
|
18
|
+
python-version: ["3.9", "3.10", "3.11", "3.12"] # TODO retry with latest python, "3.13"]
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
23
|
+
uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version: ${{ matrix.python-version }}
|
|
26
|
+
|
|
27
|
+
- name: Display Python version
|
|
28
|
+
run: python -c "import sys; print(sys.version)"
|
|
29
|
+
|
|
30
|
+
- name: install package
|
|
31
|
+
run: |
|
|
32
|
+
python -m pip install --upgrade pip
|
|
33
|
+
python -m pip install .[tests]
|
|
34
|
+
|
|
35
|
+
- name: Run tests
|
|
36
|
+
run: pytest
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
name: CI Rust testing
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
env:
|
|
8
|
+
CARGO_TERM_COLOR: always
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build_and_test:
|
|
12
|
+
name: Rust project - latest
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- run: rustup update stable && rustup default stable
|
|
18
|
+
- run: cargo build --verbose
|
|
19
|
+
- run: cargo test --verbose
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# This file is autogenerated by maturin v1.7.4
|
|
2
|
+
# To update, run
|
|
3
|
+
#
|
|
4
|
+
# maturin generate-ci github -o .github/workflows/python-publish.yml --pytest --platform manylinux windows
|
|
5
|
+
#
|
|
6
|
+
name: CI
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
branches:
|
|
11
|
+
- main
|
|
12
|
+
- master
|
|
13
|
+
tags:
|
|
14
|
+
- '*'
|
|
15
|
+
pull_request:
|
|
16
|
+
workflow_dispatch:
|
|
17
|
+
|
|
18
|
+
permissions:
|
|
19
|
+
contents: read
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
linux:
|
|
23
|
+
runs-on: ${{ matrix.platform.runner }}
|
|
24
|
+
strategy:
|
|
25
|
+
matrix:
|
|
26
|
+
platform:
|
|
27
|
+
- runner: ubuntu-latest
|
|
28
|
+
target: x86_64
|
|
29
|
+
- runner: ubuntu-latest
|
|
30
|
+
target: x86
|
|
31
|
+
- runner: ubuntu-latest
|
|
32
|
+
target: aarch64
|
|
33
|
+
- runner: ubuntu-latest
|
|
34
|
+
target: armv7
|
|
35
|
+
- runner: ubuntu-latest
|
|
36
|
+
target: s390x
|
|
37
|
+
- runner: ubuntu-latest
|
|
38
|
+
target: ppc64le
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@v4
|
|
41
|
+
- uses: actions/setup-python@v5
|
|
42
|
+
with:
|
|
43
|
+
python-version: 3.x
|
|
44
|
+
- name: Build wheels
|
|
45
|
+
uses: PyO3/maturin-action@v1
|
|
46
|
+
with:
|
|
47
|
+
target: ${{ matrix.platform.target }}
|
|
48
|
+
args: --release --out dist --find-interpreter
|
|
49
|
+
sccache: 'true'
|
|
50
|
+
manylinux: auto
|
|
51
|
+
- name: Upload wheels
|
|
52
|
+
uses: actions/upload-artifact@v4
|
|
53
|
+
with:
|
|
54
|
+
name: wheels-linux-${{ matrix.platform.target }}
|
|
55
|
+
path: dist
|
|
56
|
+
- name: pytest
|
|
57
|
+
if: ${{ startsWith(matrix.platform.target, 'x86_64') }}
|
|
58
|
+
shell: bash
|
|
59
|
+
run: |
|
|
60
|
+
set -e
|
|
61
|
+
python3 -m venv .venv
|
|
62
|
+
source .venv/bin/activate
|
|
63
|
+
pip install fusion_neutron_utils --find-links dist --force-reinstall
|
|
64
|
+
pip install pytest
|
|
65
|
+
pytest
|
|
66
|
+
- name: pytest
|
|
67
|
+
if: ${{ !startsWith(matrix.platform.target, 'x86') && matrix.platform.target != 'ppc64' }}
|
|
68
|
+
uses: uraimo/run-on-arch-action@v2
|
|
69
|
+
with:
|
|
70
|
+
arch: ${{ matrix.platform.target }}
|
|
71
|
+
distro: ubuntu22.04
|
|
72
|
+
githubToken: ${{ github.token }}
|
|
73
|
+
install: |
|
|
74
|
+
apt-get update
|
|
75
|
+
apt-get install -y --no-install-recommends python3 python3-pip
|
|
76
|
+
pip3 install -U pip pytest
|
|
77
|
+
run: |
|
|
78
|
+
set -e
|
|
79
|
+
pip3 install fusion_neutron_utils --find-links dist --force-reinstall
|
|
80
|
+
pytest
|
|
81
|
+
|
|
82
|
+
windows:
|
|
83
|
+
runs-on: ${{ matrix.platform.runner }}
|
|
84
|
+
strategy:
|
|
85
|
+
matrix:
|
|
86
|
+
platform:
|
|
87
|
+
- runner: windows-latest
|
|
88
|
+
target: x64
|
|
89
|
+
- runner: windows-latest
|
|
90
|
+
target: x86
|
|
91
|
+
steps:
|
|
92
|
+
- uses: actions/checkout@v4
|
|
93
|
+
- uses: actions/setup-python@v5
|
|
94
|
+
with:
|
|
95
|
+
python-version: 3.x
|
|
96
|
+
architecture: ${{ matrix.platform.target }}
|
|
97
|
+
- name: Build wheels
|
|
98
|
+
uses: PyO3/maturin-action@v1
|
|
99
|
+
with:
|
|
100
|
+
target: ${{ matrix.platform.target }}
|
|
101
|
+
args: --release --out dist --find-interpreter
|
|
102
|
+
sccache: 'true'
|
|
103
|
+
- name: Upload wheels
|
|
104
|
+
uses: actions/upload-artifact@v4
|
|
105
|
+
with:
|
|
106
|
+
name: wheels-windows-${{ matrix.platform.target }}
|
|
107
|
+
path: dist
|
|
108
|
+
- name: pytest
|
|
109
|
+
if: ${{ !startsWith(matrix.platform.target, 'aarch64') }}
|
|
110
|
+
shell: bash
|
|
111
|
+
run: |
|
|
112
|
+
set -e
|
|
113
|
+
python3 -m venv .venv
|
|
114
|
+
source .venv/Scripts/activate
|
|
115
|
+
pip install fusion_neutron_utils --find-links dist --force-reinstall
|
|
116
|
+
pip install pytest
|
|
117
|
+
pytest
|
|
118
|
+
|
|
119
|
+
sdist:
|
|
120
|
+
runs-on: ubuntu-latest
|
|
121
|
+
steps:
|
|
122
|
+
- uses: actions/checkout@v4
|
|
123
|
+
- name: Build sdist
|
|
124
|
+
uses: PyO3/maturin-action@v1
|
|
125
|
+
with:
|
|
126
|
+
command: sdist
|
|
127
|
+
args: --out dist
|
|
128
|
+
- name: Upload sdist
|
|
129
|
+
uses: actions/upload-artifact@v4
|
|
130
|
+
with:
|
|
131
|
+
name: wheels-sdist
|
|
132
|
+
path: dist
|
|
133
|
+
|
|
134
|
+
release:
|
|
135
|
+
name: Release
|
|
136
|
+
runs-on: ubuntu-latest
|
|
137
|
+
if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }}
|
|
138
|
+
needs: [linux, windows, sdist]
|
|
139
|
+
permissions:
|
|
140
|
+
# Use to sign the release artifacts
|
|
141
|
+
id-token: write
|
|
142
|
+
# Used to upload release artifacts
|
|
143
|
+
contents: write
|
|
144
|
+
# Used to generate artifact attestation
|
|
145
|
+
attestations: write
|
|
146
|
+
steps:
|
|
147
|
+
- uses: actions/download-artifact@v4
|
|
148
|
+
- name: Generate artifact attestation
|
|
149
|
+
uses: actions/attest-build-provenance@v1
|
|
150
|
+
with:
|
|
151
|
+
subject-path: 'wheels-*/*'
|
|
152
|
+
- name: Publish to PyPI
|
|
153
|
+
if: "startsWith(github.ref, 'refs/tags/')"
|
|
154
|
+
uses: PyO3/maturin-action@v1
|
|
155
|
+
env:
|
|
156
|
+
MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
|
|
157
|
+
with:
|
|
158
|
+
command: upload
|
|
159
|
+
args: --non-interactive --skip-existing wheels-*/*
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py,cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
#Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# poetry
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
102
|
+
#poetry.lock
|
|
103
|
+
|
|
104
|
+
# pdm
|
|
105
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
106
|
+
#pdm.lock
|
|
107
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
108
|
+
# in version control.
|
|
109
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
110
|
+
.pdm.toml
|
|
111
|
+
.pdm-python
|
|
112
|
+
.pdm-build/
|
|
113
|
+
|
|
114
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
115
|
+
__pypackages__/
|
|
116
|
+
|
|
117
|
+
# Celery stuff
|
|
118
|
+
celerybeat-schedule
|
|
119
|
+
celerybeat.pid
|
|
120
|
+
|
|
121
|
+
# SageMath parsed files
|
|
122
|
+
*.sage.py
|
|
123
|
+
|
|
124
|
+
# Environments
|
|
125
|
+
.env
|
|
126
|
+
.venv
|
|
127
|
+
env/
|
|
128
|
+
venv/
|
|
129
|
+
ENV/
|
|
130
|
+
env.bak/
|
|
131
|
+
venv.bak/
|
|
132
|
+
|
|
133
|
+
# Spyder project settings
|
|
134
|
+
.spyderproject
|
|
135
|
+
.spyproject
|
|
136
|
+
|
|
137
|
+
# Rope project settings
|
|
138
|
+
.ropeproject
|
|
139
|
+
|
|
140
|
+
# mkdocs documentation
|
|
141
|
+
/site
|
|
142
|
+
|
|
143
|
+
# mypy
|
|
144
|
+
.mypy_cache/
|
|
145
|
+
.dmypy.json
|
|
146
|
+
dmypy.json
|
|
147
|
+
|
|
148
|
+
# Pyre type checker
|
|
149
|
+
.pyre/
|
|
150
|
+
|
|
151
|
+
# pytype static type analyzer
|
|
152
|
+
.pytype/
|
|
153
|
+
|
|
154
|
+
# Cython debug symbols
|
|
155
|
+
cython_debug/
|
|
156
|
+
|
|
157
|
+
# PyCharm
|
|
158
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
159
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
160
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
161
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
162
|
+
#.idea/
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 3
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "autocfg"
|
|
7
|
+
version = "1.3.0"
|
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
9
|
+
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
|
10
|
+
|
|
11
|
+
[[package]]
|
|
12
|
+
name = "cfg-if"
|
|
13
|
+
version = "1.0.0"
|
|
14
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
15
|
+
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|
16
|
+
|
|
17
|
+
[[package]]
|
|
18
|
+
name = "fusion-reaction-rate"
|
|
19
|
+
version = "0.1.0"
|
|
20
|
+
dependencies = [
|
|
21
|
+
"pyo3",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[[package]]
|
|
25
|
+
name = "heck"
|
|
26
|
+
version = "0.5.0"
|
|
27
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
28
|
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
29
|
+
|
|
30
|
+
[[package]]
|
|
31
|
+
name = "indoc"
|
|
32
|
+
version = "2.0.5"
|
|
33
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
34
|
+
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
|
|
35
|
+
|
|
36
|
+
[[package]]
|
|
37
|
+
name = "libc"
|
|
38
|
+
version = "0.2.158"
|
|
39
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
40
|
+
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
|
|
41
|
+
|
|
42
|
+
[[package]]
|
|
43
|
+
name = "memoffset"
|
|
44
|
+
version = "0.9.1"
|
|
45
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
46
|
+
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
|
47
|
+
dependencies = [
|
|
48
|
+
"autocfg",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[[package]]
|
|
52
|
+
name = "once_cell"
|
|
53
|
+
version = "1.19.0"
|
|
54
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
55
|
+
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
|
56
|
+
|
|
57
|
+
[[package]]
|
|
58
|
+
name = "portable-atomic"
|
|
59
|
+
version = "1.7.0"
|
|
60
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
61
|
+
checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265"
|
|
62
|
+
|
|
63
|
+
[[package]]
|
|
64
|
+
name = "proc-macro2"
|
|
65
|
+
version = "1.0.86"
|
|
66
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
67
|
+
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
|
68
|
+
dependencies = [
|
|
69
|
+
"unicode-ident",
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
[[package]]
|
|
73
|
+
name = "pyo3"
|
|
74
|
+
version = "0.22.2"
|
|
75
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
76
|
+
checksum = "831e8e819a138c36e212f3af3fd9eeffed6bf1510a805af35b0edee5ffa59433"
|
|
77
|
+
dependencies = [
|
|
78
|
+
"cfg-if",
|
|
79
|
+
"indoc",
|
|
80
|
+
"libc",
|
|
81
|
+
"memoffset",
|
|
82
|
+
"once_cell",
|
|
83
|
+
"portable-atomic",
|
|
84
|
+
"pyo3-build-config",
|
|
85
|
+
"pyo3-ffi",
|
|
86
|
+
"pyo3-macros",
|
|
87
|
+
"unindent",
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
[[package]]
|
|
91
|
+
name = "pyo3-build-config"
|
|
92
|
+
version = "0.22.2"
|
|
93
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
94
|
+
checksum = "1e8730e591b14492a8945cdff32f089250b05f5accecf74aeddf9e8272ce1fa8"
|
|
95
|
+
dependencies = [
|
|
96
|
+
"once_cell",
|
|
97
|
+
"target-lexicon",
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
[[package]]
|
|
101
|
+
name = "pyo3-ffi"
|
|
102
|
+
version = "0.22.2"
|
|
103
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
104
|
+
checksum = "5e97e919d2df92eb88ca80a037969f44e5e70356559654962cbb3316d00300c6"
|
|
105
|
+
dependencies = [
|
|
106
|
+
"libc",
|
|
107
|
+
"pyo3-build-config",
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
[[package]]
|
|
111
|
+
name = "pyo3-macros"
|
|
112
|
+
version = "0.22.2"
|
|
113
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
114
|
+
checksum = "eb57983022ad41f9e683a599f2fd13c3664d7063a3ac5714cae4b7bee7d3f206"
|
|
115
|
+
dependencies = [
|
|
116
|
+
"proc-macro2",
|
|
117
|
+
"pyo3-macros-backend",
|
|
118
|
+
"quote",
|
|
119
|
+
"syn",
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
[[package]]
|
|
123
|
+
name = "pyo3-macros-backend"
|
|
124
|
+
version = "0.22.2"
|
|
125
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
126
|
+
checksum = "ec480c0c51ddec81019531705acac51bcdbeae563557c982aa8263bb96880372"
|
|
127
|
+
dependencies = [
|
|
128
|
+
"heck",
|
|
129
|
+
"proc-macro2",
|
|
130
|
+
"pyo3-build-config",
|
|
131
|
+
"quote",
|
|
132
|
+
"syn",
|
|
133
|
+
]
|
|
134
|
+
|
|
135
|
+
[[package]]
|
|
136
|
+
name = "quote"
|
|
137
|
+
version = "1.0.37"
|
|
138
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
139
|
+
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
|
140
|
+
dependencies = [
|
|
141
|
+
"proc-macro2",
|
|
142
|
+
]
|
|
143
|
+
|
|
144
|
+
[[package]]
|
|
145
|
+
name = "syn"
|
|
146
|
+
version = "2.0.77"
|
|
147
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
148
|
+
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
|
|
149
|
+
dependencies = [
|
|
150
|
+
"proc-macro2",
|
|
151
|
+
"quote",
|
|
152
|
+
"unicode-ident",
|
|
153
|
+
]
|
|
154
|
+
|
|
155
|
+
[[package]]
|
|
156
|
+
name = "target-lexicon"
|
|
157
|
+
version = "0.12.16"
|
|
158
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
159
|
+
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
|
160
|
+
|
|
161
|
+
[[package]]
|
|
162
|
+
name = "unicode-ident"
|
|
163
|
+
version = "1.0.12"
|
|
164
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
165
|
+
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
|
166
|
+
|
|
167
|
+
[[package]]
|
|
168
|
+
name = "unindent"
|
|
169
|
+
version = "0.2.3"
|
|
170
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
171
|
+
checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "fusion-reaction-rate"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
|
|
6
|
+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
7
|
+
[lib]
|
|
8
|
+
name = "fusion_neutron_utils"
|
|
9
|
+
crate-type = ["cdylib"]
|
|
10
|
+
|
|
11
|
+
[dependencies]
|
|
12
|
+
pyo3 = "0.22.2"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Fusion Energy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: fusion_neutron_utils
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Classifier: Programming Language :: Rust
|
|
5
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
7
|
+
Requires-Dist: pytest ; extra == 'tests'
|
|
8
|
+
Requires-Dist: scipy ==1.13.1 ; extra == 'tests'
|
|
9
|
+
Requires-Dist: nesst ; extra == 'tests'
|
|
10
|
+
Provides-Extra: tests
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Python: >=3.8
|
|
13
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
14
|
+
|
|
15
|
+
[](https://github.com/fusion-energy/fusion_neutron_utils/actions/workflows/ci-python.yml) [](https://github.com/fusion-energy/fusion_neutron_utils/actions/workflows/ci-rust.yml)
|
|
16
|
+
|
|
17
|
+
A package for calculating neutron properties from DT and DD fusion reactions.
|
|
18
|
+
|
|
19
|
+
This package accurately calculates the neutron energies and distributions by accounting for the plasma temperature.
|
|
20
|
+
|
|
21
|
+
- mean neutron energy
|
|
22
|
+
- neutron energy standard deviation ()
|
|
23
|
+
- thermal reactivity
|
|
24
|
+
|
|
25
|
+
A Python :snake: package with a Rust :crab: backend.
|
|
26
|
+
|
|
27
|
+
The package makes use of the [Sadler–Van Belle formula](https://doi.org/10.1016/j.fusengdes.2012.02.025) and [Bosch-Hale](https://doi.org/10.1088/0029-5515%2F32%2F4%2FI07) parametrization for reactivity. For energy distributions [Ballabio](https://doi.org/10.1088/0029-5515/38/11/310) is used.
|
|
28
|
+
|
|
29
|
+
This package has been inspired by the [NeSST package](https://github.com/aidancrilly/NeSST).
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install fusion_neutron_utils
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
To find the average neutron energy and the standard deviation of that energy.
|
|
40
|
+
|
|
41
|
+
These results could then be input into [openmc.stats.Normal](https://docs.openmc.org/en/v0.12.1/pythonapi/generated/openmc.stats.Normal.html) distribution for a neutron source term in OpenMC
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from fusion_neutron_utils import neutron_energy_mean_and_std_dev
|
|
45
|
+
neutron_energy_mean_and_std_dev(
|
|
46
|
+
reaction='D+T=n+a',
|
|
47
|
+
ion_temperature=30e3,
|
|
48
|
+
temperature_units='eV',
|
|
49
|
+
neutron_energy_units='eV'
|
|
50
|
+
)
|
|
51
|
+
>>>>(14021063.802198425, 413861.375751198)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
The relative reaction rates can be found for the different reactions, this can be useful for setting the relative ```Source.strength``` in OpenMC. Relative reaction rates returns the DT, DD (n+He3), and DD (p+T) reaction rates in that order. Note the DD (p+T) does not emit a neutron but is there for completeness.
|
|
56
|
+
```python
|
|
57
|
+
from fusion_neutron_utils import relative_reaction_rates
|
|
58
|
+
relative_reaction_rates(
|
|
59
|
+
ion_temperature=30e3,
|
|
60
|
+
temperature_units='eV',
|
|
61
|
+
dt_fraction=0.1,
|
|
62
|
+
dd_fraction=0.9,
|
|
63
|
+
)
|
|
64
|
+
>>>[0.9989900631769298, 0.0005595866500573114, 0.00045035017301275736]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The reactivity can also be found, this can be useful for finding the relative reaction rate for D+D or D+T at a specific temperature
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from fusion_neutron_utils import reactivity
|
|
71
|
+
reactivity(
|
|
72
|
+
ion_temperature=30e3,
|
|
73
|
+
temperature_units='eV',
|
|
74
|
+
reactivity_units='m^3/s',
|
|
75
|
+
reaction='D+D=p+T',
|
|
76
|
+
equation='Bosch-Hale'
|
|
77
|
+
)
|
|
78
|
+
>>>2.2356373732343755e-53
|
|
79
|
+
```
|
|
80
|
+
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
[](https://github.com/fusion-energy/fusion_neutron_utils/actions/workflows/ci-python.yml) [](https://github.com/fusion-energy/fusion_neutron_utils/actions/workflows/ci-rust.yml)
|
|
2
|
+
|
|
3
|
+
A package for calculating neutron properties from DT and DD fusion reactions.
|
|
4
|
+
|
|
5
|
+
This package accurately calculates the neutron energies and distributions by accounting for the plasma temperature.
|
|
6
|
+
|
|
7
|
+
- mean neutron energy
|
|
8
|
+
- neutron energy standard deviation ()
|
|
9
|
+
- thermal reactivity
|
|
10
|
+
|
|
11
|
+
A Python :snake: package with a Rust :crab: backend.
|
|
12
|
+
|
|
13
|
+
The package makes use of the [Sadler–Van Belle formula](https://doi.org/10.1016/j.fusengdes.2012.02.025) and [Bosch-Hale](https://doi.org/10.1088/0029-5515%2F32%2F4%2FI07) parametrization for reactivity. For energy distributions [Ballabio](https://doi.org/10.1088/0029-5515/38/11/310) is used.
|
|
14
|
+
|
|
15
|
+
This package has been inspired by the [NeSST package](https://github.com/aidancrilly/NeSST).
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install fusion_neutron_utils
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
To find the average neutron energy and the standard deviation of that energy.
|
|
26
|
+
|
|
27
|
+
These results could then be input into [openmc.stats.Normal](https://docs.openmc.org/en/v0.12.1/pythonapi/generated/openmc.stats.Normal.html) distribution for a neutron source term in OpenMC
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from fusion_neutron_utils import neutron_energy_mean_and_std_dev
|
|
31
|
+
neutron_energy_mean_and_std_dev(
|
|
32
|
+
reaction='D+T=n+a',
|
|
33
|
+
ion_temperature=30e3,
|
|
34
|
+
temperature_units='eV',
|
|
35
|
+
neutron_energy_units='eV'
|
|
36
|
+
)
|
|
37
|
+
>>>>(14021063.802198425, 413861.375751198)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
The relative reaction rates can be found for the different reactions, this can be useful for setting the relative ```Source.strength``` in OpenMC. Relative reaction rates returns the DT, DD (n+He3), and DD (p+T) reaction rates in that order. Note the DD (p+T) does not emit a neutron but is there for completeness.
|
|
42
|
+
```python
|
|
43
|
+
from fusion_neutron_utils import relative_reaction_rates
|
|
44
|
+
relative_reaction_rates(
|
|
45
|
+
ion_temperature=30e3,
|
|
46
|
+
temperature_units='eV',
|
|
47
|
+
dt_fraction=0.1,
|
|
48
|
+
dd_fraction=0.9,
|
|
49
|
+
)
|
|
50
|
+
>>>[0.9989900631769298, 0.0005595866500573114, 0.00045035017301275736]
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The reactivity can also be found, this can be useful for finding the relative reaction rate for D+D or D+T at a specific temperature
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from fusion_neutron_utils import reactivity
|
|
57
|
+
reactivity(
|
|
58
|
+
ion_temperature=30e3,
|
|
59
|
+
temperature_units='eV',
|
|
60
|
+
reactivity_units='m^3/s',
|
|
61
|
+
reaction='D+D=p+T',
|
|
62
|
+
equation='Bosch-Hale'
|
|
63
|
+
)
|
|
64
|
+
>>>2.2356373732343755e-53
|
|
65
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["maturin>=1.7,<2.0"]
|
|
3
|
+
build-backend = "maturin"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "fusion_neutron_utils"
|
|
7
|
+
requires-python = ">=3.8"
|
|
8
|
+
classifiers = [
|
|
9
|
+
"Programming Language :: Rust",
|
|
10
|
+
"Programming Language :: Python :: Implementation :: CPython",
|
|
11
|
+
"Programming Language :: Python :: Implementation :: PyPy",
|
|
12
|
+
]
|
|
13
|
+
dynamic = ["version"]
|
|
14
|
+
[tool.maturin]
|
|
15
|
+
features = ["pyo3/extension-module"]
|
|
16
|
+
|
|
17
|
+
[project.optional-dependencies]
|
|
18
|
+
tests = ["pytest", "scipy==1.13.1", "NeSST"]
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
use pyo3::prelude::*;
|
|
2
|
+
|
|
3
|
+
#[pyfunction(signature = (ion_temperature, temperature_units=None, neutron_energy_units=None, reaction=None))]
|
|
4
|
+
/// Calculate the average neutron energy for a given ion temperature and reaction.
|
|
5
|
+
#[pyo3(text_signature = "(ion_temperature, temperature_units='eV', neutron_energy_units='eV', reaction='D+T=n+a')")]
|
|
6
|
+
fn neutron_energy_mean_and_std_dev(
|
|
7
|
+
ion_temperature: f64,
|
|
8
|
+
temperature_units: Option<&str>,
|
|
9
|
+
neutron_energy_units: Option<&str>,
|
|
10
|
+
reaction: Option<&str>,
|
|
11
|
+
) -> PyResult<(f64, f64)> {
|
|
12
|
+
// values from Ballabio paper
|
|
13
|
+
let (a_1, a_2, a_3, a_4, mean) = match reaction {
|
|
14
|
+
Some("D+D=n+He3") => (4.69515, -0.040729, 0.47, 0.81844, 2.4495e6),
|
|
15
|
+
Some("D+T=n+a") => (5.30509, 2.4736e-3, 1.84, 1.3818, 14.021e6),
|
|
16
|
+
_ => return Err(PyErr::new::<pyo3::exceptions::PyValueError, _>("reaction must be either 'D+D=n+He3' or 'D+T=n+a'")),
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
let ion_temperature_kev: f64 = scale_temperature_units_to_kev(ion_temperature, temperature_units); // Ballabio equation accepts KeV units
|
|
20
|
+
|
|
21
|
+
// units of mean_delta are in ev
|
|
22
|
+
let mean_delta = a_1 * ion_temperature_kev.powf(2.0 / 3.0) / (1.0 + a_2 * ion_temperature_kev.powf(a_3)) + a_4 * ion_temperature_kev;
|
|
23
|
+
|
|
24
|
+
let mean_adjusted = mean + mean_delta;
|
|
25
|
+
print!("{}", mean);
|
|
26
|
+
|
|
27
|
+
let mean_scaled = scale_energy_in_kev_to_requested_units(mean_adjusted/1e3, neutron_energy_units);
|
|
28
|
+
|
|
29
|
+
let (w_0, a_1, a_2, a_3, a_4) = match reaction {
|
|
30
|
+
Some("D+D=n+He3") => (82.542, 1.7013e-3, 0.16888, 0.49, 7.9460e-4),
|
|
31
|
+
Some("D+T=n+a") => (177.259, 5.1068e-4, 7.6223e-3, 1.78, 8.7691e-5),
|
|
32
|
+
_ => unreachable!(), // This case is already handled above
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
let delta = a_1 * ion_temperature_kev.powf(2.0 / 3.0) / (1.0 + a_2 * ion_temperature_kev.powf(a_3)) + a_4 * ion_temperature_kev;
|
|
36
|
+
|
|
37
|
+
// 2.3548200450309493 on the line below comes from equation 2* math.sqrt(math.log(2)*2)
|
|
38
|
+
let variance = ((w_0 * (1.0 + delta)).powi(2) * ion_temperature_kev) / 2.3548200450309493_f64.powi(2);
|
|
39
|
+
// let variance = variance * 1e6; // converting keV^2 back to eV^2
|
|
40
|
+
let std_dev = variance.sqrt();
|
|
41
|
+
let std_dev = scale_energy_in_kev_to_requested_units(std_dev, neutron_energy_units);
|
|
42
|
+
|
|
43
|
+
Ok((mean_scaled, std_dev))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
#[pyfunction(signature = (ion_temperature, temperature_units=None, reactivity_units=None, reaction=None, equation=None))]
|
|
49
|
+
/// Bosch-Hale parametrization of D+T thermal reactivity, assuming a Maxwellian ion
|
|
50
|
+
/// temperature distribution. If ion_temperature_kev is given in keV, the returned
|
|
51
|
+
/// <sigma v> is in m^3/s. This is valid for 0.2 keV <= ion_temperature_kev <= 100 keV.
|
|
52
|
+
///
|
|
53
|
+
/// D + T -> T(3.56 MeV) + n(14.03 MeV)
|
|
54
|
+
///
|
|
55
|
+
/// Args:
|
|
56
|
+
/// ion_temperature_kev (float): Ion temperature.
|
|
57
|
+
/// temperature_units
|
|
58
|
+
///
|
|
59
|
+
/// Returns:
|
|
60
|
+
/// float: The thermal reactivity in m^3/s.
|
|
61
|
+
///
|
|
62
|
+
/// Source:
|
|
63
|
+
/// Sec. 5.2, Eqn. (12) - (14), Table VII in
|
|
64
|
+
/// H.-s. Bosch and G. M. Dale,
|
|
65
|
+
/// "Improved Formulas for Fusion Cross-Sections and Thermal Reactivities",
|
|
66
|
+
/// Nucl. Fusion 32, 611 (1992)
|
|
67
|
+
#[pyo3(text_signature = "(ion_temperature, temperature_units='eV', reactivity_units='m^3/s', reaction='D+T=n+a', equation='Bosch-Hale')")]
|
|
68
|
+
fn reactivity(
|
|
69
|
+
ion_temperature: f64,
|
|
70
|
+
temperature_units: Option<&str>,
|
|
71
|
+
reactivity_units: Option<&str>,
|
|
72
|
+
reaction: Option<&str>,
|
|
73
|
+
equation: Option<&str>,
|
|
74
|
+
) -> PyResult<f64> {
|
|
75
|
+
|
|
76
|
+
if ion_temperature <= 0.0 {
|
|
77
|
+
return Err(PyErr::new::<pyo3::exceptions::PyValueError, _>("Ion temperature must be positive and non-zero"));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let reaction = reaction.unwrap_or("D+T=n+a");
|
|
81
|
+
let equation_str = equation.unwrap_or("Bosch-Hale");
|
|
82
|
+
|
|
83
|
+
let ion_temperature_kev: f64 = scale_temperature_units_to_kev(ion_temperature, temperature_units);
|
|
84
|
+
|
|
85
|
+
let sigma_thermal_reactivity_scaled = if equation_str == "Bosch-Hale"{
|
|
86
|
+
let (c1, c2, c3, c4, c5, c6, c7, gamov, mrc2) = if reaction == "D+T=n+a" {
|
|
87
|
+
(
|
|
88
|
+
1.17302e-9, 1.51361e-2, 7.51886e-2, 4.60643e-3, 1.35000e-2, -1.06750e-4, 1.36600e-5,
|
|
89
|
+
34.3827, 1_124_656.0,
|
|
90
|
+
)
|
|
91
|
+
} else if reaction == "D+D=p+T" {
|
|
92
|
+
(
|
|
93
|
+
5.65718e-12, 3.41267e-3, 1.99167e-3, 0.0, 1.05060e-5, 0.0, 0.0,
|
|
94
|
+
31.3970, 937_814.0,
|
|
95
|
+
)
|
|
96
|
+
} else if reaction == "D+D=n+He3" {
|
|
97
|
+
(
|
|
98
|
+
5.43360e-12, 5.85778e-3, 7.68222e-3, 0.0, -2.96400e-6, 0.0, 0.0,
|
|
99
|
+
31.3970, 937_814.0,
|
|
100
|
+
)
|
|
101
|
+
} else {
|
|
102
|
+
return Err(PyErr::new::<pyo3::exceptions::PyValueError, _>("Only 'D+T=n+a', 'D+D=p+T', and 'D+D=n+He3' reactions are supported"));
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
let sigma_thermal_reactivity = bosch_and_hale_equations(c1, c2, c3, c4, c5, c6, c7, gamov, mrc2, ion_temperature_kev)?;
|
|
106
|
+
let scaling_factor = scale_reactivity_units(sigma_thermal_reactivity, reactivity_units);
|
|
107
|
+
Ok(sigma_thermal_reactivity*scaling_factor)
|
|
108
|
+
}else if equation_str == "Sadler-Van Belle"{
|
|
109
|
+
if reaction != "D+T=n+a" {
|
|
110
|
+
return Err(PyErr::new::<pyo3::exceptions::PyValueError, _>("Only 'D+T=n+a' reaction is supported for 'Sadler-Van Belle' equation"));
|
|
111
|
+
}
|
|
112
|
+
let sigma_thermal_reactivity = sadler_van_belle(ion_temperature_kev)?;
|
|
113
|
+
let scaling_factor = scale_reactivity_units(sigma_thermal_reactivity, reactivity_units);
|
|
114
|
+
Ok(sigma_thermal_reactivity*scaling_factor)
|
|
115
|
+
}else{
|
|
116
|
+
panic!("Only 'Bosch-Hale' and 'Sadler-Van Belle' equations are supported");
|
|
117
|
+
};
|
|
118
|
+
sigma_thermal_reactivity_scaled
|
|
119
|
+
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
#[pyfunction(signature = (ion_temperature, temperature_units=None, dt_fraction=None, dd_fraction=None, equation=None))]
|
|
124
|
+
#[pyo3(text_signature = "(ion_temperature, temperature_units='eV', dt_fraction=0.5, dd_fraction=0.5 equation='Bosch-Hale')")]
|
|
125
|
+
/// Calculate the relative reaction rates for given ion temperature and isotope fractions.
|
|
126
|
+
///
|
|
127
|
+
/// Parameters
|
|
128
|
+
/// ----------
|
|
129
|
+
/// ion_temperature : float
|
|
130
|
+
/// The ion temperature.
|
|
131
|
+
/// temperature_units : str, optional
|
|
132
|
+
/// The units of the ion temperature. Default is 'eV'.
|
|
133
|
+
/// dt_fraction : float, optional
|
|
134
|
+
/// The fraction of DT reactions. Default is 0.5.
|
|
135
|
+
/// dd_fraction : float, optional
|
|
136
|
+
/// The fraction of DD reactions. Default is 0.5.
|
|
137
|
+
/// equation : str, optional
|
|
138
|
+
/// The equation to use for reactivity calculations. Default is 'Bosch-Hale'.
|
|
139
|
+
///
|
|
140
|
+
/// Returns
|
|
141
|
+
/// -------
|
|
142
|
+
/// List[float]
|
|
143
|
+
/// A list containing the relative reaction rates for DT, DD (n+He3), and DD (p+T) reactions.
|
|
144
|
+
///
|
|
145
|
+
/// Examples
|
|
146
|
+
/// --------
|
|
147
|
+
/// >>> relative_reaction_rates(10.0)
|
|
148
|
+
/// [dt_reactivity, dd_reactivity_1, dd_reactivity_2]
|
|
149
|
+
///
|
|
150
|
+
/// >>> relative_reaction_rates(10.0, temperature_units='K', dt_fraction=0.3, dd_fraction=0.7, equation='Custom-Equation')
|
|
151
|
+
/// [dt_reactivity, dd_reactivity_1, dd_reactivity_2]
|
|
152
|
+
fn relative_reaction_rates(
|
|
153
|
+
ion_temperature: f64,
|
|
154
|
+
temperature_units: Option<&str>,
|
|
155
|
+
dt_fraction: Option<f64>,
|
|
156
|
+
dd_fraction: Option<f64>,
|
|
157
|
+
equation: Option<&str>,
|
|
158
|
+
) -> Result<Vec<f64>, PyErr> {
|
|
159
|
+
|
|
160
|
+
let ion_temperature_kev: f64 = scale_temperature_units_to_kev(ion_temperature, temperature_units);
|
|
161
|
+
|
|
162
|
+
let dt_fraction = dt_fraction.unwrap_or(0.5);
|
|
163
|
+
let dd_fraction = dd_fraction.unwrap_or(0.5);
|
|
164
|
+
|
|
165
|
+
let equation_str = equation.unwrap_or("Bosch-Hale");
|
|
166
|
+
|
|
167
|
+
let total_fraction = dt_fraction + dd_fraction;
|
|
168
|
+
let tol : f64 = 0.000001;
|
|
169
|
+
if !(total_fraction > 1. - tol && total_fraction < 1. + tol) {
|
|
170
|
+
return Err(PyErr::new::<pyo3::exceptions::PyValueError, _>("The dt_fraction + dd_fraction do not sum to 1.0 and are not within a small tolerance (+/-0.000001)"));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
let dt_reactivity = reactivity(
|
|
174
|
+
ion_temperature_kev,
|
|
175
|
+
Some("keV"),
|
|
176
|
+
Some("m^3/s"),
|
|
177
|
+
Some("D+T=n+a"),
|
|
178
|
+
Some(equation_str)
|
|
179
|
+
// Some("Bosch-Hale")
|
|
180
|
+
)?;
|
|
181
|
+
|
|
182
|
+
let dd_reactivity_1 = reactivity(
|
|
183
|
+
ion_temperature_kev,
|
|
184
|
+
Some("keV"),
|
|
185
|
+
Some("m^3/s"),
|
|
186
|
+
Some("D+D=n+He3"),
|
|
187
|
+
Some(equation_str)
|
|
188
|
+
// Some("Bosch-Hale")
|
|
189
|
+
)?;
|
|
190
|
+
|
|
191
|
+
let dd_reactivity_2 = reactivity(
|
|
192
|
+
ion_temperature_kev,
|
|
193
|
+
Some("keV"),
|
|
194
|
+
Some("m^3/s"),
|
|
195
|
+
Some("D+D=p+T"),
|
|
196
|
+
Some(equation_str)
|
|
197
|
+
// Some("Bosch-Hale")
|
|
198
|
+
)?;
|
|
199
|
+
|
|
200
|
+
let total_reactivity = dt_reactivity * dt_fraction + dd_reactivity_1 * dd_fraction + dd_reactivity_2 * dd_fraction;
|
|
201
|
+
let dt_normalized = dt_reactivity * dt_fraction / total_reactivity;
|
|
202
|
+
let dd1_normalized = dd_reactivity_1 * dd_fraction / total_reactivity;
|
|
203
|
+
let dd2_normalized = dd_reactivity_2 * dd_fraction / total_reactivity;
|
|
204
|
+
|
|
205
|
+
Ok(vec![dt_normalized, dd1_normalized, dd2_normalized])
|
|
206
|
+
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
fn sadler_van_belle(ion_temperature: f64) -> Result<f64, PyErr> {
|
|
211
|
+
let c = [
|
|
212
|
+
2.5663271e-18,
|
|
213
|
+
19.983026,
|
|
214
|
+
2.5077133e-2,
|
|
215
|
+
2.5773408e-3,
|
|
216
|
+
6.1880463e-5,
|
|
217
|
+
6.6024089e-2,
|
|
218
|
+
8.1215505e-3,
|
|
219
|
+
];
|
|
220
|
+
|
|
221
|
+
let u = 1.0 - ion_temperature * (c[2] + ion_temperature * (c[3] - c[4] * ion_temperature))
|
|
222
|
+
/ (1.0 + ion_temperature * (c[5] + c[6] * ion_temperature));
|
|
223
|
+
|
|
224
|
+
let val = c[0]
|
|
225
|
+
* ((-c[1] * (u / ion_temperature).powf(1.0 / 3.0)).exp())
|
|
226
|
+
/ (u.powf(5.0 / 6.0) * ion_temperature.powf(2.0 / 3.0));
|
|
227
|
+
|
|
228
|
+
Ok(val)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
fn bosch_and_hale_equations(c1: f64, c2: f64, c3: f64, c4: f64, c5: f64, c6: f64, c7: f64, gamov: f64, mrc2: f64, ion_temperature_kev: f64) -> Result<f64, PyErr> {
|
|
232
|
+
// Equation 13
|
|
233
|
+
let theta: f64 = ion_temperature_kev * (1.0 - (ion_temperature_kev * (c2 + ion_temperature_kev * (c4 + ion_temperature_kev * c6))) / (1.0 + ion_temperature_kev * (c3 + ion_temperature_kev * (c5 + ion_temperature_kev * c7)))) .powi(-1);
|
|
234
|
+
|
|
235
|
+
// Equation 14
|
|
236
|
+
let xi: f64 = (gamov.powi(2) / (4.0 * theta)).powf(1.0 / 3.0);
|
|
237
|
+
|
|
238
|
+
// Equation 12
|
|
239
|
+
let sigma_thermal_reactivity: f64 = c1 * theta * (xi / (mrc2 * ion_temperature_kev.powi(3))).sqrt() * (-3.0 * xi).exp();
|
|
240
|
+
|
|
241
|
+
Ok(sigma_thermal_reactivity * 1.0e-6)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
fn scale_reactivity_units(sigma_thermal_reactivity: f64, reactivity_units: Option<&str>) -> f64 {
|
|
245
|
+
match reactivity_units.unwrap_or("m^3/s") {
|
|
246
|
+
"m^3/s" => sigma_thermal_reactivity * 1.0e-6,
|
|
247
|
+
"cm^3/s" => sigma_thermal_reactivity,
|
|
248
|
+
"mm^3/s" => sigma_thermal_reactivity * 1.0e3,
|
|
249
|
+
_ => panic!("Invalid reaction rate units, accepted values are 'm^3/s', 'cm^3/s', 'mm^3/s'")
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
fn scale_temperature_units_to_kev(ion_temperature: f64, temperature_units: Option<&str>) -> f64 {
|
|
254
|
+
match temperature_units.unwrap_or("eV") {
|
|
255
|
+
"keV" => ion_temperature,
|
|
256
|
+
"eV" => ion_temperature * 1e-3,
|
|
257
|
+
"MeV" => ion_temperature * 1e3,
|
|
258
|
+
"GeV" => ion_temperature * 1e6,
|
|
259
|
+
_ => panic!("Invalid temperature units, accepted values are 'eV', 'keV', 'MeV' or 'GeV'")
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
fn scale_energy_in_kev_to_requested_units(energy_in_kev: f64, temperature_units: Option<&str>) -> f64 {
|
|
264
|
+
match temperature_units.unwrap_or("eV") {
|
|
265
|
+
"eV" => energy_in_kev * 1e3, // converting keV to eV
|
|
266
|
+
"keV" => energy_in_kev, // converting keV to MeV
|
|
267
|
+
"MeV" => energy_in_kev / 1e3, // converting keV to MeV
|
|
268
|
+
"GeV" => energy_in_kev / 1e6, // converting keV to GeV
|
|
269
|
+
_ => panic!("Unsupported temperature units, accepted values are 'eV', 'MeV', 'GeV'"),
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/// A Python module implemented in Rust.
|
|
274
|
+
#[pymodule]
|
|
275
|
+
fn fusion_neutron_utils(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
276
|
+
m.add_function(wrap_pyfunction!(reactivity, m)?)?;
|
|
277
|
+
m.add_function(wrap_pyfunction!(relative_reaction_rates, m)?)?;
|
|
278
|
+
m.add_function(wrap_pyfunction!(neutron_energy_mean_and_std_dev, m)?)?;
|
|
279
|
+
Ok(())
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
#[cfg(test)]
|
|
286
|
+
mod tests {
|
|
287
|
+
use super::*;
|
|
288
|
+
|
|
289
|
+
#[test]
|
|
290
|
+
fn test_scale_temperature_units_to_kev_eV() {
|
|
291
|
+
let ion_temperature = 1000.0; // 1000 eV
|
|
292
|
+
let temperature_units = Some("eV");
|
|
293
|
+
let result = scale_temperature_units_to_kev(ion_temperature, temperature_units);
|
|
294
|
+
assert_eq!(result, 1.0); // 1 keV
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
#[test]
|
|
298
|
+
fn test_scale_temperature_units_to_kev_keV() {
|
|
299
|
+
let ion_temperature = 1.0; // 1 keV
|
|
300
|
+
let temperature_units = Some("keV");
|
|
301
|
+
let result = scale_temperature_units_to_kev(ion_temperature, temperature_units);
|
|
302
|
+
assert_eq!(result, 1.0); // 1 keV
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
#[test]
|
|
306
|
+
fn test_scale_temperature_units_to_kev_MeV() {
|
|
307
|
+
let ion_temperature = 0.001; // 1 MeV
|
|
308
|
+
let temperature_units = Some("MeV");
|
|
309
|
+
let result = scale_temperature_units_to_kev(ion_temperature, temperature_units);
|
|
310
|
+
assert_eq!(result, 1.0); // 1 keV
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
#[test]
|
|
314
|
+
fn test_scale_temperature_units_to_kev_GeV() {
|
|
315
|
+
let ion_temperature = 0.000001; // 1 GeV
|
|
316
|
+
let temperature_units = Some("GeV");
|
|
317
|
+
let result = scale_temperature_units_to_kev(ion_temperature, temperature_units);
|
|
318
|
+
assert_eq!(result, 1.0); // 1 keV
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
#[test]
|
|
322
|
+
#[should_panic(expected = "Invalid temperature units")]
|
|
323
|
+
fn test_scale_temperature_units_to_kev_invalid_units() {
|
|
324
|
+
let ion_temperature = 1000.0;
|
|
325
|
+
let temperature_units = Some("K");
|
|
326
|
+
scale_temperature_units_to_kev(ion_temperature, temperature_units);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
#[cfg(test)]
|
|
332
|
+
mod tests2 {
|
|
333
|
+
use super::*;
|
|
334
|
+
|
|
335
|
+
#[test]
|
|
336
|
+
fn test_scale_to_ev() {
|
|
337
|
+
let energy_in_kev = 1.0;
|
|
338
|
+
let target_unit = "eV";
|
|
339
|
+
let result = scale_energy_in_kev_to_requested_units(energy_in_kev, Some(target_unit));
|
|
340
|
+
assert_eq!(result, 1000.0); // 1 keV = 1000 eV
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
#[test]
|
|
344
|
+
fn test_scale_to_mev() {
|
|
345
|
+
let energy_in_kev = 1000.0;
|
|
346
|
+
let target_unit = "MeV";
|
|
347
|
+
let result = scale_energy_in_kev_to_requested_units(energy_in_kev, Some(target_unit));
|
|
348
|
+
assert_eq!(result, 1.0); // 1000 keV = 1 MeV
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
#[test]
|
|
352
|
+
fn test_scale_to_gev() {
|
|
353
|
+
let energy_in_kev = 1_000_000.0;
|
|
354
|
+
let target_unit = "GeV";
|
|
355
|
+
let result = scale_energy_in_kev_to_requested_units(energy_in_kev, Some(target_unit));
|
|
356
|
+
assert_eq!(result, 1.0); // 1,000,000 keV = 1 GeV
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
#[test]
|
|
360
|
+
#[should_panic(expected = "Unsupported temperature units, accepted values are 'eV', 'MeV', 'GeV'")]
|
|
361
|
+
fn test_invalid_unit() {
|
|
362
|
+
let energy_in_kev = 1.0;
|
|
363
|
+
let target_unit = "invalid";
|
|
364
|
+
scale_energy_in_kev_to_requested_units(energy_in_kev, Some(target_unit));
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
#[cfg(test)]
|
|
369
|
+
mod tests3 {
|
|
370
|
+
use super::*;
|
|
371
|
+
|
|
372
|
+
#[test]
|
|
373
|
+
fn test_scale_to_m3_per_s() {
|
|
374
|
+
let sigma_thermal_reactivity = 1.0;
|
|
375
|
+
let reactivity_units = Some("m^3/s");
|
|
376
|
+
let result = scale_reactivity_units(sigma_thermal_reactivity, reactivity_units);
|
|
377
|
+
assert_eq!(result, 1.0 * 1.0e-6); // 1.0 * 1.0e-6 = 1.0e-6
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
#[test]
|
|
381
|
+
fn test_scale_to_cm3_per_s() {
|
|
382
|
+
let sigma_thermal_reactivity = 1.0;
|
|
383
|
+
let reactivity_units = Some("cm^3/s");
|
|
384
|
+
let result = scale_reactivity_units(sigma_thermal_reactivity, reactivity_units);
|
|
385
|
+
assert_eq!(result, 1.0); // 1.0 cm^3/s = 1.0 cm^3/s
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
#[test]
|
|
390
|
+
#[should_panic(expected = "Invalid reaction rate units, accepted values are 'm^3/s', 'cm^3/s', 'mm^3/s'")]
|
|
391
|
+
fn test_invalid_unit() {
|
|
392
|
+
let sigma_thermal_reactivity = 1.0;
|
|
393
|
+
let reactivity_units = Some("invalid");
|
|
394
|
+
scale_reactivity_units(sigma_thermal_reactivity, reactivity_units);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from fusion_neutron_utils import neutron_energy_mean_and_std_dev
|
|
2
|
+
from pytest import approx
|
|
3
|
+
|
|
4
|
+
def test_mean_energy():
|
|
5
|
+
mean, std_dev = neutron_energy_mean_and_std_dev(
|
|
6
|
+
reaction='D+D=n+He3',
|
|
7
|
+
ion_temperature=20e3,
|
|
8
|
+
temperature_units='eV',
|
|
9
|
+
neutron_energy_units='eV'
|
|
10
|
+
)
|
|
11
|
+
assert mean == approx(2.5e6, abs = 0.2e6)
|
|
12
|
+
|
|
13
|
+
mean, std_dev = neutron_energy_mean_and_std_dev(
|
|
14
|
+
reaction='D+D=n+He3',
|
|
15
|
+
ion_temperature=20,
|
|
16
|
+
temperature_units='keV',
|
|
17
|
+
neutron_energy_units='MeV'
|
|
18
|
+
)
|
|
19
|
+
assert mean == approx(2.5, abs = 0.2)
|
|
20
|
+
|
|
21
|
+
mean, std_dev = neutron_energy_mean_and_std_dev(
|
|
22
|
+
reaction='D+T=n+a',
|
|
23
|
+
ion_temperature=20e3,
|
|
24
|
+
temperature_units='eV',
|
|
25
|
+
)
|
|
26
|
+
assert mean == approx(14.06e6, abs = 0.04e6)
|
|
27
|
+
|
|
28
|
+
def test_mean_energy_increases_with_ion_temperature():
|
|
29
|
+
mean_cold, std_dev = neutron_energy_mean_and_std_dev(
|
|
30
|
+
reaction='D+D=n+He3',
|
|
31
|
+
ion_temperature=20e3,
|
|
32
|
+
temperature_units='eV',
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
mean_hot, std_dev = neutron_energy_mean_and_std_dev(
|
|
36
|
+
reaction='D+D=n+He3',
|
|
37
|
+
ion_temperature=40e3,
|
|
38
|
+
temperature_units='eV',
|
|
39
|
+
)
|
|
40
|
+
assert mean_cold < mean_hot
|
|
41
|
+
|
|
42
|
+
def test_mean_energy_units():
|
|
43
|
+
mean_kev, std_dev = neutron_energy_mean_and_std_dev(
|
|
44
|
+
reaction='D+T=n+a',
|
|
45
|
+
ion_temperature=30,
|
|
46
|
+
temperature_units='keV',
|
|
47
|
+
neutron_energy_units='keV'
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
mean_ev, std_dev = neutron_energy_mean_and_std_dev(
|
|
51
|
+
reaction='D+T=n+a',
|
|
52
|
+
ion_temperature=30e3,
|
|
53
|
+
temperature_units='eV',
|
|
54
|
+
neutron_energy_units='eV'
|
|
55
|
+
)
|
|
56
|
+
assert mean_kev == mean_ev/1e3
|
|
57
|
+
|
|
58
|
+
# def test_
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from fusion_neutron_utils import relative_reaction_rates
|
|
3
|
+
|
|
4
|
+
def test_relative_reaction_rates_default_fractions():
|
|
5
|
+
result = relative_reaction_rates(ion_temperature=10.0)
|
|
6
|
+
assert isinstance(result, list)
|
|
7
|
+
assert len(result) == 3
|
|
8
|
+
assert all(isinstance(x, float) for x in result)
|
|
9
|
+
|
|
10
|
+
def test_relative_reaction_rates_custom_fractions():
|
|
11
|
+
result = relative_reaction_rates(ion_temperature=10.0, dt_fraction=0.3, dd_fraction=0.7)
|
|
12
|
+
assert isinstance(result, list)
|
|
13
|
+
assert len(result) == 3
|
|
14
|
+
assert all(isinstance(x, float) for x in result)
|
|
15
|
+
|
|
16
|
+
def test_relative_reaction_rates_temperature_units():
|
|
17
|
+
result = relative_reaction_rates(ion_temperature=10.0, temperature_units="keV")
|
|
18
|
+
assert isinstance(result, list)
|
|
19
|
+
assert len(result) == 3
|
|
20
|
+
assert all(isinstance(x, float) for x in result)
|
|
21
|
+
|
|
22
|
+
def test_relative_reaction_rates_custom_equation():
|
|
23
|
+
result = relative_reaction_rates(ion_temperature=10.0, equation="Bosch-Hale")
|
|
24
|
+
assert isinstance(result, list)
|
|
25
|
+
assert len(result) == 3
|
|
26
|
+
assert all(isinstance(x, float) for x in result)
|
|
27
|
+
|
|
28
|
+
def test_relative_reaction_rates_invalid_fractions():
|
|
29
|
+
with pytest.raises(TypeError):
|
|
30
|
+
relative_reaction_rates(ion_temperature=10.0, dt_fraction="invalid", dd_fraction="invalid")
|
|
31
|
+
|
|
32
|
+
def test_relative_reaction_rates_zero_fractions():
|
|
33
|
+
with pytest.raises(ValueError):
|
|
34
|
+
relative_reaction_rates(ion_temperature=10.0, dt_fraction=0.0, dd_fraction=0.0)
|
|
35
|
+
|
|
36
|
+
def test_relative_reaction_rates_non_sum_1_fractions():
|
|
37
|
+
with pytest.raises(ValueError):
|
|
38
|
+
relative_reaction_rates(ion_temperature=10.0, dt_fraction=0.5, dd_fraction=0.4)
|
|
39
|
+
|
|
40
|
+
if __name__ == "__main__":
|
|
41
|
+
pytest.main()
|