openndm 0.2.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.
- openndm-0.2.0/.clang-format +24 -0
- openndm-0.2.0/.github/CODEOWNERS +25 -0
- openndm-0.2.0/.github/ISSUE_TEMPLATE/bug_report.yml +78 -0
- openndm-0.2.0/.github/ISSUE_TEMPLATE/config.yml +18 -0
- openndm-0.2.0/.github/ISSUE_TEMPLATE/feature_request.yml +49 -0
- openndm-0.2.0/.github/ISSUE_TEMPLATE/physics_discrepancy.yml +74 -0
- openndm-0.2.0/.github/dependabot.yml +32 -0
- openndm-0.2.0/.github/pull_request_template.md +43 -0
- openndm-0.2.0/.github/workflows/ci.yml +343 -0
- openndm-0.2.0/.github/workflows/release.yml +146 -0
- openndm-0.2.0/.gitignore +42 -0
- openndm-0.2.0/.pre-commit-config.yaml +49 -0
- openndm-0.2.0/.readthedocs.yaml +37 -0
- openndm-0.2.0/CHANGELOG.md +269 -0
- openndm-0.2.0/CITATION.cff +28 -0
- openndm-0.2.0/CMakeLists.txt +67 -0
- openndm-0.2.0/CODE_OF_CONDUCT.md +84 -0
- openndm-0.2.0/CONTRIBUTING.md +164 -0
- openndm-0.2.0/LICENSE +21 -0
- openndm-0.2.0/PKG-INFO +280 -0
- openndm-0.2.0/README.md +218 -0
- openndm-0.2.0/SECURITY.md +63 -0
- openndm-0.2.0/benchmarks/README.md +165 -0
- openndm-0.2.0/benchmarks/analytic/run.py +64 -0
- openndm-0.2.0/benchmarks/common.py +153 -0
- openndm-0.2.0/benchmarks/iaea2d/run.py +82 -0
- openndm-0.2.0/benchmarks/iaea3d/run.py +145 -0
- openndm-0.2.0/conda-recipe/README.md +40 -0
- openndm-0.2.0/conda-recipe/meta.yaml +88 -0
- openndm-0.2.0/docs/_static/.gitkeep +4 -0
- openndm-0.2.0/docs/api/exceptions.rst +7 -0
- openndm-0.2.0/docs/api/gc.rst +68 -0
- openndm-0.2.0/docs/api/geometry.rst +7 -0
- openndm-0.2.0/docs/api/index.rst +40 -0
- openndm-0.2.0/docs/api/model.rst +7 -0
- openndm-0.2.0/docs/api/plots.rst +7 -0
- openndm-0.2.0/docs/api/settings.rst +7 -0
- openndm-0.2.0/docs/api/statepoint.rst +7 -0
- openndm-0.2.0/docs/api/xslib.rst +7 -0
- openndm-0.2.0/docs/architecture.md +198 -0
- openndm-0.2.0/docs/changelog.rst +5 -0
- openndm-0.2.0/docs/conf.py +146 -0
- openndm-0.2.0/docs/contributing.rst +5 -0
- openndm-0.2.0/docs/examples/index.rst +41 -0
- openndm-0.2.0/docs/index.rst +75 -0
- openndm-0.2.0/docs/releasing.md +159 -0
- openndm-0.2.0/docs/requirements.md +438 -0
- openndm-0.2.0/docs/requirements.txt +19 -0
- openndm-0.2.0/docs/roadmap.md +130 -0
- openndm-0.2.0/docs/status.md +203 -0
- openndm-0.2.0/docs/theory.md +502 -0
- openndm-0.2.0/docs/user-guide.md +706 -0
- openndm-0.2.0/examples/01_getting_started.ipynb +379 -0
- openndm-0.2.0/examples/02_openmc_to_openndm.ipynb +633 -0
- openndm-0.2.0/examples/03_iaea_benchmark.ipynb +301 -0
- openndm-0.2.0/examples/04_branch_library_and_boron_search.ipynb +649 -0
- openndm-0.2.0/examples/README.md +66 -0
- openndm-0.2.0/include/openndm/cmfd.h +133 -0
- openndm-0.2.0/include/openndm/constants.h +46 -0
- openndm-0.2.0/include/openndm/error.h +72 -0
- openndm-0.2.0/include/openndm/geometry.h +134 -0
- openndm-0.2.0/include/openndm/kernel.h +89 -0
- openndm-0.2.0/include/openndm/matrix.h +118 -0
- openndm-0.2.0/include/openndm/settings.h +81 -0
- openndm-0.2.0/include/openndm/solver.h +85 -0
- openndm-0.2.0/include/openndm/xslib.h +142 -0
- openndm-0.2.0/pyproject.toml +82 -0
- openndm-0.2.0/python/openndm/__init__.py +71 -0
- openndm-0.2.0/python/openndm/exceptions.py +49 -0
- openndm-0.2.0/python/openndm/gc/__init__.py +55 -0
- openndm-0.2.0/python/openndm/gc/adf.py +286 -0
- openndm-0.2.0/python/openndm/gc/driver.py +354 -0
- openndm-0.2.0/python/openndm/gc/formfunc.py +78 -0
- openndm-0.2.0/python/openndm/gc/kinetics.py +191 -0
- openndm-0.2.0/python/openndm/gc/leakage.py +352 -0
- openndm-0.2.0/python/openndm/gc/mgxs.py +484 -0
- openndm-0.2.0/python/openndm/geometry.py +227 -0
- openndm-0.2.0/python/openndm/model.py +552 -0
- openndm-0.2.0/python/openndm/plots.py +85 -0
- openndm-0.2.0/python/openndm/settings.py +109 -0
- openndm-0.2.0/python/openndm/statepoint.py +195 -0
- openndm-0.2.0/python/openndm/xslib.py +481 -0
- openndm-0.2.0/scripts/check_notebook_paths.py +84 -0
- openndm-0.2.0/src/bindings.cpp +349 -0
- openndm-0.2.0/src/cmfd.cpp +560 -0
- openndm-0.2.0/src/geometry.cpp +205 -0
- openndm-0.2.0/src/kernel.cpp +24 -0
- openndm-0.2.0/src/kernel_common.h +147 -0
- openndm-0.2.0/src/kernel_fdm.cpp +34 -0
- openndm-0.2.0/src/kernel_nem.cpp +196 -0
- openndm-0.2.0/src/kernel_sanm.cpp +184 -0
- openndm-0.2.0/src/matrix.cpp +296 -0
- openndm-0.2.0/src/solver.cpp +324 -0
- openndm-0.2.0/src/xslib.cpp +302 -0
- openndm-0.2.0/tests/cpp/CMakeLists.txt +23 -0
- openndm-0.2.0/tests/cpp/test_geometry.cpp +110 -0
- openndm-0.2.0/tests/cpp/test_kernels.cpp +137 -0
- openndm-0.2.0/tests/cpp/test_matrix.cpp +115 -0
- openndm-0.2.0/tests/cpp/test_xslib.cpp +161 -0
- openndm-0.2.0/tests/python/conftest.py +149 -0
- openndm-0.2.0/tests/python/test_analytic.py +518 -0
- openndm-0.2.0/tests/python/test_benchmarks.py +189 -0
- openndm-0.2.0/tests/python/test_discontinuity_factors.py +397 -0
- openndm-0.2.0/tests/python/test_gc.py +671 -0
- openndm-0.2.0/tests/python/test_geometry.py +122 -0
- openndm-0.2.0/tests/python/test_openmc_integration.py +173 -0
- openndm-0.2.0/tests/python/test_solver.py +316 -0
- openndm-0.2.0/tests/python/test_statepoint.py +69 -0
- openndm-0.2.0/tests/python/test_verification.py +461 -0
- openndm-0.2.0/tests/python/test_xslib.py +259 -0
- openndm-0.2.0/tests/validation/README.md +146 -0
- openndm-0.2.0/tests/validation/c1_homogeneous.py +201 -0
- openndm-0.2.0/tests/validation/c2_assembly_adf.py +202 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Matches OpenMC's C++ style (DP-1).
|
|
2
|
+
---
|
|
3
|
+
Language: Cpp
|
|
4
|
+
BasedOnStyle: Google
|
|
5
|
+
AccessModifierOffset: -2
|
|
6
|
+
AlignAfterOpenBracket: DontAlign
|
|
7
|
+
AllowShortFunctionsOnASingleLine: Inline
|
|
8
|
+
AlwaysBreakTemplateDeclarations: Yes
|
|
9
|
+
BinPackArguments: true
|
|
10
|
+
BinPackParameters: true
|
|
11
|
+
BreakBeforeBraces: Custom
|
|
12
|
+
BraceWrapping:
|
|
13
|
+
AfterClass: false
|
|
14
|
+
AfterFunction: true
|
|
15
|
+
AfterNamespace: false
|
|
16
|
+
AfterStruct: false
|
|
17
|
+
BeforeElse: false
|
|
18
|
+
ColumnLimit: 80
|
|
19
|
+
PackConstructorInitializers: Never
|
|
20
|
+
DerivePointerAlignment: false
|
|
21
|
+
IndentWidth: 2
|
|
22
|
+
PointerAlignment: Left
|
|
23
|
+
SpaceAfterTemplateKeyword: false
|
|
24
|
+
Standard: c++17
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Every file needs review from the owner before it reaches main.
|
|
2
|
+
#
|
|
3
|
+
# The `main` ruleset requires code owner review, so this file is what makes
|
|
4
|
+
# "@rizkiokt must approve" a rule rather than a convention. A pull request
|
|
5
|
+
# touching any path below cannot merge without an approving review from the
|
|
6
|
+
# owner listed for that path.
|
|
7
|
+
|
|
8
|
+
* @rizkiokt
|
|
9
|
+
|
|
10
|
+
# The numerical core and the verification suite are the parts where a wrong
|
|
11
|
+
# merge is expensive and quiet: a physics regression passes every style check
|
|
12
|
+
# and most of the unit tests. They are listed separately so that GitHub names
|
|
13
|
+
# them explicitly in the review request rather than falling through to the
|
|
14
|
+
# catch-all above.
|
|
15
|
+
/src/ @rizkiokt
|
|
16
|
+
/include/ @rizkiokt
|
|
17
|
+
/tests/validation/ @rizkiokt
|
|
18
|
+
/tests/python/test_verification.py @rizkiokt
|
|
19
|
+
/benchmarks/ @rizkiokt
|
|
20
|
+
|
|
21
|
+
# Release and distribution machinery: a bad merge here publishes.
|
|
22
|
+
/.github/workflows/ @rizkiokt
|
|
23
|
+
/conda-recipe/ @rizkiokt
|
|
24
|
+
/pyproject.toml @rizkiokt
|
|
25
|
+
/CMakeLists.txt @rizkiokt
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Something crashes, raises, or behaves differently from what the docs say.
|
|
3
|
+
labels: ["bug"]
|
|
4
|
+
body:
|
|
5
|
+
- type: markdown
|
|
6
|
+
attributes:
|
|
7
|
+
value: >-
|
|
8
|
+
For a result that runs fine but looks numerically wrong, use the
|
|
9
|
+
**Physics discrepancy** template instead — it asks for the reference
|
|
10
|
+
the answer is being judged against, which is what makes that class of
|
|
11
|
+
report actionable.
|
|
12
|
+
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: what-happened
|
|
15
|
+
attributes:
|
|
16
|
+
label: What happened
|
|
17
|
+
description: What you expected, and what you got instead.
|
|
18
|
+
validations:
|
|
19
|
+
required: true
|
|
20
|
+
|
|
21
|
+
- type: textarea
|
|
22
|
+
id: reproducer
|
|
23
|
+
attributes:
|
|
24
|
+
label: Minimal reproducer
|
|
25
|
+
description: >-
|
|
26
|
+
A self-contained script that shows the problem. Inline the cross
|
|
27
|
+
sections rather than attaching a library where you can — a case that
|
|
28
|
+
runs on its own gets fixed much faster than one that needs a data file.
|
|
29
|
+
render: python
|
|
30
|
+
validations:
|
|
31
|
+
required: true
|
|
32
|
+
|
|
33
|
+
- type: textarea
|
|
34
|
+
id: traceback
|
|
35
|
+
attributes:
|
|
36
|
+
label: Traceback or error output
|
|
37
|
+
description: The complete output, not just the last line.
|
|
38
|
+
render: text
|
|
39
|
+
|
|
40
|
+
- type: textarea
|
|
41
|
+
id: version
|
|
42
|
+
attributes:
|
|
43
|
+
label: Version information
|
|
44
|
+
description: Output of the command below.
|
|
45
|
+
value: |
|
|
46
|
+
<!-- paste the output of:
|
|
47
|
+
|
|
48
|
+
python -c "import openndm, sys, platform; print(openndm.__version__, sys.version, platform.platform())"
|
|
49
|
+
|
|
50
|
+
-->
|
|
51
|
+
render: text
|
|
52
|
+
validations:
|
|
53
|
+
required: true
|
|
54
|
+
|
|
55
|
+
- type: dropdown
|
|
56
|
+
id: install-method
|
|
57
|
+
attributes:
|
|
58
|
+
label: How did you install OpenNDM?
|
|
59
|
+
options:
|
|
60
|
+
- conda / mamba (conda-forge)
|
|
61
|
+
- pip, from a wheel on PyPI
|
|
62
|
+
- pip, built from source
|
|
63
|
+
- built from source with CMake directly
|
|
64
|
+
- other
|
|
65
|
+
validations:
|
|
66
|
+
required: true
|
|
67
|
+
|
|
68
|
+
- type: checkboxes
|
|
69
|
+
id: checks
|
|
70
|
+
attributes:
|
|
71
|
+
label: Before submitting
|
|
72
|
+
options:
|
|
73
|
+
- label: >-
|
|
74
|
+
I checked [docs/status.md](https://github.com/rizkiokt/openndm/blob/main/docs/status.md)
|
|
75
|
+
and this is not a feature that is simply not implemented yet.
|
|
76
|
+
required: true
|
|
77
|
+
- label: I searched existing issues for this.
|
|
78
|
+
required: true
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
blank_issues_enabled: false
|
|
2
|
+
contact_links:
|
|
3
|
+
- name: Question about using OpenNDM
|
|
4
|
+
url: https://github.com/rizkiokt/openndm/discussions
|
|
5
|
+
about: >-
|
|
6
|
+
How do I set up this geometry, why is my case not converging, what does
|
|
7
|
+
this setting do. Questions get answered faster in Discussions and stay
|
|
8
|
+
searchable for the next person.
|
|
9
|
+
- name: Theory and method background
|
|
10
|
+
url: https://github.com/rizkiokt/openndm/blob/main/docs/theory.md
|
|
11
|
+
about: >-
|
|
12
|
+
Every equation the solver implements, with its discretisation written
|
|
13
|
+
out. Check here before filing a physics discrepancy.
|
|
14
|
+
- name: What is and is not implemented yet
|
|
15
|
+
url: https://github.com/rizkiokt/openndm/blob/main/docs/status.md
|
|
16
|
+
about: >-
|
|
17
|
+
Requirement-by-requirement implementation status. A missing feature is
|
|
18
|
+
often listed here already.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Propose a capability OpenNDM does not have.
|
|
3
|
+
labels: ["enhancement"]
|
|
4
|
+
body:
|
|
5
|
+
- type: markdown
|
|
6
|
+
attributes:
|
|
7
|
+
value: >-
|
|
8
|
+
[docs/status.md](https://github.com/rizkiokt/openndm/blob/main/docs/status.md)
|
|
9
|
+
maps every requirement in the specification to what is actually built.
|
|
10
|
+
Much of the roadmap is already written down there — if your request is
|
|
11
|
+
on it, say so and the issue becomes a place to discuss priority rather
|
|
12
|
+
than scope.
|
|
13
|
+
|
|
14
|
+
- type: textarea
|
|
15
|
+
id: problem
|
|
16
|
+
attributes:
|
|
17
|
+
label: The calculation you cannot do today
|
|
18
|
+
description: >-
|
|
19
|
+
Describe the analysis you are trying to run, not the API you imagine.
|
|
20
|
+
The physics problem usually admits a better design than the one that
|
|
21
|
+
comes to mind first.
|
|
22
|
+
validations:
|
|
23
|
+
required: true
|
|
24
|
+
|
|
25
|
+
- type: textarea
|
|
26
|
+
id: proposal
|
|
27
|
+
attributes:
|
|
28
|
+
label: What you have in mind
|
|
29
|
+
description: Sketch the API or the method if you have one.
|
|
30
|
+
|
|
31
|
+
- type: input
|
|
32
|
+
id: requirement-id
|
|
33
|
+
attributes:
|
|
34
|
+
label: Related requirement ID
|
|
35
|
+
description: >-
|
|
36
|
+
If this corresponds to an ID in docs/requirements.md, give it. Leave
|
|
37
|
+
blank if this is new scope.
|
|
38
|
+
placeholder: "e.g. FR-GEO-7, or blank"
|
|
39
|
+
|
|
40
|
+
- type: dropdown
|
|
41
|
+
id: contribution
|
|
42
|
+
attributes:
|
|
43
|
+
label: Would you be willing to implement this?
|
|
44
|
+
options:
|
|
45
|
+
- "Yes, with guidance on where it belongs"
|
|
46
|
+
- "Yes, I know the codebase well enough"
|
|
47
|
+
- "No, I am requesting it"
|
|
48
|
+
validations:
|
|
49
|
+
required: true
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
name: Physics discrepancy
|
|
2
|
+
description: The solver converges, but the answer disagrees with a reference you trust.
|
|
3
|
+
labels: ["physics", "needs-verification"]
|
|
4
|
+
body:
|
|
5
|
+
- type: markdown
|
|
6
|
+
attributes:
|
|
7
|
+
value: >-
|
|
8
|
+
A disagreement is only a bug once there is something to disagree with.
|
|
9
|
+
The fields below exist to establish that: what the reference is, how
|
|
10
|
+
tight it is, and whether the gap is larger than the discretisation
|
|
11
|
+
error you should expect at your mesh.
|
|
12
|
+
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: discrepancy
|
|
15
|
+
attributes:
|
|
16
|
+
label: The numbers
|
|
17
|
+
description: >-
|
|
18
|
+
What OpenNDM produced, what the reference says, and the difference.
|
|
19
|
+
Give eigenvalues in pcm and distributions as a max and RMS relative
|
|
20
|
+
error.
|
|
21
|
+
value: |
|
|
22
|
+
quantity OpenNDM reference difference
|
|
23
|
+
k_eff
|
|
24
|
+
max pin power
|
|
25
|
+
RMS power
|
|
26
|
+
validations:
|
|
27
|
+
required: true
|
|
28
|
+
|
|
29
|
+
- type: input
|
|
30
|
+
id: reference-source
|
|
31
|
+
attributes:
|
|
32
|
+
label: Where the reference comes from
|
|
33
|
+
description: >-
|
|
34
|
+
A published benchmark and its revision, a Monte Carlo run (give the
|
|
35
|
+
code, version and reported uncertainty), or another deterministic code
|
|
36
|
+
and its options.
|
|
37
|
+
placeholder: "e.g. IAEA-2D, ANL-7416 Rev. 2, or OpenMC 0.14.0, 200 batches, sigma = 12 pcm"
|
|
38
|
+
validations:
|
|
39
|
+
required: true
|
|
40
|
+
|
|
41
|
+
- type: textarea
|
|
42
|
+
id: mesh
|
|
43
|
+
attributes:
|
|
44
|
+
label: Mesh and kernel
|
|
45
|
+
description: >-
|
|
46
|
+
Nodes per assembly, axial layers, and which kernel. Discretisation
|
|
47
|
+
error is the first thing to rule out: the nodal kernels are second
|
|
48
|
+
order in node size, so a one-node-per-assembly result is expected to
|
|
49
|
+
carry tens of pcm on some problems.
|
|
50
|
+
placeholder: "2x2 per assembly, 24 axial layers, SANM"
|
|
51
|
+
validations:
|
|
52
|
+
required: true
|
|
53
|
+
|
|
54
|
+
- type: checkboxes
|
|
55
|
+
id: convergence
|
|
56
|
+
attributes:
|
|
57
|
+
label: Convergence
|
|
58
|
+
options:
|
|
59
|
+
- label: The solve converged; it did not stop at the iteration limit.
|
|
60
|
+
required: true
|
|
61
|
+
- label: >-
|
|
62
|
+
I tightened the tolerances and refined the mesh, and the
|
|
63
|
+
disagreement did not shrink.
|
|
64
|
+
|
|
65
|
+
- type: textarea
|
|
66
|
+
id: reproducer
|
|
67
|
+
attributes:
|
|
68
|
+
label: Input
|
|
69
|
+
description: >-
|
|
70
|
+
The deck, or a link to it. If the case needs a cross section library,
|
|
71
|
+
say how to obtain it.
|
|
72
|
+
render: python
|
|
73
|
+
validations:
|
|
74
|
+
required: true
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Dependency updates arrive as pull requests, which is the only way into `main`
|
|
2
|
+
# anyway under the branch ruleset. They still need review and green CI.
|
|
3
|
+
version: 2
|
|
4
|
+
updates:
|
|
5
|
+
# The pinned action versions in the workflows. An unpinned or stale action is
|
|
6
|
+
# the most common supply chain exposure a repository this size actually has.
|
|
7
|
+
- package-ecosystem: github-actions
|
|
8
|
+
directory: /
|
|
9
|
+
schedule:
|
|
10
|
+
interval: weekly
|
|
11
|
+
day: monday
|
|
12
|
+
open-pull-requests-limit: 5
|
|
13
|
+
commit-message:
|
|
14
|
+
prefix: "ci"
|
|
15
|
+
labels: ["dependencies", "ci"]
|
|
16
|
+
|
|
17
|
+
# Build requirements in pyproject.toml. Runtime dependencies are deliberately
|
|
18
|
+
# loose ranges, so this mostly surfaces scikit-build-core and pybind11.
|
|
19
|
+
- package-ecosystem: pip
|
|
20
|
+
directory: /
|
|
21
|
+
schedule:
|
|
22
|
+
interval: monthly
|
|
23
|
+
open-pull-requests-limit: 5
|
|
24
|
+
commit-message:
|
|
25
|
+
prefix: "build"
|
|
26
|
+
labels: ["dependencies"]
|
|
27
|
+
# The linters are pinned on purpose: an unpinned formatter turns every
|
|
28
|
+
# upstream release into a red build on an unrelated commit. Dependabot may
|
|
29
|
+
# propose the bump, but it goes in as a deliberate reformat commit.
|
|
30
|
+
groups:
|
|
31
|
+
linters:
|
|
32
|
+
patterns: ["ruff", "clang-format"]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
## What this changes
|
|
2
|
+
|
|
3
|
+
<!-- One or two sentences. The diff says what; say why. -->
|
|
4
|
+
|
|
5
|
+
## Type of change
|
|
6
|
+
|
|
7
|
+
- [ ] `fix` — corrects behaviour that was wrong
|
|
8
|
+
- [ ] `feat` — adds behaviour
|
|
9
|
+
- [ ] `docs` — documentation only
|
|
10
|
+
- [ ] `test` — tests only
|
|
11
|
+
- [ ] `refactor` — no behaviour change, no number changes
|
|
12
|
+
- [ ] `chore` / `ci` / `build` — tooling, packaging, workflows
|
|
13
|
+
|
|
14
|
+
## Does this move any number?
|
|
15
|
+
|
|
16
|
+
A change to `k_eff`, a flux, a power distribution, a convergence order or a
|
|
17
|
+
benchmark result is a **physics change**, not a refactor, even when it looks
|
|
18
|
+
like a cleanup. See `CONTRIBUTING.md`.
|
|
19
|
+
|
|
20
|
+
- [ ] No verification or benchmark result moves.
|
|
21
|
+
- [ ] Some result moves, and the body below says which, by how much, and why
|
|
22
|
+
the new number is the correct one.
|
|
23
|
+
|
|
24
|
+
<!-- If a number moved, paste the before/after here:
|
|
25
|
+
|
|
26
|
+
case before after delta
|
|
27
|
+
analytic bare cuboid 1.00123456 1.00123401 -0.55 pcm
|
|
28
|
+
IAEA-2D k_eff 1.02958 1.02958 0.00 pcm
|
|
29
|
+
-->
|
|
30
|
+
|
|
31
|
+
## Checklist
|
|
32
|
+
|
|
33
|
+
- [ ] `pytest tests/python` passes locally.
|
|
34
|
+
- [ ] `ruff check python tests benchmarks` is clean.
|
|
35
|
+
- [ ] `clang-format` is clean on any touched C++ (`pre-commit run -a` covers both).
|
|
36
|
+
- [ ] New behaviour has a test. A new kernel is registered in `ALL_KERNELS`
|
|
37
|
+
so the verification suite picks it up automatically.
|
|
38
|
+
- [ ] Public API changes are reflected in `docs/` and `CHANGELOG.md`.
|
|
39
|
+
- [ ] Commits follow conventional-commit subjects (`feat:`, `fix:`, ...).
|
|
40
|
+
|
|
41
|
+
## Related issues
|
|
42
|
+
|
|
43
|
+
<!-- "Closes #123", or delete this section. -->
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
inputs:
|
|
9
|
+
cross_sections_url:
|
|
10
|
+
description: >-
|
|
11
|
+
URL of a nuclear data archive containing cross_sections.xml. Leave
|
|
12
|
+
empty to skip the OpenMC coupling validation, which cannot run
|
|
13
|
+
without one.
|
|
14
|
+
required: false
|
|
15
|
+
default: ""
|
|
16
|
+
|
|
17
|
+
concurrency:
|
|
18
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
19
|
+
cancel-in-progress: true
|
|
20
|
+
|
|
21
|
+
env:
|
|
22
|
+
FORCE_COLOR: 1
|
|
23
|
+
|
|
24
|
+
jobs:
|
|
25
|
+
lint:
|
|
26
|
+
name: lint
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v7
|
|
30
|
+
- uses: actions/setup-python@v7
|
|
31
|
+
with:
|
|
32
|
+
python-version: "3.12"
|
|
33
|
+
# Both linters are pinned. An unpinned formatter turns every upstream
|
|
34
|
+
# release into a red build on an unrelated commit, and clang-format in
|
|
35
|
+
# particular reformats differently between major versions: the first CI
|
|
36
|
+
# run failed here purely because the runner had 18 and the tree had been
|
|
37
|
+
# formatted with 23.
|
|
38
|
+
- name: Install linters
|
|
39
|
+
run: pip install "ruff==0.14.14" "clang-format==18.1.8"
|
|
40
|
+
- name: ruff (NFR-QA-4)
|
|
41
|
+
run: ruff check python tests benchmarks
|
|
42
|
+
- name: clang-format (NFR-QA-4)
|
|
43
|
+
run: |
|
|
44
|
+
for f in $(find include src tests/cpp -name '*.h' -o -name '*.cpp'); do
|
|
45
|
+
clang-format --dry-run --Werror "$f" || exit 1
|
|
46
|
+
done
|
|
47
|
+
|
|
48
|
+
test:
|
|
49
|
+
name: ${{ matrix.os }} / ${{ matrix.compiler }} / py${{ matrix.python }}
|
|
50
|
+
runs-on: ${{ matrix.os }}
|
|
51
|
+
strategy:
|
|
52
|
+
fail-fast: false
|
|
53
|
+
matrix:
|
|
54
|
+
include:
|
|
55
|
+
- { os: ubuntu-latest, compiler: gcc, python: "3.10" }
|
|
56
|
+
- { os: ubuntu-latest, compiler: gcc, python: "3.12" }
|
|
57
|
+
- { os: ubuntu-latest, compiler: gcc, python: "3.13" }
|
|
58
|
+
- { os: ubuntu-latest, compiler: clang, python: "3.12" }
|
|
59
|
+
- { os: macos-latest, compiler: clang, python: "3.12" }
|
|
60
|
+
steps:
|
|
61
|
+
- uses: actions/checkout@v7
|
|
62
|
+
|
|
63
|
+
- uses: actions/setup-python@v7
|
|
64
|
+
with:
|
|
65
|
+
python-version: ${{ matrix.python }}
|
|
66
|
+
|
|
67
|
+
- name: Select the compiler
|
|
68
|
+
if: runner.os == 'Linux'
|
|
69
|
+
run: |
|
|
70
|
+
if [ "${{ matrix.compiler }}" = "clang" ]; then
|
|
71
|
+
echo "CC=clang" >> "$GITHUB_ENV"
|
|
72
|
+
echo "CXX=clang++" >> "$GITHUB_ENV"
|
|
73
|
+
else
|
|
74
|
+
echo "CC=gcc" >> "$GITHUB_ENV"
|
|
75
|
+
echo "CXX=g++" >> "$GITHUB_ENV"
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
- name: Install OpenMP for AppleClang
|
|
79
|
+
if: runner.os == 'macOS'
|
|
80
|
+
run: |
|
|
81
|
+
brew install libomp
|
|
82
|
+
echo "OpenMP_ROOT=$(brew --prefix libomp)" >> "$GITHUB_ENV"
|
|
83
|
+
|
|
84
|
+
- name: Build and install
|
|
85
|
+
run: pip install -v '.[dev]'
|
|
86
|
+
|
|
87
|
+
- name: Python tests
|
|
88
|
+
run: pytest tests/python -v --durations=10
|
|
89
|
+
|
|
90
|
+
- name: Verify the analytic benchmark
|
|
91
|
+
working-directory: benchmarks/analytic
|
|
92
|
+
run: python run.py
|
|
93
|
+
|
|
94
|
+
cpp-tests:
|
|
95
|
+
name: C++ unit tests and coverage
|
|
96
|
+
runs-on: ubuntu-latest
|
|
97
|
+
steps:
|
|
98
|
+
- uses: actions/checkout@v7
|
|
99
|
+
- uses: actions/setup-python@v7
|
|
100
|
+
with:
|
|
101
|
+
python-version: "3.12"
|
|
102
|
+
|
|
103
|
+
- name: Install build tools
|
|
104
|
+
run: |
|
|
105
|
+
sudo apt-get update -qq
|
|
106
|
+
sudo apt-get install -y cmake ninja-build lcov
|
|
107
|
+
|
|
108
|
+
- name: Configure with coverage instrumentation
|
|
109
|
+
run: |
|
|
110
|
+
cmake -S . -B build -G Ninja \
|
|
111
|
+
-DOPENNDM_BUILD_TESTS=ON \
|
|
112
|
+
-DOPENNDM_BUILD_PYTHON=OFF \
|
|
113
|
+
-DCMAKE_BUILD_TYPE=Debug \
|
|
114
|
+
-DCMAKE_CXX_FLAGS="--coverage -O0"
|
|
115
|
+
|
|
116
|
+
- name: Build
|
|
117
|
+
run: cmake --build build -j
|
|
118
|
+
|
|
119
|
+
- name: Run (NFR-QA-1)
|
|
120
|
+
run: ctest --test-dir build --output-on-failure
|
|
121
|
+
|
|
122
|
+
- name: Collect coverage
|
|
123
|
+
run: |
|
|
124
|
+
# geninfo walks the fetched Catch2 sources, which are not all on
|
|
125
|
+
# disk, so "source" has to be ignored during capture; the _deps
|
|
126
|
+
# tree is removed from the report immediately afterwards anyway.
|
|
127
|
+
lcov --capture --directory build --output-file coverage.info \
|
|
128
|
+
--ignore-errors mismatch,gcov,source,empty
|
|
129
|
+
lcov --remove coverage.info '/usr/*' '*/_deps/*' '*/tests/*' \
|
|
130
|
+
--ignore-errors unused --output-file coverage.info
|
|
131
|
+
lcov --list coverage.info
|
|
132
|
+
|
|
133
|
+
- uses: actions/upload-artifact@v7
|
|
134
|
+
with:
|
|
135
|
+
name: cpp-coverage
|
|
136
|
+
path: coverage.info
|
|
137
|
+
|
|
138
|
+
no-openmc:
|
|
139
|
+
name: import without OpenMC (FR-OMC-14)
|
|
140
|
+
runs-on: ubuntu-latest
|
|
141
|
+
steps:
|
|
142
|
+
- uses: actions/checkout@v7
|
|
143
|
+
- uses: actions/setup-python@v7
|
|
144
|
+
with:
|
|
145
|
+
python-version: "3.12"
|
|
146
|
+
- run: pip install .
|
|
147
|
+
- name: The solver must work with OpenMC absent
|
|
148
|
+
run: |
|
|
149
|
+
python - <<'PY'
|
|
150
|
+
import importlib.util
|
|
151
|
+
import sys
|
|
152
|
+
|
|
153
|
+
assert importlib.util.find_spec("openmc") is None, \
|
|
154
|
+
"this job is meaningless with OpenMC installed"
|
|
155
|
+
|
|
156
|
+
import numpy as np
|
|
157
|
+
import openndm
|
|
158
|
+
|
|
159
|
+
lib = openndm.XSLibrary(1, 1)
|
|
160
|
+
lib.set_composition(0, D=[1.0], absorption=[0.08], nu_fission=[0.1],
|
|
161
|
+
kappa_fission=[0.1], chi=[1.0], scatter=[[0.0]])
|
|
162
|
+
lib.finalize()
|
|
163
|
+
# 16 nodes per side, where the SANM discretisation error on this
|
|
164
|
+
# problem is about 3.5 pcm. Eight nodes leaves 28 pcm, which is
|
|
165
|
+
# correct physics but too coarse to assert anything tight against.
|
|
166
|
+
geom = openndm.Geometry.from_lattice(
|
|
167
|
+
np.zeros((16, 16, 16), int), pitch=6.25,
|
|
168
|
+
boundaries=dict.fromkeys(
|
|
169
|
+
["x_min", "x_max", "y_min", "y_max", "z_min", "z_max"],
|
|
170
|
+
"zero_flux"))
|
|
171
|
+
k = openndm.Model(geom, lib, openndm.Settings(verbosity=0)).solve().k_eff
|
|
172
|
+
reference = 0.1 / (0.08 + 3 * (np.pi / 100) ** 2)
|
|
173
|
+
error = 1e5 * (k - reference)
|
|
174
|
+
print(f"k_eff = {k:.8f}, {error:+.2f} pcm from analytic")
|
|
175
|
+
assert abs(error) < 10.0, error
|
|
176
|
+
|
|
177
|
+
# Importing openndm must not have dragged OpenMC in.
|
|
178
|
+
assert "openmc" not in sys.modules
|
|
179
|
+
PY
|
|
180
|
+
|
|
181
|
+
openmc-coupling:
|
|
182
|
+
name: OpenMC coupling validation (C-1, C-2)
|
|
183
|
+
runs-on: ubuntu-latest
|
|
184
|
+
# Manual trigger only, and it needs a nuclear data library URL supplied at
|
|
185
|
+
# dispatch time. There is no stable public URL to hard-code: the OpenMC
|
|
186
|
+
# official data libraries are served from rotating Box links and the page
|
|
187
|
+
# listing them is itself a 404 as of this writing. A job that silently
|
|
188
|
+
# skips when it cannot fetch data is worse than no job, because the
|
|
189
|
+
# silence is indistinguishable from success, so this one simply does not
|
|
190
|
+
# run unless it has been given somewhere to fetch from.
|
|
191
|
+
#
|
|
192
|
+
# C-1 and C-2 are otherwise run locally; see tests/validation/README.md.
|
|
193
|
+
if: >-
|
|
194
|
+
github.event_name == 'workflow_dispatch' &&
|
|
195
|
+
inputs.cross_sections_url != ''
|
|
196
|
+
defaults:
|
|
197
|
+
run:
|
|
198
|
+
shell: bash -el {0}
|
|
199
|
+
steps:
|
|
200
|
+
- uses: actions/checkout@v7
|
|
201
|
+
|
|
202
|
+
- uses: conda-incubator/setup-miniconda@v4
|
|
203
|
+
with:
|
|
204
|
+
miniforge-version: latest
|
|
205
|
+
activate-environment: openmc-env
|
|
206
|
+
channels: conda-forge
|
|
207
|
+
|
|
208
|
+
- name: Install OpenMC
|
|
209
|
+
run: conda install -y openmc pytest
|
|
210
|
+
|
|
211
|
+
- name: Install OpenNDM
|
|
212
|
+
run: pip install .
|
|
213
|
+
|
|
214
|
+
- name: Cache the nuclear data library
|
|
215
|
+
id: data
|
|
216
|
+
uses: actions/cache@v4
|
|
217
|
+
with:
|
|
218
|
+
path: nuclear-data
|
|
219
|
+
key: xs-${{ hashFiles('.github/workflows/ci.yml') }}-${{ inputs.cross_sections_url }}
|
|
220
|
+
|
|
221
|
+
- name: Fetch the nuclear data library
|
|
222
|
+
if: steps.data.outputs.cache-hit != 'true'
|
|
223
|
+
run: |
|
|
224
|
+
mkdir -p nuclear-data
|
|
225
|
+
curl -fsSL "${{ inputs.cross_sections_url }}" -o data.tar.xz
|
|
226
|
+
tar -xf data.tar.xz -C nuclear-data --strip-components=1
|
|
227
|
+
|
|
228
|
+
- name: Point OpenMC at it
|
|
229
|
+
run: |
|
|
230
|
+
xs=$(find nuclear-data -name cross_sections.xml | head -1)
|
|
231
|
+
test -n "$xs" || { echo "no cross_sections.xml in the archive"; exit 1; }
|
|
232
|
+
echo "OPENMC_CROSS_SECTIONS=$PWD/$xs" >> "$GITHUB_ENV"
|
|
233
|
+
|
|
234
|
+
- name: C-1 and C-2 as unit tests (FR-OMC-1, FR-OMC-7)
|
|
235
|
+
run: pytest tests/python/test_openmc_integration.py -v
|
|
236
|
+
|
|
237
|
+
- name: The full C-1 and C-2 cases
|
|
238
|
+
working-directory: tests/validation
|
|
239
|
+
run: |
|
|
240
|
+
python c1_homogeneous.py --particles 50000 --batches 120
|
|
241
|
+
python c2_assembly_adf.py --particles 20000 --batches 80
|
|
242
|
+
|
|
243
|
+
docs:
|
|
244
|
+
name: documentation
|
|
245
|
+
runs-on: ubuntu-latest
|
|
246
|
+
steps:
|
|
247
|
+
- uses: actions/checkout@v7
|
|
248
|
+
- uses: actions/setup-python@v7
|
|
249
|
+
with:
|
|
250
|
+
python-version: "3.12"
|
|
251
|
+
|
|
252
|
+
- name: Install pandoc
|
|
253
|
+
run: |
|
|
254
|
+
# nbsphinx shells out to pandoc to convert the notebooks' markdown
|
|
255
|
+
# cells. It is a system binary, so it is absent here even though a
|
|
256
|
+
# virtualenv on a developer machine usually finds one on PATH.
|
|
257
|
+
sudo apt-get update -qq
|
|
258
|
+
sudo apt-get install -y pandoc
|
|
259
|
+
|
|
260
|
+
- name: Install the docs toolchain
|
|
261
|
+
run: pip install -r docs/requirements.txt
|
|
262
|
+
|
|
263
|
+
# The API reference is autodoc against the installed package, so the
|
|
264
|
+
# extension has to be built. Without this the api/ pages render empty
|
|
265
|
+
# and the build still succeeds, which is the failure mode worth
|
|
266
|
+
# preventing.
|
|
267
|
+
- name: Install OpenNDM
|
|
268
|
+
run: pip install .
|
|
269
|
+
|
|
270
|
+
- name: Build (warnings are errors)
|
|
271
|
+
run: sphinx-build -b html -W --keep-going docs docs/_build/html
|
|
272
|
+
|
|
273
|
+
- name: Check for dead internal links
|
|
274
|
+
run: sphinx-build -b linkcheck docs docs/_build/linkcheck || true
|
|
275
|
+
|
|
276
|
+
- uses: actions/upload-artifact@v7
|
|
277
|
+
with:
|
|
278
|
+
name: docs-html
|
|
279
|
+
path: docs/_build/html
|
|
280
|
+
|
|
281
|
+
# ---------------------------------------------------------------------------
|
|
282
|
+
# The single check the `main` ruleset requires. Depending on this one job
|
|
283
|
+
# rather than on the matrix legs by name means the required-checks list does
|
|
284
|
+
# not have to be edited every time a Python version or a compiler is added
|
|
285
|
+
# or dropped, which is the usual way a protected branch quietly stops being
|
|
286
|
+
# protected.
|
|
287
|
+
ci:
|
|
288
|
+
name: ci
|
|
289
|
+
if: always()
|
|
290
|
+
needs: [lint, test, cpp-tests, no-openmc, docs]
|
|
291
|
+
runs-on: ubuntu-latest
|
|
292
|
+
steps:
|
|
293
|
+
- name: Fail if any required job did not succeed
|
|
294
|
+
run: |
|
|
295
|
+
results='${{ toJSON(needs) }}'
|
|
296
|
+
echo "$results"
|
|
297
|
+
echo "$results" | python3 -c "
|
|
298
|
+
import json, sys
|
|
299
|
+
needs = json.load(sys.stdin)
|
|
300
|
+
bad = {k: v['result'] for k, v in needs.items() if v['result'] != 'success'}
|
|
301
|
+
if bad:
|
|
302
|
+
for name, result in bad.items():
|
|
303
|
+
print(f'::error::{name}: {result}')
|
|
304
|
+
sys.exit(1)
|
|
305
|
+
print('all required jobs succeeded')
|
|
306
|
+
"
|
|
307
|
+
|
|
308
|
+
wheels:
|
|
309
|
+
name: build wheels
|
|
310
|
+
runs-on: ${{ matrix.os }}
|
|
311
|
+
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
|
312
|
+
strategy:
|
|
313
|
+
fail-fast: false
|
|
314
|
+
matrix:
|
|
315
|
+
# macos-13 is the Intel runner, which GitHub is retiring; a job
|
|
316
|
+
# targeting it sat unassigned for over an hour. Both macOS
|
|
317
|
+
# architectures are produced from the arm64 runner instead, with
|
|
318
|
+
# cibuildwheel cross-compiling the x86_64 wheels. It skips the test
|
|
319
|
+
# step for a cross-compiled wheel, which it cannot execute.
|
|
320
|
+
os: [ubuntu-latest, macos-14]
|
|
321
|
+
steps:
|
|
322
|
+
- uses: actions/checkout@v7
|
|
323
|
+
- name: Build wheels (NFR-EXT-3)
|
|
324
|
+
uses: pypa/cibuildwheel@v4.2
|
|
325
|
+
env:
|
|
326
|
+
CIBW_BUILD: cp310-* cp311-* cp312-* cp313-*
|
|
327
|
+
# h5py, a runtime dependency, publishes no musllinux wheels and no
|
|
328
|
+
# manylinux2014 wheels either, only manylinux_2_28. Without both of
|
|
329
|
+
# these the test step builds it from source in a container with no
|
|
330
|
+
# HDF5 and the job dies after the OpenNDM wheel has built fine.
|
|
331
|
+
CIBW_SKIP: "*_i686 *-musllinux*"
|
|
332
|
+
CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28
|
|
333
|
+
CIBW_MANYLINUX_AARCH64_IMAGE: manylinux_2_28
|
|
334
|
+
# NFR-EXT-3 wants both macOS architectures.
|
|
335
|
+
CIBW_ARCHS_MACOS: "x86_64 arm64"
|
|
336
|
+
CIBW_TEST_REQUIRES: pytest
|
|
337
|
+
CIBW_TEST_COMMAND: >
|
|
338
|
+
pytest {project}/tests/python -q
|
|
339
|
+
-k "not benchmark"
|
|
340
|
+
- uses: actions/upload-artifact@v7
|
|
341
|
+
with:
|
|
342
|
+
name: wheels-${{ matrix.os }}
|
|
343
|
+
path: wheelhouse/*.whl
|